From d625caf03c7d5a7cc673af432a8b6d32787c3bf1 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 8 Dec 2014 18:46:10 -0500 Subject: [PATCH 001/117] - Ported SDL backend to SDL 2.0. Still needs a little bit of polish, but it works. --- FindSDL2.cmake | 180 +++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 9 +- src/sdl/i_gui.cpp | 54 +++--------- src/sdl/i_input.cpp | 196 ++++++++++++++--------------------------- src/sdl/i_joystick.cpp | 2 +- src/sdl/i_main.cpp | 17 +--- src/sdl/sdlvideo.cpp | 99 ++++++++++++++------- 7 files changed, 331 insertions(+), 226 deletions(-) create mode 100644 FindSDL2.cmake diff --git a/FindSDL2.cmake b/FindSDL2.cmake new file mode 100644 index 000000000..614426ccc --- /dev/null +++ b/FindSDL2.cmake @@ -0,0 +1,180 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07c13c20c..8838dea05 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -210,14 +210,11 @@ else( WIN32 ) endif( NO_GTK ) # Non-Windows version also needs SDL - find_package( SDL ) - if( NOT SDL_FOUND ) - message( SEND_ERROR "SDL is required for building." ) - endif( NOT SDL_FOUND ) + find_package( SDL2 REQUIRED ) if( NOT APPLE OR NOT OSX_COCOA_BACKEND ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL2_LIBRARY}" ) endif( NOT APPLE OR NOT OSX_COCOA_BACKEND ) - include_directories( "${SDL_INCLUDE_DIR}" ) + include_directories( "${SDL2_INCLUDE_DIR}" ) find_path( FPU_CONTROL_DIR fpu_control.h ) if( FPU_CONTROL_DIR ) diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index b40fb7517..39b66c085 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -9,9 +9,6 @@ #include "v_palette.h" #include "textures.h" -extern SDL_Surface *cursorSurface; -extern SDL_Rect cursorBlit; - #ifdef USE_XCURSOR // Xlib has its own GC, so don't let it interfere. #define GC XGC @@ -22,40 +19,6 @@ bool UseXCursor; SDL_Cursor *X11Cursor; SDL_Cursor *FirstCursor; -// Hack! Hack! SDL does not provide a clean way to get the XDisplay. -// On the other hand, there are no more planned updates for SDL 1.2, -// so we should be fine making assumptions. -struct SDL_PrivateVideoData -{ - int local_X11; - Display *X11_Display; -}; - -struct SDL_VideoDevice -{ - const char *name; - int (*functions[9])(); - SDL_VideoInfo info; - SDL_PixelFormat *displayformatalphapixel; - int (*morefuncs[9])(); - Uint16 *gamma; - int (*somefuncs[9])(); - unsigned int texture; // Only here if SDL was compiled with OpenGL support. Ack! - int is_32bit; - int (*itsafuncs[13])(); - SDL_Surface *surfaces[3]; - SDL_Palette *physpal; - SDL_Color *gammacols; - char *wm_strings[2]; - int offsets[2]; - SDL_GrabMode input_grab; - int handles_any_size; - SDL_PrivateVideoData *hidden; // Why did they have to bury this so far in? -}; - -extern SDL_VideoDevice *current_video; -#define SDL_Display (current_video->hidden->X11_Display) - SDL_Cursor *CreateColorCursor(FTexture *cursorpic) { return NULL; @@ -64,6 +27,9 @@ SDL_Cursor *CreateColorCursor(FTexture *cursorpic) bool I_SetCursor(FTexture *cursorpic) { + static SDL_Cursor *cursor; + static SDL_Surface *cursorSurface; + if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) { // Must be no larger than 32x32. @@ -90,7 +56,6 @@ bool I_SetCursor(FTexture *cursorpic) if (cursorSurface == NULL) cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); - SDL_ShowCursor(0); SDL_LockSurface(cursorSurface); BYTE buffer[32*32*4]; memset(buffer, 0, 32*32*4); @@ -98,11 +63,20 @@ bool I_SetCursor(FTexture *cursorpic) cursorpic->CopyTrueColorPixels(&bmp, 0, 0); memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); SDL_UnlockSurface(cursorSurface); + + if (cursor) + SDL_FreeCursor (cursor); + cursor = SDL_CreateColorCursor (cursorSurface, 0, 0); + SDL_SetCursor (cursor); } else { - SDL_ShowCursor(1); - + if (cursor) + { + SDL_SetCursor (NULL); + SDL_FreeCursor (cursor); + cursor = NULL; + } if (cursorSurface != NULL) { SDL_FreeSurface(cursorSurface); diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index bf676db70..722345cb2 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -29,19 +29,16 @@ extern int paused; CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, sdl_nokeyrepeat, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) EXTERN_CVAR (Bool, fullscreen) extern int WaitingForKey, chatmodeon; extern constate_e ConsoleState; -extern SDL_Surface *cursorSurface; -extern SDL_Rect cursorBlit; +static TMap KeySymToDIK; +static bool DownState[SDL_NUM_SCANCODES]; -static BYTE KeySymToDIK[SDLK_LAST], DownState[SDLK_LAST]; - -static WORD DIKToKeySym[256] = +static SDL_Keycode DIKToKeySym[256] = { 0, SDLK_ESCAPE, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', SDLK_BACKSPACE, SDLK_TAB, @@ -51,9 +48,9 @@ static WORD DIKToKeySym[256] = '\'', '`', SDLK_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', SDLK_RSHIFT, SDLK_KP_MULTIPLY, SDLK_LALT, ' ', SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, - SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCK, SDLK_SCROLLOCK, SDLK_KP7, - SDLK_KP8, SDLK_KP9, SDLK_KP_MINUS, SDLK_KP4, SDLK_KP5, SDLK_KP6, SDLK_KP_PLUS, SDLK_KP1, - SDLK_KP2, SDLK_KP3, SDLK_KP0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11, + SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7, + SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1, + SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11, SDLK_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -70,7 +67,7 @@ static WORD DIKToKeySym[256] = 0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME, SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END, SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0, - 0, 0, 0, SDLK_LSUPER, SDLK_RSUPER, SDLK_MENU, SDLK_POWER, 0, + 0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -92,16 +89,12 @@ static void InitKeySymMap () KeySymToDIK[SDLK_RCTRL] = DIK_LCONTROL; KeySymToDIK[SDLK_RALT] = DIK_LMENU; // Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ - KeySymToDIK[SDLK_PRINT] = DIK_SYSRQ; + KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ; } static void I_CheckGUICapture () { bool wantCapt; - bool repeat; - int oldrepeat, interval; - - SDL_GetKeyRepeat(&oldrepeat, &interval); if (menuactive == MENU_Off) { @@ -117,56 +110,20 @@ static void I_CheckGUICapture () GUICapture = wantCapt; if (wantCapt) { - int x, y; - SDL_GetMouseState (&x, &y); - cursorBlit.x = x; - cursorBlit.y = y; - FlushDIKState (); memset (DownState, 0, sizeof(DownState)); - repeat = !sdl_nokeyrepeat; - SDL_EnableUNICODE (1); - } - else - { - repeat = false; - SDL_EnableUNICODE (0); - } - } - if (wantCapt) - { - repeat = !sdl_nokeyrepeat; - } - else - { - repeat = false; - } - if (repeat != (oldrepeat != 0)) - { - if (repeat) - { - SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); - } - else - { - SDL_EnableKeyRepeat (0, 0); } } } void I_SetMouseCapture() { + SDL_SetRelativeMouseMode (SDL_TRUE); } void I_ReleaseMouseCapture() { -} - -static void CenterMouse () -{ - SDL_WarpMouse (screen->GetWidth()/2, screen->GetHeight()/2); - SDL_PumpEvents (); - SDL_GetRelativeMouseState (NULL, NULL); + SDL_SetRelativeMouseMode (SDL_FALSE); } static void PostMouseMove (int x, int y) @@ -210,33 +167,10 @@ static void MouseRead () } if (x | y) { - CenterMouse (); PostMouseMove (x, -y); } } -static void WheelMoved(event_t *event) -{ - if (GUICapture) - { - if (event->type != EV_KeyUp) - { - SDLMod mod = SDL_GetModState(); - event->type = EV_GUI_Event; - event->subtype = event->data1 == KEY_MWHEELUP ? EV_GUI_WheelUp : EV_GUI_WheelDown; - event->data1 = 0; - event->data3 = ((mod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((mod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((mod & KMOD_ALT) ? GKM_ALT : 0); - D_PostEvent(event); - } - } - else - { - D_PostEvent(event); - } -} - CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) { if (self < 0) self = 0; @@ -259,26 +193,20 @@ static bool inGame() static void I_CheckNativeMouse () { - bool focus = (SDL_GetAppState() & (SDL_APPINPUTFOCUS|SDL_APPACTIVE)) - == (SDL_APPINPUTFOCUS|SDL_APPACTIVE); - bool fs = (SDL_GetVideoSurface ()->flags & SDL_FULLSCREEN) != 0; + bool focus = SDL_GetMouseFocus() != NULL; + bool fs = screen->IsFullscreen(); bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame()); if (wantNative != NativeMouse) { NativeMouse = wantNative; - SDL_ShowCursor (wantNative ? cursorSurface == NULL : 0); + SDL_ShowCursor (wantNative); + SDL_SetRelativeMouseMode (wantNative ? SDL_FALSE : SDL_TRUE); if (wantNative) { - SDL_WM_GrabInput (SDL_GRAB_OFF); FlushDIKState (KEY_MOUSE1, KEY_MOUSE8); } - else - { - SDL_WM_GrabInput (SDL_GRAB_ON); - CenterMouse (); - } } } @@ -293,14 +221,17 @@ void MessagePump (const SDL_Event &sev) case SDL_QUIT: exit (0); - case SDL_ACTIVEEVENT: - if (sev.active.state == SDL_APPINPUTFOCUS) + case SDL_WINDOWEVENT: + switch (sev.window.event) { - if (sev.active.gain == 0) - { // kill focus - FlushDIKState (); - } - S_SetSoundPaused(sev.active.gain); + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + if (sev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) + { // kill focus + FlushDIKState (); + } + S_SetSoundPaused(sev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); + break; } break; @@ -320,32 +251,20 @@ void MessagePump (const SDL_Event &sev) */ switch (sev.button.button) { - case 1: event.data1 = KEY_MOUSE1; break; - case 2: event.data1 = KEY_MOUSE3; break; - case 3: event.data1 = KEY_MOUSE2; break; - case 4: event.data1 = KEY_MWHEELUP; break; - case 5: event.data1 = KEY_MWHEELDOWN; break; - case 6: event.data1 = KEY_MOUSE4; break; /* dunno; not generated by my mouse */ - case 7: event.data1 = KEY_MOUSE5; break; /* ditto */ - case 8: event.data1 = KEY_MOUSE4; break; + case SDL_BUTTON_LEFT: event.data1 = KEY_MOUSE1; break; + case SDL_BUTTON_MIDDLE: event.data1 = KEY_MOUSE3; break; + case SDL_BUTTON_RIGHT: event.data1 = KEY_MOUSE2; break; + case 8: event.data1 = KEY_MOUSE4; break; // For whatever reason my side mouse buttons are here. case 9: event.data1 = KEY_MOUSE5; break; - case 10: event.data1 = KEY_MOUSE6; break; - case 11: event.data1 = KEY_MOUSE7; break; - case 12: event.data1 = KEY_MOUSE8; break; + case SDL_BUTTON_X1: event.data1 = KEY_MOUSE6; break; // And these don't exist + case SDL_BUTTON_X2: event.data1 = KEY_MOUSE7; break; + case 6: event.data1 = KEY_MOUSE8; break; default: printf("SDL mouse button %s %d\n", sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break; } if (event.data1 != 0) { - //DIKState[ActiveDIKState][event.data1] = (event.type == EV_KeyDown); - if (event.data1 == KEY_MWHEELUP || event.data1 == KEY_MWHEELDOWN) - { - WheelMoved(&event); - } - else - { - D_PostEvent(&event); - } + D_PostEvent(&event); } } } @@ -354,8 +273,8 @@ void MessagePump (const SDL_Event &sev) int x, y; SDL_GetMouseState (&x, &y); - cursorBlit.x = event.data1 = x; - cursorBlit.y = event.data2 = y; + event.data1 = x; + event.data2 = y; event.type = EV_GUI_Event; if(sev.type == SDL_MOUSEMOTION) event.subtype = EV_GUI_MouseMove; @@ -368,11 +287,25 @@ void MessagePump (const SDL_Event &sev) } break; + case SDL_MOUSEWHEEL: + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = sev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown; + D_PostEvent (&event); + } + else + { + event.type = EV_KeyDown; + event.data1 = sev.wheel.y > 0 ? KEY_MWHEELUP : KEY_MWHEELDOWN; + D_PostEvent (&event); + event.type = EV_KeyUp; + D_PostEvent (&event); + } + break; + case SDL_KEYDOWN: case SDL_KEYUP: - if (sev.key.keysym.sym >= SDLK_LAST) - break; - if (!GUICapture) { event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp; @@ -394,19 +327,19 @@ void MessagePump (const SDL_Event &sev) ((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) | ((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0); - if (sev.key.keysym.sym < SDLK_LAST) + //if (sev.key.keysym.sym < SDLK_LAST) { if (event.subtype == EV_GUI_KeyDown) { - if (DownState[sev.key.keysym.sym]) + if (DownState[sev.key.keysym.scancode]) { event.subtype = EV_GUI_KeyRepeat; } - DownState[sev.key.keysym.sym] = 1; + DownState[sev.key.keysym.scancode] = 1; } else { - DownState[sev.key.keysym.sym] = 0; + DownState[sev.key.keysym.scancode] = 0; } } @@ -442,20 +375,21 @@ void MessagePump (const SDL_Event &sev) } break; } - event.data2 = sev.key.keysym.unicode & 0xff; if (event.data1 < 128) { event.data1 = toupper(event.data1); D_PostEvent (&event); } - if (!iscntrl(event.data2) && event.subtype != EV_GUI_KeyUp) - { - event.subtype = EV_GUI_Char; - event.data1 = event.data2; - event.data2 = sev.key.keysym.mod & KMOD_ALT; - event.data3 = 0; - D_PostEvent (&event); - } + } + break; + + case SDL_TEXTINPUT: + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = EV_GUI_Char; + event.data1 = sev.text.text[0]; + D_PostEvent (&event); } break; diff --git a/src/sdl/i_joystick.cpp b/src/sdl/i_joystick.cpp index 7a529861f..9b1d326ae 100644 --- a/src/sdl/i_joystick.cpp +++ b/src/sdl/i_joystick.cpp @@ -35,7 +35,7 @@ public: FString GetName() { - return SDL_JoystickName(DeviceIndex); + return SDL_JoystickName(Device); } float GetSensitivity() { diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 7a18fb05f..a928b7e7c 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -278,22 +278,11 @@ int main (int argc, char **argv) } atterm (SDL_Quit); - { - char viddriver[80]; - - if (SDL_VideoDriverName(viddriver, sizeof(viddriver)) != NULL) - { - printf("Using video driver %s\n", viddriver); + printf("Using video driver %s\n", SDL_GetCurrentVideoDriver()); #ifdef USE_XCURSOR - UseXCursor = (strcmp(viddriver, "x11") == 0); + UseXCursor = (strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0); #endif - } - printf("\n"); - } - - char caption[100]; - mysnprintf(caption, countof(caption), GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); - SDL_WM_SetCaption(caption, caption); + printf("\n"); #ifdef __APPLE__ diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index e4222633d..838c082b4 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -12,6 +12,7 @@ #include "v_palette.h" #include "sdlvideo.h" #include "r_swrenderer.h" +#include "version.h" #include @@ -56,13 +57,15 @@ private: int FlashAmount; float Gamma; bool UpdatePending; - - SDL_Surface *Screen; - + + SDL_Window *Screen; + SDL_Renderer *Renderer; + SDL_Texture *Texture; + bool NeedPalUpdate; bool NeedGammaUpdate; bool NotPaletted; - + void UpdateColors (); SDLFB () {} @@ -83,9 +86,6 @@ struct MiniModeInfo extern IVideo *Video; extern bool GUICapture; -SDL_Surface *cursorSurface = NULL; -SDL_Rect cursorBlit = {0, 0, 32, 32}; - EXTERN_CVAR (Float, Gamma) EXTERN_CVAR (Int, vid_maxfps) EXTERN_CVAR (Bool, cl_capfps) @@ -93,6 +93,8 @@ EXTERN_CVAR (Bool, vid_vsync) // PUBLIC DATA DEFINITIONS ------------------------------------------------- +CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + CVAR (Int, vid_displaybits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // vid_asyncblit needs a restart to work. SDL doesn't seem to change if the @@ -204,14 +206,19 @@ bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) } else { - SDL_Rect **modes = SDL_ListModes (NULL, SDL_FULLSCREEN|SDL_HWSURFACE); - if (modes != NULL && modes[IteratorMode] != NULL) + SDL_DisplayMode mode = {}, oldmode = {}; + if(IteratorMode != 0) + SDL_GetDisplayMode(vid_adapter, IteratorMode-1, &oldmode); + do { - *width = modes[IteratorMode]->w; - *height = modes[IteratorMode]->h; + if (SDL_GetDisplayMode(vid_adapter, IteratorMode, &mode) != 0) + return false; ++IteratorMode; - return true; - } + } while(mode.w == oldmode.w && mode.h == oldmode.h); + + *width = mode.w; + *height = mode.h; + return true; } return false; } @@ -230,11 +237,11 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscree if (fb->Width == width && fb->Height == height) { - bool fsnow = (fb->Screen->flags & SDL_FULLSCREEN) != 0; + bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; if (fsnow != fullscreen) { - SDL_WM_ToggleFullScreen (fb->Screen); + SDL_SetWindowFullscreen (fb->Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); } return old; } @@ -313,24 +320,37 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) UpdatePending = false; NotPaletted = false; FlashAmount = 0; - Screen = SDL_SetVideoMode (width, height, vid_displaybits, - (vid_asyncblit ? SDL_ASYNCBLIT : 0)|SDL_HWSURFACE|SDL_HWPALETTE|SDL_DOUBLEBUF|SDL_ANYFORMAT| - (fullscreen ? SDL_FULLSCREEN : 0)); + + FString caption; + caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + Screen = SDL_CreateWindow (caption, + SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), + width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); if (Screen == NULL) return; + Renderer = SDL_CreateRenderer (Screen, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE); + if (Renderer == NULL) + return; + + Texture = SDL_CreateTexture (Renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + for (i = 0; i < 256; i++) { GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; } - if (Screen->format->palette == NULL) + //if (Screen->format->palette == NULL) { NotPaletted = true; - GPfx.SetFormat (Screen->format->BitsPerPixel, - Screen->format->Rmask, - Screen->format->Gmask, - Screen->format->Bmask); + + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); } memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdateColors (); @@ -339,6 +359,16 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) SDLFB::~SDLFB () { + if(Screen) + { + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + SDL_DestroyWindow (Screen); + } } bool SDLFB::IsValid () @@ -403,15 +433,18 @@ void SDLFB::Update () SDLFlipCycles.Reset(); BlitCycles.Clock(); - if (SDL_LockSurface (Screen) == -1) + void *pixels; + int pitch; + if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) return; if (NotPaletted) { GPfx.Convert (MemBuffer, Pitch, - Screen->pixels, Screen->pitch, Width, Height, + pixels, pitch, Width, Height, FRACUNIT, FRACUNIT, 0, 0); } +#if 0 else { if (Screen->pitch == Pitch) @@ -426,17 +459,13 @@ void SDLFB::Update () } } } - - SDL_UnlockSurface (Screen); +#endif - if (cursorSurface != NULL && GUICapture) - { - // SDL requires us to draw a surface to get true color cursors. - SDL_BlitSurface(cursorSurface, NULL, Screen, &cursorBlit); - } + SDL_UnlockTexture (Texture); SDLFlipCycles.Clock(); - SDL_Flip (Screen); + SDL_RenderCopy(Renderer, Texture, NULL, NULL); + SDL_RenderPresent(Renderer); SDLFlipCycles.Unclock(); BlitCycles.Unclock(); @@ -478,6 +507,7 @@ void SDLFB::UpdateColors () } GPfx.SetPalette (palette); } +#if 0 else { SDL_Color colors[256]; @@ -496,6 +526,7 @@ void SDLFB::UpdateColors () } SDL_SetPalette (Screen, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256); } +#endif } PalEntry *SDLFB::GetPalette () @@ -541,7 +572,7 @@ void SDLFB::GetFlashedPalette (PalEntry pal[256]) bool SDLFB::IsFullscreen () { - return (Screen->flags & SDL_FULLSCREEN) != 0; + return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; } void SDLFB::SetVSync (bool vsync) From 4aef69600733e6d0dd15e702c0a7057546412e55 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 8 Dec 2014 22:47:40 -0500 Subject: [PATCH 002/117] - Enabled vid_vsync for SDL2 although just turning it on doesn't perform right. - Improved fullscreen a bit. --- src/sdl/sdlvideo.cpp | 94 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 838c082b4..71a534f04 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -43,6 +43,7 @@ public: bool SetGamma (float gamma); bool SetFlash (PalEntry rgb, int amount); void GetFlash (PalEntry &rgb, int &amount); + void SetFullscreen (bool fullscreen); int GetPageCount (); bool IsFullscreen (); @@ -61,12 +62,14 @@ private: SDL_Window *Screen; SDL_Renderer *Renderer; SDL_Texture *Texture; + SDL_Rect UpdateRect; bool NeedPalUpdate; bool NeedGammaUpdate; bool NotPaletted; void UpdateColors (); + void ResetSDLRenderer (); SDLFB () {} }; @@ -241,7 +244,7 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscree if (fsnow != fullscreen) { - SDL_SetWindowFullscreen (fb->Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + fb->SetFullscreen (fullscreen); } return old; } @@ -323,6 +326,7 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) FString caption; caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + Screen = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); @@ -330,33 +334,24 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) if (Screen == NULL) return; - Renderer = SDL_CreateRenderer (Screen, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE); - if (Renderer == NULL) - return; - - Texture = SDL_CreateTexture (Renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + Renderer = NULL; + Texture = NULL; + ResetSDLRenderer (); for (i = 0; i < 256; i++) { GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; } - //if (Screen->format->palette == NULL) - { - NotPaletted = true; - Uint32 format; - SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); - - Uint32 Rmask, Gmask, Bmask, Amask; - int bpp; - SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); - } memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdateColors (); + +#ifdef __APPLE__ SetVSync (vid_vsync); +#endif } + SDLFB::~SDLFB () { if(Screen) @@ -464,7 +459,7 @@ void SDLFB::Update () SDL_UnlockTexture (Texture); SDLFlipCycles.Clock(); - SDL_RenderCopy(Renderer, Texture, NULL, NULL); + SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); SDL_RenderPresent(Renderer); SDLFlipCycles.Unclock(); @@ -570,11 +565,72 @@ void SDLFB::GetFlashedPalette (PalEntry pal[256]) } } +void SDLFB::SetFullscreen (bool fullscreen) +{ + SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + if (!fullscreen) + { + // Restore proper window size + SDL_SetWindowSize (Screen, Width, Height); + } + + ResetSDLRenderer (); +} + bool SDLFB::IsFullscreen () { return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; } +void SDLFB::ResetSDLRenderer () +{ + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + + Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| + (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (!Renderer) + return; + + Texture = SDL_CreateTexture (Renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Width, Height); + + //if (Screen->format->palette == NULL) + { + NotPaletted = true; + + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); + } + + // Calculate update rectangle + if (IsFullscreen ()) + { + int w, h; + SDL_GetWindowSize (Screen, &w, &h); + UpdateRect.w = w; + UpdateRect.h = w*Height/Width; + UpdateRect.x = 0; + UpdateRect.y = (h - UpdateRect.h)/2; + } + else + { + // In windowed mode we just update the whole window. + UpdateRect.x = 0; + UpdateRect.y = 0; + UpdateRect.w = Width; + UpdateRect.h = Height; + } +} + void SDLFB::SetVSync (bool vsync) { #ifdef __APPLE__ @@ -592,6 +648,8 @@ void SDLFB::SetVSync (bool vsync) const GLint value = vsync ? 1 : 0; CGLSetParameter(context, kCGLCPSwapInterval, &value); } +#else + ResetSDLRenderer (); #endif // __APPLE__ } From a7b33a8ce3fd644f27a408b9747a5e1049464c95 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 10 Dec 2014 01:53:22 -0500 Subject: [PATCH 003/117] - Removed XCursor code since SDL2 has proper color cursor support. - Improved international keyboard support by falling back to scan codes if we don't recognize a keycode. - Clear out any residual mouse movement when entering relative mouse mode. --- src/CMakeLists.txt | 13 ----- src/sdl/i_gui.cpp | 39 ------------- src/sdl/i_input.cpp | 134 +++++++++++++++++++++++++++++-------------- src/sdl/i_main.cpp | 7 --- src/sdl/sdlvideo.cpp | 5 +- 5 files changed, 93 insertions(+), 105 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8838dea05..38e399ef1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -189,19 +189,6 @@ else( WIN32 ) set( NO_GTK ON ) endif( GTK2_FOUND ) endif( NOT NO_GTK ) - - # Check for Xcursor library and header files - find_library( XCURSOR_LIB Xcursor ) - if( XCURSOR_LIB ) - find_file( XCURSOR_HEADER "X11/Xcursor/Xcursor.h" ) - if( XCURSOR_HEADER ) - add_definitions( -DUSE_XCURSOR=1 ) - message( STATUS "Found Xcursor at ${XCURSOR_LIB}" ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} ${XCURSOR_LIB} ) - else( XCURSOR_HEADER ) - unset( XCURSOR_LIB ) - endif( XCURSOR_HEADER ) - endif( XCURSOR_LIB ) endif( APPLE ) set( NASM_NAMES nasm ) diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index 39b66c085..c2b91b39c 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -9,22 +9,6 @@ #include "v_palette.h" #include "textures.h" -#ifdef USE_XCURSOR -// Xlib has its own GC, so don't let it interfere. -#define GC XGC -#include -#undef GC - -bool UseXCursor; -SDL_Cursor *X11Cursor; -SDL_Cursor *FirstCursor; - -SDL_Cursor *CreateColorCursor(FTexture *cursorpic) -{ - return NULL; -} -#endif - bool I_SetCursor(FTexture *cursorpic) { static SDL_Cursor *cursor; @@ -38,21 +22,6 @@ bool I_SetCursor(FTexture *cursorpic) return false; } -#ifdef USE_XCURSOR - if (UseXCursor) - { - if (FirstCursor == NULL) - { - FirstCursor = SDL_GetCursor(); - } - X11Cursor = CreateColorCursor(cursorpic); - if (X11Cursor != NULL) - { - SDL_SetCursor(X11Cursor); - return true; - } - } -#endif if (cursorSurface == NULL) cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); @@ -82,14 +51,6 @@ bool I_SetCursor(FTexture *cursorpic) SDL_FreeSurface(cursorSurface); cursorSurface = NULL; } -#ifdef USE_XCURSOR - if (X11Cursor != NULL) - { - SDL_SetCursor(FirstCursor); - SDL_FreeCursor(X11Cursor); - X11Cursor = NULL; - } -#endif } return true; } diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index 722345cb2..ea877e204 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -35,19 +35,18 @@ EXTERN_CVAR (Bool, fullscreen) extern int WaitingForKey, chatmodeon; extern constate_e ConsoleState; -static TMap KeySymToDIK; static bool DownState[SDL_NUM_SCANCODES]; -static SDL_Keycode DIKToKeySym[256] = +static const SDL_Keycode DIKToKeySym[256] = { - 0, SDLK_ESCAPE, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', SDLK_BACKSPACE, SDLK_TAB, - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', SDLK_RETURN, SDLK_LCTRL, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', SDLK_SEMICOLON, - '\'', '`', SDLK_LSHIFT, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', SDLK_RSHIFT, SDLK_KP_MULTIPLY, - SDLK_LALT, ' ', SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, + 0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, + SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB, + SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, + SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s, + SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, + SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v, + SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY, + SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7, SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1, SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11, @@ -62,24 +61,58 @@ static SDL_Keycode DIKToKeySym[256] = 0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ, + 0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ, SDLK_RALT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME, SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END, SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0, - 0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP, + 0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH, + SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static void FlushDIKState (int low=0, int high=NUM_KEYS-1) +static const SDL_Scancode DIKToKeyScan[256] = { -} + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, + SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, + SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, + SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, + SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, + SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, + SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY, + SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, + SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7, + SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1, + SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11, + SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ, + SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME, + SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END, + SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH, + SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN +}; -static void InitKeySymMap () +static TMap InitKeySymMap () { + TMap KeySymToDIK; + for (int i = 0; i < 256; ++i) { KeySymToDIK[DIKToKeySym[i]] = i; @@ -90,7 +123,23 @@ static void InitKeySymMap () KeySymToDIK[SDLK_RALT] = DIK_LMENU; // Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ; + + return KeySymToDIK; } +static const TMap KeySymToDIK(InitKeySymMap()); + +static TMap InitKeyScanMap () +{ + TMap KeyScanToDIK; + + for (int i = 0; i < 256; ++i) + { + KeyScanToDIK[DIKToKeyScan[i]] = i; + } + + return KeyScanToDIK; +} +static const TMap KeyScanToDIK(InitKeyScanMap()); static void I_CheckGUICapture () { @@ -110,7 +159,6 @@ static void I_CheckGUICapture () GUICapture = wantCapt; if (wantCapt) { - FlushDIKState (); memset (DownState, 0, sizeof(DownState)); } } @@ -118,6 +166,8 @@ static void I_CheckGUICapture () void I_SetMouseCapture() { + // Clear out any mouse movement. + SDL_GetRelativeMouseState (NULL, NULL); SDL_SetRelativeMouseMode (SDL_TRUE); } @@ -193,7 +243,7 @@ static bool inGame() static void I_CheckNativeMouse () { - bool focus = SDL_GetMouseFocus() != NULL; + bool focus = SDL_GetKeyboardFocus() != NULL; bool fs = screen->IsFullscreen(); bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame()); @@ -202,11 +252,10 @@ static void I_CheckNativeMouse () { NativeMouse = wantNative; SDL_ShowCursor (wantNative); - SDL_SetRelativeMouseMode (wantNative ? SDL_FALSE : SDL_TRUE); if (wantNative) - { - FlushDIKState (KEY_MOUSE1, KEY_MOUSE8); - } + I_ReleaseMouseCapture (); + else + I_SetMouseCapture (); } } @@ -226,10 +275,6 @@ void MessagePump (const SDL_Event &sev) { case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_LOST: - if (sev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) - { // kill focus - FlushDIKState (); - } S_SetSoundPaused(sev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); break; } @@ -309,7 +354,16 @@ void MessagePump (const SDL_Event &sev) if (!GUICapture) { event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp; - event.data1 = KeySymToDIK[sev.key.keysym.sym]; + + // Try to look up our key mapped key for conversion to DirectInput. + // If that fails, then we'll do a lookup against the scan code, + // which may not return the right key, but at least the key should + // work in the game. + if (const BYTE *dik = KeySymToDIK.CheckKey (sev.key.keysym.sym)) + event.data1 = *dik; + else if (const BYTE *dik = KeyScanToDIK.CheckKey (sev.key.keysym.scancode)) + event.data1 = *dik; + if (event.data1) { if (sev.key.keysym.sym < 256) @@ -327,20 +381,17 @@ void MessagePump (const SDL_Event &sev) ((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) | ((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0); - //if (sev.key.keysym.sym < SDLK_LAST) + if (event.subtype == EV_GUI_KeyDown) { - if (event.subtype == EV_GUI_KeyDown) + if (DownState[sev.key.keysym.scancode]) { - if (DownState[sev.key.keysym.scancode]) - { - event.subtype = EV_GUI_KeyRepeat; - } - DownState[sev.key.keysym.scancode] = 1; - } - else - { - DownState[sev.key.keysym.scancode] = 0; + event.subtype = EV_GUI_KeyRepeat; } + DownState[sev.key.keysym.scancode] = 1; + } + else + { + DownState[sev.key.keysym.scancode] = 0; } switch (sev.key.keysym.sym) @@ -430,10 +481,5 @@ void I_StartTic () void I_ProcessJoysticks (); void I_StartFrame () { - if (KeySymToDIK[SDLK_BACKSPACE] == 0) - { - InitKeySymMap (); - } - I_ProcessJoysticks(); } diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index a928b7e7c..85be7dcef 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -85,10 +85,6 @@ void Mac_I_FatalError(const char* errortext); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -#ifdef USE_XCURSOR -extern bool UseXCursor; -#endif - // PUBLIC DATA DEFINITIONS ------------------------------------------------- #ifndef NO_GTK @@ -279,9 +275,6 @@ int main (int argc, char **argv) atterm (SDL_Quit); printf("Using video driver %s\n", SDL_GetCurrentVideoDriver()); -#ifdef USE_XCURSOR - UseXCursor = (strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0); -#endif printf("\n"); #ifdef __APPLE__ diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 71a534f04..a47e2a813 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -361,7 +361,8 @@ SDLFB::~SDLFB () if (Texture) SDL_DestroyTexture (Texture); SDL_DestroyRenderer (Renderer); - } + } + SDL_DestroyWindow (Screen); } } @@ -657,6 +658,6 @@ ADD_STAT (blit) { FString out; out.Format ("blit=%04.1f ms flip=%04.1f ms", - BlitCycles.Time() * 1e-3, SDLFlipCycles.TimeMS()); + BlitCycles.TimeMS(), SDLFlipCycles.TimeMS()); return out; } From 965d602d26167fdec3e6d5c5f6d7f12063d22128 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 11 Dec 2014 01:35:27 -0500 Subject: [PATCH 004/117] - Improve letterboxing in fullscreen by taking into account animorphic ratio settings. - Added more resolutions to the hard coded table (up to 5K). - Since we're using scaling for fullscreen, we should probably just use the hard coded table for fullscreen resolutions as well. - Fixed: Resolution menu used fake aspect ratio to determine which aspect to file a resolution under. - Added a way to use SDL surface blitting instead of accelerated textures. --- src/menu/videomenu.cpp | 6 +- src/sdl/i_input.cpp | 29 +++++++ src/sdl/sdlvideo.cpp | 185 +++++++++++++++++++++++++++-------------- src/sdl/sdlvideo.h | 1 - 4 files changed, 156 insertions(+), 65 deletions(-) diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index 1d1d02383..5e44bbfd4 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -247,8 +247,12 @@ static void BuildModesList (int hiwidth, int hiheight, int hi_bits) if (Video != NULL) { while ((haveMode = Video->NextMode (&width, &height, &letterbox)) && - (ratiomatch >= 0 && CheckRatio (width, height) != ratiomatch)) + ratiomatch >= 0) { + int ratio; + CheckRatio (width, height, &ratio); + if (ratio == ratiomatch) + break; } } diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index ea877e204..210cf2e2c 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -18,6 +18,8 @@ #include "templates.h" #include "s_sound.h" +void ScaleWithAspect (int &w, int &h, int Width, int Height); + static void I_CheckGUICapture (); static void I_CheckNativeMouse (); @@ -318,6 +320,33 @@ void MessagePump (const SDL_Event &sev) int x, y; SDL_GetMouseState (&x, &y); + // Detect if we're doing scaling in the Window and adjust the mouse + // coordinates accordingly. This could be more efficent, but I + // don't think performance is an issue in the menus. + SDL_Window *focus; + if (screen->IsFullscreen() && (focus = SDL_GetMouseFocus ())) + { + int w, h; + SDL_GetWindowSize (focus, &w, &h); + int realw = w, realh = h; + ScaleWithAspect (realw, realh, SCREENWIDTH, SCREENHEIGHT); + if (realw != SCREENWIDTH || realh != SCREENHEIGHT) + { + double xratio = (double)SCREENWIDTH/realw; + double yratio = (double)SCREENHEIGHT/realh; + if (realw < w) + { + x = (x - (w - realw)/2)*xratio; + y *= yratio; + } + else + { + y = (y - (h - realh)/2)*yratio; + x *= xratio; + } + } + } + event.data1 = x; event.data2 = y; event.type = EV_GUI_Event; diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index a47e2a813..5b96389a5 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -61,9 +61,14 @@ private: SDL_Window *Screen; SDL_Renderer *Renderer; - SDL_Texture *Texture; + union + { + SDL_Texture *Texture; + SDL_Surface *Surface; + }; SDL_Rect UpdateRect; + bool UsingRenderer; bool NeedPalUpdate; bool NeedGammaUpdate; bool NotPaletted; @@ -98,11 +103,9 @@ EXTERN_CVAR (Bool, vid_vsync) CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Int, vid_displaybits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -// vid_asyncblit needs a restart to work. SDL doesn't seem to change if the -// frame buffer is changed at run time. -CVAR (Bool, vid_asyncblit, 1, CVAR_NOINITCALL|CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -151,6 +154,7 @@ static MiniModeInfo WinModes[] = { 960, 600 }, // 16:10 { 960, 720 }, { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 { 1024, 640 }, // 16:10 { 1024, 768 }, { 1088, 612 }, // 16:9 @@ -160,6 +164,7 @@ static MiniModeInfo WinModes[] = { 1280, 720 }, // 16:9 { 1280, 800 }, // 16:10 { 1280, 960 }, + { 1280, 1024 }, // 5:4 { 1360, 768 }, // 16:9 { 1400, 787 }, // 16:9 { 1400, 875 }, // 16:10 @@ -168,6 +173,13 @@ static MiniModeInfo WinModes[] = { 1600, 1000 }, // 16:10 { 1600, 1200 }, { 1920, 1080 }, + { 1920, 1200 }, + { 2048, 1536 }, + { 2560, 1440 }, + { 2560, 1600 }, + { 2880, 1800 }, + { 3840, 2160 }, + { 5120, 2880 } }; static cycle_t BlitCycles; @@ -175,10 +187,34 @@ static cycle_t SDLFlipCycles; // CODE -------------------------------------------------------------------- +void ScaleWithAspect (int &w, int &h, int Width, int Height) +{ + int resRatio = CheckRatio (Width, Height); + int screenRatio; + CheckRatio (w, h, &screenRatio); + if (resRatio == screenRatio) + return; + + double yratio; + switch(resRatio) + { + case 0: yratio = 4./3.; break; + case 1: yratio = 16./9.; break; + case 2: yratio = 16./10.; break; + case 3: yratio = 17./10.; break; + case 4: yratio = 5./4.; break; + default: return; + } + double y = w/yratio; + if (y > h) + w = h*yratio; + else + h = y; +} + SDLVideo::SDLVideo (int parm) { IteratorBits = 0; - IteratorFS = false; } SDLVideo::~SDLVideo () @@ -189,38 +225,18 @@ void SDLVideo::StartModeIterator (int bits, bool fs) { IteratorMode = 0; IteratorBits = bits; - IteratorFS = fs; } bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) { if (IteratorBits != 8) return false; - - if (!IteratorFS) - { - if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) - { - *width = WinModes[IteratorMode].Width; - *height = WinModes[IteratorMode].Height; - ++IteratorMode; - return true; - } - } - else - { - SDL_DisplayMode mode = {}, oldmode = {}; - if(IteratorMode != 0) - SDL_GetDisplayMode(vid_adapter, IteratorMode-1, &oldmode); - do - { - if (SDL_GetDisplayMode(vid_adapter, IteratorMode, &mode) != 0) - return false; - ++IteratorMode; - } while(mode.w == oldmode.w && mode.h == oldmode.h); - *width = mode.w; - *height = mode.h; + if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) + { + *width = WinModes[IteratorMode].Width; + *height = WinModes[IteratorMode].Height; + ++IteratorMode; return true; } return false; @@ -431,8 +447,19 @@ void SDLFB::Update () void *pixels; int pitch; - if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) - return; + if (UsingRenderer) + { + if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) + return; + } + else + { + if (SDL_LockSurface (Surface)) + return; + + pixels = Surface->pixels; + pitch = Surface->pitch; + } if (NotPaletted) { @@ -440,29 +467,38 @@ void SDLFB::Update () pixels, pitch, Width, Height, FRACUNIT, FRACUNIT, 0, 0); } -#if 0 else { - if (Screen->pitch == Pitch) + if (pitch == Pitch) { - memcpy (Screen->pixels, MemBuffer, Width*Height); + memcpy (pixels, MemBuffer, Width*Height); } else { for (int y = 0; y < Height; ++y) { - memcpy ((BYTE *)Screen->pixels+y*Screen->pitch, MemBuffer+y*Pitch, Width); + memcpy ((BYTE *)pixels+y*pitch, MemBuffer+y*Pitch, Width); } } } -#endif - SDL_UnlockTexture (Texture); + if (UsingRenderer) + { + SDL_UnlockTexture (Texture); - SDLFlipCycles.Clock(); - SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); - SDL_RenderPresent(Renderer); - SDLFlipCycles.Unclock(); + SDLFlipCycles.Clock(); + SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); + SDL_RenderPresent(Renderer); + SDLFlipCycles.Unclock(); + } + else + { + SDL_UnlockSurface (Surface); + + SDLFlipCycles.Clock(); + SDL_UpdateWindowSurface (Screen); + SDLFlipCycles.Unclock(); + } BlitCycles.Unclock(); @@ -503,7 +539,6 @@ void SDLFB::UpdateColors () } GPfx.SetPalette (palette); } -#if 0 else { SDL_Color colors[256]; @@ -520,9 +555,8 @@ void SDLFB::UpdateColors () 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], FlashAmount); } - SDL_SetPalette (Screen, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256); + SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256); } -#endif } PalEntry *SDLFB::GetPalette () @@ -592,24 +626,48 @@ void SDLFB::ResetSDLRenderer () SDL_DestroyRenderer (Renderer); } - Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| - (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); - if (!Renderer) - return; - - Texture = SDL_CreateTexture (Renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Width, Height); - - //if (Screen->format->palette == NULL) + UsingRenderer = !vid_forcesurface; + if (UsingRenderer) { - NotPaletted = true; + Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| + (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (!Renderer) + return; - Uint32 format; - SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + Uint32 fmt; + switch(vid_displaybits) + { + default: fmt = SDL_PIXELFORMAT_ARGB8888; break; + case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; + case 24: fmt = SDL_PIXELFORMAT_RGB888; break; + case 16: fmt = SDL_PIXELFORMAT_RGB565; break; + case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + } + Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); - Uint32 Rmask, Gmask, Bmask, Amask; - int bpp; - SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); + { + NotPaletted = true; + + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); + } + } + else + { + Surface = SDL_GetWindowSurface (Screen); + + if (Surface->format->palette == NULL) + { + NotPaletted = true; + GPfx.SetFormat (Surface->format->BitsPerPixel, Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask); + } + else + NotPaletted = false; } // Calculate update rectangle @@ -618,8 +676,9 @@ void SDLFB::ResetSDLRenderer () int w, h; SDL_GetWindowSize (Screen, &w, &h); UpdateRect.w = w; - UpdateRect.h = w*Height/Width; - UpdateRect.x = 0; + UpdateRect.h = h; + ScaleWithAspect (UpdateRect.w, UpdateRect.h, Width, Height); + UpdateRect.x = (w - UpdateRect.w)/2; UpdateRect.y = (h - UpdateRect.h)/2; } else diff --git a/src/sdl/sdlvideo.h b/src/sdl/sdlvideo.h index 3cd38a140..8869f6fa7 100644 --- a/src/sdl/sdlvideo.h +++ b/src/sdl/sdlvideo.h @@ -18,5 +18,4 @@ class SDLVideo : public IVideo private: int IteratorMode; int IteratorBits; - bool IteratorFS; }; From da8f2185d8fb69df6e012c24add8b01dba2a1f59 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 11 Dec 2014 16:54:38 -0500 Subject: [PATCH 005/117] - Adapt SDL2 changes for the Mac (both using SDL2 backend and Cocoa). --- src/CMakeLists.txt | 7 +- src/cocoa/i_backend_cocoa.mm | 204 ++++++++++++++++++------- src/{sdl => cocoa}/i_system_cocoa.mm | 0 src/{sdl => cocoa}/iwadpicker_cocoa.mm | 0 src/sdl/i_main.cpp | 18 ++- 5 files changed, 159 insertions(+), 70 deletions(-) rename src/{sdl => cocoa}/i_system_cocoa.mm (100%) rename src/{sdl => cocoa}/iwadpicker_cocoa.mm (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38e399ef1..61fea294d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -560,8 +560,8 @@ set( PLAT_SDL_SPECIAL_SOURCES sdl/i_joystick.cpp sdl/i_timer.cpp ) set( PLAT_MAC_SOURCES - sdl/iwadpicker_cocoa.mm - sdl/i_system_cocoa.mm ) + cocoa/iwadpicker_cocoa.mm + cocoa/i_system_cocoa.mm ) set( PLAT_COCOA_SOURCES cocoa/HID_Config_Utilities.c cocoa/HID_Error_Handler.c @@ -581,8 +581,9 @@ if( APPLE ) if( OSX_COCOA_BACKEND ) set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) + add_definitions( -DUSE_NATIVE_COCOA ) else( OSX_COCOA_BACKEND ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} sdl/SDLMain.m ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} ) endif( OSX_COCOA_BACKEND ) set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index bd3dc3115..df2a1f450 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1702,37 +1702,27 @@ void SDL_Quit() } -char* SDL_GetError() +const char* SDL_GetError() { static char empty[] = {0}; return empty; } -char* SDL_VideoDriverName(char* namebuf, int maxlen) +const char* SDL_GetCurrentVideoDriver() { - return strncpy(namebuf, "Native OpenGL", maxlen); + return "Native OpenGL"; } -const SDL_VideoInfo* SDL_GetVideoInfo() +int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode) { - // NOTE: Only required fields are assigned - - static SDL_PixelFormat pixelFormat; - memset(&pixelFormat, 0, sizeof(pixelFormat)); - - pixelFormat.BitsPerPixel = 32; - - static SDL_VideoInfo videoInfo; - memset(&videoInfo, 0, sizeof(videoInfo)); - + // NOTE: Only required fields are assigned const NSRect displayRect = [[NSScreen mainScreen] frame]; - videoInfo.current_w = displayRect.size.width; - videoInfo.current_h = displayRect.size.height; - videoInfo.vfmt = &pixelFormat; + mode->w = displayRect.size.width; + mode->h = displayRect.size.height; - return &videoInfo; + return 0; } SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) @@ -1820,73 +1810,151 @@ static SDL_PixelFormat* GetPixelFormat() result.Rmask = 0x000000FF; result.Gmask = 0x0000FF00; result.Bmask = 0x00FF0000; - result.Amask = 0xFF000000; - result.colorkey = 0; - result.alpha = 0xFF; + result.Amask = 0xFF000000; return &result; } - -SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) +SDL_bool SDL_PixelFormatEnumToMasks(Uint32 format, int* bpp, Uint32* Rmask, Uint32* Gmask, Uint32* Bmask, Uint32* Amask) { - [appCtrl changeVideoResolution:(SDL_FULLSCREEN & flags) + assert(format == SDL_PIXELFORMAT_ABGR8888); + + *bpp = 32; + *Rmask = 0x000000FF; + *Gmask = 0x0000FF00; + *Bmask = 0x00FF0000; + *Amask = 0xFF000000; + + return SDL_TRUE; +} + +struct SDL_Window +{ + Uint32 flags; + int w, h; + int pitch; + void *pixels; +}; + +struct SDL_Renderer { SDL_Window *window; }; +struct SDL_Texture { SDL_Window *window; }; + +SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int width, int height, Uint32 flags) +{ + [appCtrl changeVideoResolution:(SDL_WINDOW_FULLSCREEN_DESKTOP & flags) width:width height:height useHiDPI:vid_hidpi]; - static SDL_Surface result; + static SDL_Window result; - if (!(SDL_OPENGL & flags)) + if (!(SDL_WINDOW_OPENGL & flags)) { [appCtrl setupSoftwareRenderingWithWidth:width height:height]; } result.flags = flags; - result.format = GetPixelFormat(); result.w = width; result.h = height; result.pitch = width * BYTES_PER_PIXEL; result.pixels = [appCtrl softwareRenderingBuffer]; - result.refcount = 1; - - result.clip_rect.x = 0; - result.clip_rect.y = 0; - result.clip_rect.w = width; - result.clip_rect.h = height; - + return &result; } - - -void SDL_WM_SetCaption(const char* title, const char* icon) +void SDL_DestroyWindow(SDL_Window *window) { - ZD_UNUSED(title); - ZD_UNUSED(icon); - - // Window title is set in SDL_SetVideoMode() + ZD_UNUSED(window); } -int SDL_WM_ToggleFullScreen(SDL_Surface* surface) +Uint32 SDL_GetWindowFlags(SDL_Window *window) { - if (surface->flags & SDL_FULLSCREEN) + return window->flags; +} + +SDL_Surface *SDL_GetWindowSurface(SDL_Window *window) +{ + ZD_UNUSED(window); + return NULL; +} + +void SDL_GetWindowSize(SDL_Window *window, int *w, int *h) +{ + *w = window->w; + *h = window->h; +} + +void SDL_SetWindowSize(SDL_Window *window, int w, int h) +{ + // Currently this is used for handling the fullscreen->windowed transition. + // We can just no-op this for now. + ZD_UNUSED(window); + ZD_UNUSED(w); + ZD_UNUSED(h); +} + +int SDL_SetWindowFullscreen(SDL_Window* window, Uint32 flags) +{ + if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == (flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) + return 0; + + if (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { - surface->flags &= ~SDL_FULLSCREEN; + window->flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP; } else { - surface->flags |= SDL_FULLSCREEN; + window->flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } - [appCtrl changeVideoResolution:(SDL_FULLSCREEN & surface->flags) - width:surface->w - height:surface->h + [appCtrl changeVideoResolution:(SDL_WINDOW_FULLSCREEN_DESKTOP & flags) + width:window->w + height:window->h useHiDPI:vid_hidpi]; - return 1; + return 0; } +SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags) +{ + ZD_UNUSED(index); + ZD_UNUSED(flags); + + static SDL_Renderer result; + result.window = window; + + return &result; +} +void SDL_DestroyRenderer(SDL_Renderer *renderer) +{ + ZD_UNUSED(renderer); +} + +SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h) +{ + ZD_UNUSED(format); + ZD_UNUSED(access); + ZD_UNUSED(w); + ZD_UNUSED(h); + + static SDL_Texture result; + result.window = renderer->window; + + return &result; +} +void SDL_DestroyTexture(SDL_Texture *texture) +{ + ZD_UNUSED(texture); +} + +int SDL_QueryTexture(SDL_Texture *texture, Uint32* format, int* access, int* w, int* h) +{ + if(format) *format = SDL_PIXELFORMAT_ABGR8888; + if(access) *access = SDL_TEXTUREACCESS_STREAMING; + if(w) *w = texture->window->w; + if(h) *h = texture->window->h; + return 0; +} void SDL_GL_SwapBuffers() { @@ -1918,18 +1986,22 @@ void SDL_UnlockSurface(SDL_Surface* surface) ZD_UNUSED(surface); } -int SDL_BlitSurface(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect) +int SDL_LockTexture(SDL_Texture* texture, const SDL_Rect *rect, void** pixels, int *pitch) { - ZD_UNUSED(src); - ZD_UNUSED(srcrect); - ZD_UNUSED(dst); - ZD_UNUSED(dstrect); - + assert(NULL == rect); + + *pixels = texture->window->pixels; + *pitch = texture->window->pitch; + return 0; } +void SDL_UnlockTexture(SDL_Texture *texture) +{ + ZD_UNUSED(texture); +} -int SDL_Flip(SDL_Surface* screen) +int SDL_UpdateWindowSurface(SDL_Window *screen) { assert(NULL != screen); @@ -1976,10 +2048,24 @@ int SDL_Flip(SDL_Surface* screen) return 0; } -int SDL_SetPalette(SDL_Surface* surface, int flags, SDL_Color* colors, int firstcolor, int ncolors) +int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect) { - ZD_UNUSED(surface); - ZD_UNUSED(flags); + ZD_UNUSED(renderer); + ZD_UNUSED(texture); + ZD_UNUSED(srcrect); + ZD_UNUSED(dstrect); + + return 0; +} + +void SDL_RenderPresent(SDL_Renderer *renderer) +{ + SDL_UpdateWindowSurface(renderer->window); +} + +int SDL_SetPaletteColors(SDL_Palette* palette, const SDL_Color* colors, int firstcolor, int ncolors) +{ + ZD_UNUSED(palette); ZD_UNUSED(colors); ZD_UNUSED(firstcolor); ZD_UNUSED(ncolors); diff --git a/src/sdl/i_system_cocoa.mm b/src/cocoa/i_system_cocoa.mm similarity index 100% rename from src/sdl/i_system_cocoa.mm rename to src/cocoa/i_system_cocoa.mm diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/cocoa/iwadpicker_cocoa.mm similarity index 100% rename from src/sdl/iwadpicker_cocoa.mm rename to src/cocoa/iwadpicker_cocoa.mm diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 85be7dcef..33395e0f1 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -237,7 +237,11 @@ void I_ShutdownJoysticks(); const char* I_GetBackEndName(); +#ifdef USE_NATIVE_COCOA +int SDL_main (int argc, char **argv) +#else int main (int argc, char **argv) +#endif { #if !defined (__APPLE__) { @@ -278,9 +282,9 @@ int main (int argc, char **argv) printf("\n"); #ifdef __APPLE__ - - const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo(); - if ( NULL != videoInfo ) + EXTERN_CVAR( Int, vid_adapter ) + SDL_DisplayMode videoInfo = {}; + if ( SDL_GetDesktopDisplayMode (vid_adapter, &videoInfo) == 0 ) { EXTERN_CVAR( Int, vid_defwidth ) EXTERN_CVAR( Int, vid_defheight ) @@ -288,13 +292,11 @@ int main (int argc, char **argv) EXTERN_CVAR( Bool, vid_vsync ) EXTERN_CVAR( Bool, fullscreen ) - vid_defwidth = videoInfo->current_w; - vid_defheight = videoInfo->current_h; - vid_defbits = videoInfo->vfmt->BitsPerPixel; + vid_defwidth = videoInfo.w; + vid_defheight = videoInfo.h; vid_vsync = true; fullscreen = true; - } - + } #endif // __APPLE__ try From 6241f047a89e3064d99e84969671f5b45181e0dd Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Dec 2014 12:32:20 +0200 Subject: [PATCH 006/117] Removed no longer used SDL wrapper functions from OS X native backend --- src/cocoa/i_backend_cocoa.mm | 70 ------------------------------------ 1 file changed, 70 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index df2a1f450..6abf1b7d1 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1725,72 +1725,6 @@ int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode) return 0; } -SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) -{ - ZD_UNUSED(format); - ZD_UNUSED(flags); - - static std::vector resolutions; - - if (resolutions.empty()) - { -#define DEFINE_RESOLUTION(WIDTH, HEIGHT) \ - static SDL_Rect resolution_##WIDTH##_##HEIGHT = { 0, 0, WIDTH, HEIGHT }; \ - resolutions.push_back(&resolution_##WIDTH##_##HEIGHT); - - DEFINE_RESOLUTION( 640, 480); - DEFINE_RESOLUTION( 720, 480); - DEFINE_RESOLUTION( 800, 480); - DEFINE_RESOLUTION( 800, 600); - DEFINE_RESOLUTION(1024, 600); - DEFINE_RESOLUTION(1024, 640); - DEFINE_RESOLUTION(1024, 768); - DEFINE_RESOLUTION(1152, 720); - DEFINE_RESOLUTION(1152, 864); - DEFINE_RESOLUTION(1280, 720); - DEFINE_RESOLUTION(1280, 768); - DEFINE_RESOLUTION(1280, 800); - DEFINE_RESOLUTION(1280, 854); - DEFINE_RESOLUTION(1280, 960); - DEFINE_RESOLUTION(1280, 1024); - DEFINE_RESOLUTION(1366, 768); - DEFINE_RESOLUTION(1400, 1050); - DEFINE_RESOLUTION(1440, 900); - DEFINE_RESOLUTION(1440, 960); - DEFINE_RESOLUTION(1440, 1080); - DEFINE_RESOLUTION(1600, 900); - DEFINE_RESOLUTION(1600, 1200); - DEFINE_RESOLUTION(1680, 1050); - DEFINE_RESOLUTION(1920, 1080); - DEFINE_RESOLUTION(1920, 1200); - DEFINE_RESOLUTION(2048, 1080); - DEFINE_RESOLUTION(2048, 1536); - DEFINE_RESOLUTION(2560, 1080); - DEFINE_RESOLUTION(2560, 1440); - DEFINE_RESOLUTION(2560, 1600); - DEFINE_RESOLUTION(2560, 2048); - DEFINE_RESOLUTION(2880, 1800); - DEFINE_RESOLUTION(3200, 1800); - DEFINE_RESOLUTION(3440, 1440); - DEFINE_RESOLUTION(3840, 2160); - DEFINE_RESOLUTION(3840, 2400); - DEFINE_RESOLUTION(4096, 2160); - DEFINE_RESOLUTION(5120, 2880); - -#undef DEFINE_RESOLUTION - - resolutions.push_back(NULL); - } - - return &resolutions[0]; -} - -int SDL_ShowCursor(int) -{ - // Does nothing - return 0; -} - static SDL_PixelFormat* GetPixelFormat() { @@ -2205,10 +2139,6 @@ DarwinVersion GetDarwinVersion() const DarwinVersion darwinVersion = GetDarwinVersion(); -#ifdef main -#undef main -#endif // main - int main(int argc, char** argv) { gettimeofday(&s_startTicks, NULL); From 9837721d7f882ed2e86206f36e7b10cd5352a79c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Dec 2014 12:32:29 +0200 Subject: [PATCH 007/117] Added several missing video resolutions All feasible display dimensions are in the list except with 21:9 aspect ratio --- src/sdl/sdlvideo.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 5b96389a5..e477df526 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -148,6 +148,7 @@ static MiniModeInfo WinModes[] = { 720, 480 }, // 16:10 { 720, 540 }, { 800, 450 }, // 16:9 + { 800, 480 }, { 800, 500 }, // 16:10 { 800, 600 }, { 848, 480 }, // 16:9 @@ -162,13 +163,18 @@ static MiniModeInfo WinModes[] = { 1152, 720 }, // 16:10 { 1152, 864 }, { 1280, 720 }, // 16:9 + { 1280, 854 }, { 1280, 800 }, // 16:10 { 1280, 960 }, { 1280, 1024 }, // 5:4 { 1360, 768 }, // 16:9 + { 1366, 768 }, { 1400, 787 }, // 16:9 { 1400, 875 }, // 16:10 { 1400, 1050 }, + { 1440, 900 }, + { 1440, 960 }, + { 1440, 1080 }, { 1600, 900 }, // 16:9 { 1600, 1000 }, // 16:10 { 1600, 1200 }, @@ -177,8 +183,12 @@ static MiniModeInfo WinModes[] = { 2048, 1536 }, { 2560, 1440 }, { 2560, 1600 }, + { 2560, 2048 }, { 2880, 1800 }, + { 3200, 1800 }, { 3840, 2160 }, + { 3840, 2400 }, + { 4096, 2160 }, { 5120, 2880 } }; From 8d16c2e3c0d4e90cd45702ed81e12f2200347721 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 14 Dec 2014 11:29:28 +0200 Subject: [PATCH 008/117] OS X native backend no longer use files from src/sdl directly Cocoa backend is compiling but failed to link Some files are referenced using symbolic links Apple's HID Utilities were moved to own directory --- src/CMakeLists.txt | 72 +++++++++++--------- src/cocoa/critsec.cpp | 62 +++++++++++++++++ src/cocoa/critsec.h | 53 ++++++++++++++ src/cocoa/dikeys.h | 1 + src/cocoa/hardware.h | 1 + src/cocoa/{ => hid}/HID_Config_Utilities.c | 0 src/cocoa/{ => hid}/HID_Error_Handler.c | 0 src/cocoa/{ => hid}/HID_Name_Lookup.c | 0 src/cocoa/{ => hid}/HID_Queue_Utilities.c | 0 src/cocoa/{ => hid}/HID_Utilities.c | 0 src/cocoa/{ => hid}/HID_Utilities_External.h | 0 src/cocoa/{ => hid}/IOHIDDevice_.c | 0 src/cocoa/{ => hid}/IOHIDDevice_.h | 0 src/cocoa/{ => hid}/IOHIDElement_.c | 0 src/cocoa/{ => hid}/IOHIDElement_.h | 0 src/cocoa/{ => hid}/IOHIDLib_.h | 0 src/cocoa/{ => hid}/ImmrHIDUtilAddOn.c | 0 src/cocoa/{ => hid}/ImmrHIDUtilAddOn.h | 0 src/cocoa/i_backend_cocoa.mm | 42 ------------ src/cocoa/i_cd.cpp | 1 + src/cocoa/i_input.h | 1 + src/cocoa/i_joystick.cpp | 2 +- src/cocoa/i_movie.cpp | 1 + src/cocoa/i_system.h | 1 + src/cocoa/st_start.cpp | 1 + 25 files changed, 161 insertions(+), 77 deletions(-) create mode 100644 src/cocoa/critsec.cpp create mode 100644 src/cocoa/critsec.h create mode 120000 src/cocoa/dikeys.h create mode 120000 src/cocoa/hardware.h rename src/cocoa/{ => hid}/HID_Config_Utilities.c (100%) rename src/cocoa/{ => hid}/HID_Error_Handler.c (100%) rename src/cocoa/{ => hid}/HID_Name_Lookup.c (100%) rename src/cocoa/{ => hid}/HID_Queue_Utilities.c (100%) rename src/cocoa/{ => hid}/HID_Utilities.c (100%) rename src/cocoa/{ => hid}/HID_Utilities_External.h (100%) rename src/cocoa/{ => hid}/IOHIDDevice_.c (100%) rename src/cocoa/{ => hid}/IOHIDDevice_.h (100%) rename src/cocoa/{ => hid}/IOHIDElement_.c (100%) rename src/cocoa/{ => hid}/IOHIDElement_.h (100%) rename src/cocoa/{ => hid}/IOHIDLib_.h (100%) rename src/cocoa/{ => hid}/ImmrHIDUtilAddOn.c (100%) rename src/cocoa/{ => hid}/ImmrHIDUtilAddOn.h (100%) create mode 120000 src/cocoa/i_cd.cpp create mode 120000 src/cocoa/i_input.h create mode 120000 src/cocoa/i_movie.cpp create mode 120000 src/cocoa/i_system.h create mode 120000 src/cocoa/st_start.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 61fea294d..2e6bb59f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -545,7 +545,7 @@ set( PLAT_WIN32_SOURCES win32/i_system.cpp win32/st_start.cpp win32/win32video.cpp ) -set( PLAT_SDL_SYSTEM_SOURCES +set( PLAT_SDL_SOURCES sdl/crashcatcher.c sdl/hardware.cpp sdl/i_cd.cpp @@ -553,8 +553,7 @@ set( PLAT_SDL_SYSTEM_SOURCES sdl/i_movie.cpp sdl/i_system.cpp sdl/sdlvideo.cpp - sdl/st_start.cpp ) -set( PLAT_SDL_SPECIAL_SOURCES + sdl/st_start.cpp sdl/i_gui.cpp sdl/i_input.cpp sdl/i_joystick.cpp @@ -563,39 +562,27 @@ set( PLAT_MAC_SOURCES cocoa/iwadpicker_cocoa.mm cocoa/i_system_cocoa.mm ) set( PLAT_COCOA_SOURCES - cocoa/HID_Config_Utilities.c - cocoa/HID_Error_Handler.c - cocoa/HID_Name_Lookup.c - cocoa/HID_Queue_Utilities.c - cocoa/HID_Utilities.c - cocoa/IOHIDDevice_.c - cocoa/IOHIDElement_.c - cocoa/ImmrHIDUtilAddOn.c + cocoa/hid/HID_Config_Utilities.c + cocoa/hid/HID_Error_Handler.c + cocoa/hid/HID_Name_Lookup.c + cocoa/hid/HID_Queue_Utilities.c + cocoa/hid/HID_Utilities.c + cocoa/hid/IOHIDDevice_.c + cocoa/hid/IOHIDElement_.c + cocoa/hid/ImmrHIDUtilAddOn.c + cocoa/critsec.cpp cocoa/i_backend_cocoa.mm + cocoa/i_cd.cpp cocoa/i_joystick.cpp + cocoa/i_movie.cpp + cocoa/st_start.cpp cocoa/i_timer.cpp cocoa/zdoom.icns ) -if( APPLE ) - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} "${FMOD_LIBRARY}" ) - - if( OSX_COCOA_BACKEND ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) - add_definitions( -DUSE_NATIVE_COCOA ) - else( OSX_COCOA_BACKEND ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} ) - endif( OSX_COCOA_BACKEND ) - - set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) - set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) -else( APPLE ) - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} ) -endif( APPLE ) - if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -606,15 +593,25 @@ if( WIN32 ) else( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) +elseif( APPLE ) + if( OSX_COCOA_BACKEND ) + set( SYSTEM_SOURCES_DIR cocoa ) + set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) + else( OSX_COCOA_BACKEND ) + set( SYSTEM_SOURCES_DIR sdl ) + set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) + endif( OSX_COCOA_BACKEND ) + + set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_MAC_SOURCES} "${FMOD_LIBRARY}" ) + + set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) + set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) else( WIN32 ) set( SYSTEM_SOURCES_DIR sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) - if( APPLE ) - set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_MAC_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - else( APPLE ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_MAC_SOURCES} ) - endif( APPLE ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( WIN32 ) if( NOT ASM_SOURCES ) @@ -672,11 +669,18 @@ endif( DYN_FLUIDSYNTH ) # there's generally a new cpp for every header so this file will get changed if( WIN32 ) set( EXTRA_HEADER_DIRS win32/*.h ) +elseif( APPLE ) + if( OSX_COCOA_BACKEND ) + set( EXTRA_HEADER_DIRS cocoa/*.h ) + else( OSX_COCOA_BACKEND ) + set( EXTRA_HEADER_DIRS sdl/*.h ) + endif( OSX_COCOA_BACKEND ) else( WIN32 ) set( EXTRA_HEADER_DIRS sdl/*.h ) endif( WIN32 ) file( GLOB HEADER_FILES ${EXTRA_HEADER_DIRS} + cocoa/*.h fragglescript/*.h g_doom/*.h g_heretic/*.h diff --git a/src/cocoa/critsec.cpp b/src/cocoa/critsec.cpp new file mode 100644 index 000000000..97bc2251c --- /dev/null +++ b/src/cocoa/critsec.cpp @@ -0,0 +1,62 @@ +/* + ** critsec.cpp + ** + **--------------------------------------------------------------------------- + ** Copyright 2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include "critsec.h" + +// TODO: add error handling + +FCriticalSection::FCriticalSection() +{ + pthread_mutexattr_t attributes; + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&m_mutex, &attributes); + + pthread_mutexattr_destroy(&attributes); +} + +FCriticalSection::~FCriticalSection() +{ + pthread_mutex_destroy(&m_mutex); +} + +void FCriticalSection::Enter() +{ + pthread_mutex_lock(&m_mutex); +} + +void FCriticalSection::Leave() +{ + pthread_mutex_unlock(&m_mutex); +} diff --git a/src/cocoa/critsec.h b/src/cocoa/critsec.h new file mode 100644 index 000000000..20c476ae3 --- /dev/null +++ b/src/cocoa/critsec.h @@ -0,0 +1,53 @@ +/* + ** critsec.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#ifndef CRITSEC_H +#define CRITSEC_H + +#include + +class FCriticalSection +{ +public: + FCriticalSection(); + ~FCriticalSection(); + + void Enter(); + void Leave(); + +private: + pthread_mutex_t m_mutex; + +}; + +#endif diff --git a/src/cocoa/dikeys.h b/src/cocoa/dikeys.h new file mode 120000 index 000000000..011f85acb --- /dev/null +++ b/src/cocoa/dikeys.h @@ -0,0 +1 @@ +../sdl/dikeys.h \ No newline at end of file diff --git a/src/cocoa/hardware.h b/src/cocoa/hardware.h new file mode 120000 index 000000000..4261b9477 --- /dev/null +++ b/src/cocoa/hardware.h @@ -0,0 +1 @@ +../sdl/hardware.h \ No newline at end of file diff --git a/src/cocoa/HID_Config_Utilities.c b/src/cocoa/hid/HID_Config_Utilities.c similarity index 100% rename from src/cocoa/HID_Config_Utilities.c rename to src/cocoa/hid/HID_Config_Utilities.c diff --git a/src/cocoa/HID_Error_Handler.c b/src/cocoa/hid/HID_Error_Handler.c similarity index 100% rename from src/cocoa/HID_Error_Handler.c rename to src/cocoa/hid/HID_Error_Handler.c diff --git a/src/cocoa/HID_Name_Lookup.c b/src/cocoa/hid/HID_Name_Lookup.c similarity index 100% rename from src/cocoa/HID_Name_Lookup.c rename to src/cocoa/hid/HID_Name_Lookup.c diff --git a/src/cocoa/HID_Queue_Utilities.c b/src/cocoa/hid/HID_Queue_Utilities.c similarity index 100% rename from src/cocoa/HID_Queue_Utilities.c rename to src/cocoa/hid/HID_Queue_Utilities.c diff --git a/src/cocoa/HID_Utilities.c b/src/cocoa/hid/HID_Utilities.c similarity index 100% rename from src/cocoa/HID_Utilities.c rename to src/cocoa/hid/HID_Utilities.c diff --git a/src/cocoa/HID_Utilities_External.h b/src/cocoa/hid/HID_Utilities_External.h similarity index 100% rename from src/cocoa/HID_Utilities_External.h rename to src/cocoa/hid/HID_Utilities_External.h diff --git a/src/cocoa/IOHIDDevice_.c b/src/cocoa/hid/IOHIDDevice_.c similarity index 100% rename from src/cocoa/IOHIDDevice_.c rename to src/cocoa/hid/IOHIDDevice_.c diff --git a/src/cocoa/IOHIDDevice_.h b/src/cocoa/hid/IOHIDDevice_.h similarity index 100% rename from src/cocoa/IOHIDDevice_.h rename to src/cocoa/hid/IOHIDDevice_.h diff --git a/src/cocoa/IOHIDElement_.c b/src/cocoa/hid/IOHIDElement_.c similarity index 100% rename from src/cocoa/IOHIDElement_.c rename to src/cocoa/hid/IOHIDElement_.c diff --git a/src/cocoa/IOHIDElement_.h b/src/cocoa/hid/IOHIDElement_.h similarity index 100% rename from src/cocoa/IOHIDElement_.h rename to src/cocoa/hid/IOHIDElement_.h diff --git a/src/cocoa/IOHIDLib_.h b/src/cocoa/hid/IOHIDLib_.h similarity index 100% rename from src/cocoa/IOHIDLib_.h rename to src/cocoa/hid/IOHIDLib_.h diff --git a/src/cocoa/ImmrHIDUtilAddOn.c b/src/cocoa/hid/ImmrHIDUtilAddOn.c similarity index 100% rename from src/cocoa/ImmrHIDUtilAddOn.c rename to src/cocoa/hid/ImmrHIDUtilAddOn.c diff --git a/src/cocoa/ImmrHIDUtilAddOn.h b/src/cocoa/hid/ImmrHIDUtilAddOn.h similarity index 100% rename from src/cocoa/ImmrHIDUtilAddOn.h rename to src/cocoa/hid/ImmrHIDUtilAddOn.h diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 6abf1b7d1..be2d14276 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1625,48 +1625,6 @@ const char* I_GetBackEndName() extern "C" { -struct SDL_mutex -{ - pthread_mutex_t mutex; -}; - - -SDL_mutex* SDL_CreateMutex() -{ - pthread_mutexattr_t attributes; - pthread_mutexattr_init(&attributes); - pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); - - SDL_mutex* result = new SDL_mutex; - - if (0 != pthread_mutex_init(&result->mutex, &attributes)) - { - delete result; - result = NULL; - } - - pthread_mutexattr_destroy(&attributes); - - return result; -} - -int SDL_mutexP(SDL_mutex* mutex) -{ - return pthread_mutex_lock(&mutex->mutex); -} - -int SDL_mutexV(SDL_mutex* mutex) -{ - return pthread_mutex_unlock(&mutex->mutex); -} - -void SDL_DestroyMutex(SDL_mutex* mutex) -{ - pthread_mutex_destroy(&mutex->mutex); - delete mutex; -} - - static timeval s_startTicks; uint32_t SDL_GetTicks() diff --git a/src/cocoa/i_cd.cpp b/src/cocoa/i_cd.cpp new file mode 120000 index 000000000..5573d7e1e --- /dev/null +++ b/src/cocoa/i_cd.cpp @@ -0,0 +1 @@ +../sdl/i_cd.cpp \ No newline at end of file diff --git a/src/cocoa/i_input.h b/src/cocoa/i_input.h new file mode 120000 index 000000000..208b03106 --- /dev/null +++ b/src/cocoa/i_input.h @@ -0,0 +1 @@ +../sdl/i_input.h \ No newline at end of file diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index 56db8f815..234f94c8e 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -37,7 +37,7 @@ #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 -#include "HID_Utilities_External.h" +#include "hid/HID_Utilities_External.h" #include "d_event.h" #include "doomdef.h" diff --git a/src/cocoa/i_movie.cpp b/src/cocoa/i_movie.cpp new file mode 120000 index 000000000..04258f3e5 --- /dev/null +++ b/src/cocoa/i_movie.cpp @@ -0,0 +1 @@ +../sdl/i_movie.cpp \ No newline at end of file diff --git a/src/cocoa/i_system.h b/src/cocoa/i_system.h new file mode 120000 index 000000000..ced516311 --- /dev/null +++ b/src/cocoa/i_system.h @@ -0,0 +1 @@ +../sdl/i_system.h \ No newline at end of file diff --git a/src/cocoa/st_start.cpp b/src/cocoa/st_start.cpp new file mode 120000 index 000000000..38ce4fa1c --- /dev/null +++ b/src/cocoa/st_start.cpp @@ -0,0 +1 @@ +../sdl/st_start.cpp \ No newline at end of file From 9d135a05866d26d6ee03f513ef7aeafb5fdfb9f4 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 14 Dec 2014 13:20:39 +0200 Subject: [PATCH 009/117] Get rid of SDL in more parts of native OS X backend --- src/CMakeLists.txt | 19 +-- src/cocoa/i_backend_cocoa.mm | 232 ++++++++++++++++++++++++++++++----- src/cocoa/i_system.cpp | 1 + src/cocoa/i_timer.cpp | 41 ++++--- src/sdl/i_gui.cpp | 5 - src/sdl/i_main.cpp | 28 +---- src/sdl/i_system.cpp | 1 - 7 files changed, 238 insertions(+), 89 deletions(-) create mode 120000 src/cocoa/i_system.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e6bb59f5..67f7d405b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -549,18 +549,17 @@ set( PLAT_SDL_SOURCES sdl/crashcatcher.c sdl/hardware.cpp sdl/i_cd.cpp - sdl/i_main.cpp - sdl/i_movie.cpp - sdl/i_system.cpp - sdl/sdlvideo.cpp - sdl/st_start.cpp sdl/i_gui.cpp sdl/i_input.cpp sdl/i_joystick.cpp - sdl/i_timer.cpp ) + sdl/i_main.cpp + sdl/i_movie.cpp + sdl/i_system.cpp + sdl/i_timer.cpp + sdl/sdlvideo.cpp + sdl/st_start.cpp ) set( PLAT_MAC_SOURCES - cocoa/iwadpicker_cocoa.mm - cocoa/i_system_cocoa.mm ) + cocoa/iwadpicker_cocoa.mm ) set( PLAT_COCOA_SOURCES cocoa/hid/HID_Config_Utilities.c cocoa/hid/HID_Error_Handler.c @@ -575,8 +574,9 @@ set( PLAT_COCOA_SOURCES cocoa/i_cd.cpp cocoa/i_joystick.cpp cocoa/i_movie.cpp - cocoa/st_start.cpp + cocoa/i_system.cpp cocoa/i_timer.cpp + cocoa/st_start.cpp cocoa/zdoom.icns ) if( WIN32 ) @@ -601,6 +601,7 @@ elseif( APPLE ) else( OSX_COCOA_BACKEND ) set( SYSTEM_SOURCES_DIR sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} cocoa/i_system_cocoa.mm ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( OSX_COCOA_BACKEND ) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index be2d14276..3f4574e04 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -56,15 +56,19 @@ #include "cmdlib.h" #include "d_event.h" #include "d_gui.h" +#include "d_main.h" #include "dikeys.h" #include "doomdef.h" +#include "doomerrors.h" #include "doomstat.h" +#include "m_argv.h" #include "s_sound.h" #include "textures.h" #include "v_video.h" #include "version.h" #include "i_rbopts.h" #include "i_osversion.h" +#include "i_system.h" #undef Class @@ -190,8 +194,12 @@ typedef NSInteger NSApplicationActivationPolicy; RenderBufferOptions rbOpts; -EXTERN_CVAR(Bool, fullscreen) -EXTERN_CVAR(Bool, vid_hidpi) +EXTERN_CVAR(Bool, fullscreen ) +EXTERN_CVAR(Bool, vid_hidpi ) +EXTERN_CVAR(Bool, vid_vsync ) +EXTERN_CVAR(Int, vid_adapter ) +EXTERN_CVAR(Int, vid_defwidth ) +EXTERN_CVAR(Int, vid_defheight) CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -217,9 +225,90 @@ extern constate_e ConsoleState; EXTERN_CVAR(Int, m_use_mouse); +void I_StartupJoysticks(); void I_ShutdownJoysticks(); +// --------------------------------------------------------------------------- + + +DArgs *Args; // command line arguments + +namespace +{ + +// The maximum number of functions that can be registered with atterm. +static const size_t MAX_TERMS = 64; + +static void (*TermFuncs[MAX_TERMS])(); +static const char *TermNames[MAX_TERMS]; +static size_t NumTerms; + +void call_terms() +{ + while (NumTerms > 0) + { + TermFuncs[--NumTerms](); + } +} + +} // unnamed namespace + + +void addterm(void (*func)(), const char *name) +{ + // Make sure this function wasn't already registered. + + for (size_t i = 0; i < NumTerms; ++i) + { + if (TermFuncs[i] == func) + { + return; + } + } + + if (NumTerms == MAX_TERMS) + { + func(); + I_FatalError("Too many exit functions registered."); + } + + TermNames[NumTerms] = name; + TermFuncs[NumTerms] = func; + + ++NumTerms; +} + +void popterm() +{ + if (NumTerms) + { + --NumTerms; + } +} + + +void I_SetMainWindowVisible(bool); + +void Mac_I_FatalError(const char* const message) +{ + I_SetMainWindowVisible(false); + + const CFStringRef errorString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, + message, kCFStringEncodingASCII, kCFAllocatorNull); + + if (NULL != errorString) + { + CFOptionFlags dummy; + + CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, + CFSTR("Fatal Error"), errorString, CFSTR("Exit"), NULL, NULL, &dummy); + + CFRelease(errorString); + } +} + + namespace { @@ -241,6 +330,112 @@ size_t s_skipMouseMoves; NSCursor* s_cursor; +void NewFailure() +{ + I_FatalError("Failed to allocate memory from system heap"); +} + + +int OriginalMain(int argc, char** argv) +{ + printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n", + GetVersionString(), GetGitTime(), __DATE__); + + seteuid(getuid()); + std::set_new_handler(NewFailure); + + // Set LC_NUMERIC environment variable in case some library decides to + // clear the setlocale call at least this will be correct. + // Note that the LANG environment variable is overridden by LC_* + setenv("LC_NUMERIC", "C", 1); + setlocale(LC_ALL, "C"); + + if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE|SDL_INIT_JOYSTICK) == -1) + { + fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError()); + return -1; + } + atterm(SDL_Quit); + + printf("\n"); + + SDL_DisplayMode videoInfo = {}; + + if (0 == SDL_GetDesktopDisplayMode(vid_adapter, &videoInfo)) + { + vid_defwidth = videoInfo.w; + vid_defheight = videoInfo.h; + vid_vsync = true; + fullscreen = true; + } + + try + { + Args = new DArgs(argc, argv); + + /* + killough 1/98: + + This fixes some problems with exit handling + during abnormal situations. + + The old code called I_Quit() to end program, + while now I_Quit() is installed as an exit + handler and exit() is called to exit, either + normally or abnormally. Seg faults are caught + and the error handler is used, to prevent + being left in graphics mode or having very + loud SFX noise because the sound card is + left in an unstable state. + */ + + atexit (call_terms); + atterm (I_Quit); + + // Should we even be doing anything with progdir on Unix systems? + char program[PATH_MAX]; + if (realpath (argv[0], program) == NULL) + strcpy (program, argv[0]); + char *slash = strrchr (program, '/'); + if (slash != NULL) + { + *(slash + 1) = '\0'; + progdir = program; + } + else + { + progdir = "./"; + } + + I_StartupJoysticks(); + C_InitConsole(80 * 8, 25 * 8, false); + D_DoomMain(); + } + catch(const CDoomError& error) + { + const char* const message = error.GetMessage(); + + if (NULL != message) + { + fprintf(stderr, "%s\n", message); + Mac_I_FatalError(message); + } + + exit(-1); + } + catch(...) + { + call_terms(); + throw; + } + + return 0; +} + + +// --------------------------------------------------------------------------- + + void CheckGUICapture() { const bool wantCapture = (MENU_Off == menuactive) @@ -1131,7 +1326,7 @@ static bool s_fullscreenNewAPI; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; - exit(SDL_main(s_argc, s_argv)); + exit(OriginalMain(s_argc, s_argv)); } @@ -1613,33 +1808,9 @@ bool I_SetCursor(FTexture* cursorpic) // --------------------------------------------------------------------------- -const char* I_GetBackEndName() -{ - return "Native Cocoa"; -} - - -// --------------------------------------------------------------------------- - - extern "C" { -static timeval s_startTicks; - -uint32_t SDL_GetTicks() -{ - timeval now; - gettimeofday(&now, NULL); - - const uint32_t ticks = - (now.tv_sec - s_startTicks.tv_sec ) * 1000 - + (now.tv_usec - s_startTicks.tv_usec) / 1000; - - return ticks; -} - - int SDL_Init(Uint32 flags) { ZD_UNUSED(flags); @@ -1667,11 +1838,6 @@ const char* SDL_GetError() } -const char* SDL_GetCurrentVideoDriver() -{ - return "Native OpenGL"; -} - int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode) { // NOTE: Only required fields are assigned @@ -2099,8 +2265,6 @@ const DarwinVersion darwinVersion = GetDarwinVersion(); int main(int argc, char** argv) { - gettimeofday(&s_startTicks, NULL); - for (int i = 0; i <= argc; ++i) { const char* const argument = argv[i]; diff --git a/src/cocoa/i_system.cpp b/src/cocoa/i_system.cpp new file mode 120000 index 000000000..b95185a13 --- /dev/null +++ b/src/cocoa/i_system.cpp @@ -0,0 +1 @@ +../sdl/i_system.cpp \ No newline at end of file diff --git a/src/cocoa/i_timer.cpp b/src/cocoa/i_timer.cpp index 0699a9967..c63561077 100644 --- a/src/cocoa/i_timer.cpp +++ b/src/cocoa/i_timer.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include "basictypes.h" #include "basicinlines.h" #include "doomdef.h" @@ -13,23 +11,36 @@ #include "templates.h" +static timeval s_startTicks; + + unsigned int I_MSTime() { - return SDL_GetTicks(); + timeval now; + gettimeofday(&now, NULL); + + const uint32_t ticks = + (now.tv_sec - s_startTicks.tv_sec ) * 1000 + + (now.tv_usec - s_startTicks.tv_usec) / 1000; + + return ticks; } unsigned int I_FPSTime() { - return SDL_GetTicks(); + timeval now; + gettimeofday(&now, NULL); + + return static_cast( + (now.tv_sec) * 1000 + (now.tv_usec) / 1000); } -bool g_isTicFrozen; - - namespace { +bool s_isTicFrozen; + timespec GetNextTickTime() { static const long MILLISECONDS_IN_SECOND = 1000; @@ -91,7 +102,7 @@ void* TimerThreadFunc(void*) pthread_mutex_lock(&s_timerMutex); pthread_cond_timedwait(&s_timerEvent, &s_timerMutex, &timeToNextTick); - if (!g_isTicFrozen) + if (!s_isTicFrozen) { // The following GCC/Clang intrinsic can be used instead of OS X specific function: // __sync_add_and_fetch(&s_tics, 1); @@ -101,7 +112,7 @@ void* TimerThreadFunc(void*) OSAtomicIncrement32(&s_tics); } - s_timerStart = SDL_GetTicks(); + s_timerStart = I_MSTime(); pthread_cond_broadcast(&s_timerEvent); pthread_mutex_unlock(&s_timerMutex); @@ -122,7 +133,7 @@ int GetTimeThreaded(bool saveMS) int WaitForTicThreaded(int prevTic) { - assert(!g_isTicFrozen); + assert(!s_isTicFrozen); while (s_tics <= prevTic) { @@ -136,7 +147,7 @@ int WaitForTicThreaded(int prevTic) void FreezeTimeThreaded(bool frozen) { - g_isTicFrozen = frozen; + s_isTicFrozen = frozen; } } // unnamed namespace @@ -144,7 +155,7 @@ void FreezeTimeThreaded(bool frozen) fixed_t I_GetTimeFrac(uint32* ms) { - const uint32_t now = SDL_GetTicks(); + const uint32_t now = I_MSTime(); if (NULL != ms) { @@ -157,11 +168,13 @@ fixed_t I_GetTimeFrac(uint32* ms) } -void I_InitTimer () +void I_InitTimer() { assert(!s_timerInitialized); s_timerInitialized = true; + gettimeofday(&s_startTicks, NULL); + pthread_cond_init (&s_timerEvent, NULL); pthread_mutex_init(&s_timerMutex, NULL); @@ -172,7 +185,7 @@ void I_InitTimer () I_FreezeTime = FreezeTimeThreaded; } -void I_ShutdownTimer () +void I_ShutdownTimer() { if (!s_timerInitialized) { diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index c2b91b39c..f9e6714b4 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -59,8 +59,3 @@ void I_SetMainWindowVisible(bool visible) { } - -const char* I_GetBackEndName() -{ - return "SDL"; -} diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 33395e0f1..10a9b8c75 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -235,13 +235,7 @@ static void unprotect_rtext() void I_StartupJoysticks(); void I_ShutdownJoysticks(); -const char* I_GetBackEndName(); - -#ifdef USE_NATIVE_COCOA -int SDL_main (int argc, char **argv) -#else int main (int argc, char **argv) -#endif { #if !defined (__APPLE__) { @@ -250,8 +244,8 @@ int main (int argc, char **argv) } #endif // !__APPLE__ - printf(GAMENAME" %s - %s - %s version\nCompiled on %s\n", - GetVersionString(), GetGitTime(), I_GetBackEndName(), __DATE__); + printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n", + GetVersionString(), GetGitTime(), __DATE__); seteuid (getuid ()); std::set_new_handler (NewFailure); @@ -280,24 +274,6 @@ int main (int argc, char **argv) printf("Using video driver %s\n", SDL_GetCurrentVideoDriver()); printf("\n"); - -#ifdef __APPLE__ - EXTERN_CVAR( Int, vid_adapter ) - SDL_DisplayMode videoInfo = {}; - if ( SDL_GetDesktopDisplayMode (vid_adapter, &videoInfo) == 0 ) - { - EXTERN_CVAR( Int, vid_defwidth ) - EXTERN_CVAR( Int, vid_defheight ) - EXTERN_CVAR( Int, vid_defbits ) - EXTERN_CVAR( Bool, vid_vsync ) - EXTERN_CVAR( Bool, fullscreen ) - - vid_defwidth = videoInfo.w; - vid_defheight = videoInfo.h; - vid_vsync = true; - fullscreen = true; - } -#endif // __APPLE__ try { diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 41fdef323..c2ce4aa9f 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -41,7 +41,6 @@ #include "doomerrors.h" #include -#include "SDL.h" #include "doomtype.h" #include "doomstat.h" #include "version.h" From ce70a7c66e7aaf76ba3671bbd5f2dbd400fa6ae9 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 14 Dec 2014 17:08:47 +0200 Subject: [PATCH 010/117] Video part of native OS X backend as a copy-paste of SDL one Dependency from SDL still exists, pending major refactoring --- src/cocoa/i_backend_cocoa.mm | 874 ++++++++++++++++++++++++++++++++++- 1 file changed, 873 insertions(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 3f4574e04..442a7192a 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -61,9 +61,16 @@ #include "doomdef.h" #include "doomerrors.h" #include "doomstat.h" +#include "hardware.h" #include "m_argv.h" +#include "r_renderer.h" +#include "r_swrenderer.h" #include "s_sound.h" +#include "stats.h" #include "textures.h" +#include "v_palette.h" +#include "v_pfx.h" +#include "v_text.h" #include "v_video.h" #include "version.h" #include "i_rbopts.h" @@ -194,7 +201,7 @@ typedef NSInteger NSApplicationActivationPolicy; RenderBufferOptions rbOpts; -EXTERN_CVAR(Bool, fullscreen ) +EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_hidpi ) EXTERN_CVAR(Bool, vid_vsync ) EXTERN_CVAR(Int, vid_adapter ) @@ -232,6 +239,37 @@ void I_ShutdownJoysticks(); // --------------------------------------------------------------------------- +extern int NewWidth, NewHeight, NewBits, DisplayBits; + + +CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + +CUSTOM_CVAR(Float, vid_winscale, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 1.f) + { + self = 1.f; + } + else if (Video) + { + Video->SetWindowedScale (self); + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; + } +} + + +// --------------------------------------------------------------------------- + + DArgs *Args; // command line arguments namespace @@ -2134,6 +2172,840 @@ int SDL_SetPaletteColors(SDL_Palette* palette, const SDL_Color* colors, int firs } // extern "C" +// --------------------------------------------------------------------------- + + +class CocoaVideo : public IVideo +{ +public: + CocoaVideo(int parm); + ~CocoaVideo(); + + EDisplayType GetDisplayType () { return DISPLAY_Both; } + void SetWindowedScale (float scale); + + DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + + void StartModeIterator (int bits, bool fs); + bool NextMode (int *width, int *height, bool *letterbox); + +private: + int IteratorMode; + int IteratorBits; +}; + +class CocoaFrameBuffer : public DFrameBuffer +{ +public: + CocoaFrameBuffer (int width, int height, bool fullscreen); + ~CocoaFrameBuffer (); + + bool Lock (bool buffer); + void Unlock (); + bool Relock (); + void ForceBuffering (bool force); + bool IsValid (); + void Update (); + PalEntry *GetPalette (); + void GetFlashedPalette (PalEntry pal[256]); + void UpdatePalette (); + bool SetGamma (float gamma); + bool SetFlash (PalEntry rgb, int amount); + void GetFlash (PalEntry &rgb, int &amount); + void SetFullscreen (bool fullscreen); + int GetPageCount (); + bool IsFullscreen (); + + friend class CocoaVideo; + + virtual void SetVSync (bool vsync); + +private: + PalEntry SourcePalette[256]; + BYTE GammaTable[3][256]; + PalEntry Flash; + int FlashAmount; + float Gamma; + bool UpdatePending; + + SDL_Window *Screen; + SDL_Renderer *Renderer; + union + { + SDL_Texture *Texture; + SDL_Surface *Surface; + }; + SDL_Rect UpdateRect; + + bool NeedPalUpdate; + bool NeedGammaUpdate; + bool NotPaletted; + + void UpdateColors (); + void ResetSDLRenderer (); + + CocoaFrameBuffer () {} +}; + +struct MiniModeInfo +{ + WORD Width, Height; +}; + + +EXTERN_CVAR (Float, Gamma) + +CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} + +static MiniModeInfo WinModes[] = +{ + { 320, 200 }, + { 320, 240 }, + { 400, 225 }, // 16:9 + { 400, 300 }, + { 480, 270 }, // 16:9 + { 480, 360 }, + { 512, 288 }, // 16:9 + { 512, 384 }, + { 640, 360 }, // 16:9 + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, // 16:10 + { 720, 540 }, + { 800, 450 }, // 16:9 + { 800, 480 }, + { 800, 500 }, // 16:10 + { 800, 600 }, + { 848, 480 }, // 16:9 + { 960, 600 }, // 16:10 + { 960, 720 }, + { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 + { 1024, 640 }, // 16:10 + { 1024, 768 }, + { 1088, 612 }, // 16:9 + { 1152, 648 }, // 16:9 + { 1152, 720 }, // 16:10 + { 1152, 864 }, + { 1280, 720 }, // 16:9 + { 1280, 854 }, + { 1280, 800 }, // 16:10 + { 1280, 960 }, + { 1280, 1024 }, // 5:4 + { 1360, 768 }, // 16:9 + { 1366, 768 }, + { 1400, 787 }, // 16:9 + { 1400, 875 }, // 16:10 + { 1400, 1050 }, + { 1440, 900 }, + { 1440, 960 }, + { 1440, 1080 }, + { 1600, 900 }, // 16:9 + { 1600, 1000 }, // 16:10 + { 1600, 1200 }, + { 1920, 1080 }, + { 1920, 1200 }, + { 2048, 1536 }, + { 2560, 1440 }, + { 2560, 1600 }, + { 2560, 2048 }, + { 2880, 1800 }, + { 3200, 1800 }, + { 3840, 2160 }, + { 3840, 2400 }, + { 4096, 2160 }, + { 5120, 2880 } +}; + +static cycle_t BlitCycles; +static cycle_t SDLFlipCycles; + +// CODE -------------------------------------------------------------------- + +void ScaleWithAspect (int &w, int &h, int Width, int Height) +{ + int resRatio = CheckRatio (Width, Height); + int screenRatio; + CheckRatio (w, h, &screenRatio); + if (resRatio == screenRatio) + return; + + double yratio; + switch(resRatio) + { + case 0: yratio = 4./3.; break; + case 1: yratio = 16./9.; break; + case 2: yratio = 16./10.; break; + case 3: yratio = 17./10.; break; + case 4: yratio = 5./4.; break; + default: return; + } + double y = w/yratio; + if (y > h) + w = h*yratio; + else + h = y; +} + +CocoaVideo::CocoaVideo (int parm) +{ + IteratorBits = 0; +} + +CocoaVideo::~CocoaVideo () +{ +} + +void CocoaVideo::StartModeIterator (int bits, bool fs) +{ + IteratorMode = 0; + IteratorBits = bits; +} + +bool CocoaVideo::NextMode (int *width, int *height, bool *letterbox) +{ + if (IteratorBits != 8) + return false; + + if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) + { + *width = WinModes[IteratorMode].Width; + *height = WinModes[IteratorMode].Height; + ++IteratorMode; + return true; + } + return false; +} + +DFrameBuffer *CocoaVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +{ + static int retry = 0; + static int owidth, oheight; + + PalEntry flashColor; + int flashAmount; + + if (old != NULL) + { // Reuse the old framebuffer if its attributes are the same + CocoaFrameBuffer *fb = static_cast (old); + if (fb->Width == width && + fb->Height == height) + { + bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; + + if (fsnow != fullscreen) + { + fb->SetFullscreen (fullscreen); + } + return old; + } + old->GetFlash (flashColor, flashAmount); + old->ObjectFlags |= OF_YesReallyDelete; + if (screen == old) screen = NULL; + delete old; + } + else + { + flashColor = 0; + flashAmount = 0; + } + + CocoaFrameBuffer *fb = new CocoaFrameBuffer (width, height, fullscreen); + retry = 0; + + // If we could not create the framebuffer, try again with slightly + // different parameters in this order: + // 1. Try with the closest size + // 2. Try in the opposite screen mode with the original size + // 3. Try in the opposite screen mode with the closest size + // This is a somewhat confusing mass of recursion here. + + while (fb == NULL || !fb->IsValid ()) + { + if (fb != NULL) + { + delete fb; + } + + switch (retry) + { + case 0: + owidth = width; + oheight = height; + case 2: + // Try a different resolution. Hopefully that will work. + I_ClosestResolution (&width, &height, 8); + break; + + case 1: + // Try changing fullscreen mode. Maybe that will work. + width = owidth; + height = oheight; + fullscreen = !fullscreen; + break; + + default: + // I give up! + I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); + } + + ++retry; + fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + } + + fb->SetFlash (flashColor, flashAmount); + + return fb; +} + +void CocoaVideo::SetWindowedScale (float scale) +{ +} + + +CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) +: DFrameBuffer (width, height) +{ + int i; + + NeedPalUpdate = false; + NeedGammaUpdate = false; + UpdatePending = false; + NotPaletted = false; + FlashAmount = 0; + + FString caption; + caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + + Screen = SDL_CreateWindow (caption, + SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), + width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); + + if (Screen == NULL) + return; + + Renderer = NULL; + Texture = NULL; + ResetSDLRenderer (); + + for (i = 0; i < 256; i++) + { + GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; + } + + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + UpdateColors (); + + SetVSync (vid_vsync); +} + + +CocoaFrameBuffer::~CocoaFrameBuffer () +{ + if(Screen) + { + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + + SDL_DestroyWindow (Screen); + } +} + +bool CocoaFrameBuffer::IsValid () +{ + return DFrameBuffer::IsValid() && Screen != NULL; +} + +int CocoaFrameBuffer::GetPageCount () +{ + return 1; +} + +bool CocoaFrameBuffer::Lock (bool buffered) +{ + return DSimpleCanvas::Lock (); +} + +bool CocoaFrameBuffer::Relock () +{ + return DSimpleCanvas::Lock (); +} + +void CocoaFrameBuffer::Unlock () +{ + if (UpdatePending && LockCount == 1) + { + Update (); + } + else if (--LockCount <= 0) + { + Buffer = NULL; + LockCount = 0; + } +} + +void CocoaFrameBuffer::Update () +{ + if (LockCount != 1) + { + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff (); + + Buffer = NULL; + LockCount = 0; + UpdatePending = false; + + BlitCycles.Reset(); + SDLFlipCycles.Reset(); + BlitCycles.Clock(); + + void *pixels; + int pitch; + + if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) + return; + + if (NotPaletted) + { + GPfx.Convert (MemBuffer, Pitch, + pixels, pitch, Width, Height, + FRACUNIT, FRACUNIT, 0, 0); + } + else + { + if (pitch == Pitch) + { + memcpy (pixels, MemBuffer, Width*Height); + } + else + { + for (int y = 0; y < Height; ++y) + { + memcpy ((BYTE *)pixels+y*pitch, MemBuffer+y*Pitch, Width); + } + } + } + + SDL_UnlockTexture (Texture); + + SDLFlipCycles.Clock(); + SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); + SDL_RenderPresent(Renderer); + SDLFlipCycles.Unclock(); + + BlitCycles.Unclock(); + + if (NeedGammaUpdate) + { + bool Windowed = false; + NeedGammaUpdate = false; + CalcGamma ((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); + CalcGamma ((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); + CalcGamma ((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); + NeedPalUpdate = true; + } + + if (NeedPalUpdate) + { + NeedPalUpdate = false; + UpdateColors (); + } +} + +void CocoaFrameBuffer::UpdateColors () +{ + if (NotPaletted) + { + PalEntry palette[256]; + + for (int i = 0; i < 256; ++i) + { + palette[i].r = GammaTable[0][SourcePalette[i].r]; + palette[i].g = GammaTable[1][SourcePalette[i].g]; + palette[i].b = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending (palette, palette, + 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], + FlashAmount); + } + GPfx.SetPalette (palette); + } + else + { + SDL_Color colors[256]; + + for (int i = 0; i < 256; ++i) + { + colors[i].r = GammaTable[0][SourcePalette[i].r]; + colors[i].g = GammaTable[1][SourcePalette[i].g]; + colors[i].b = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending ((PalEntry *)colors, (PalEntry *)colors, + 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], + FlashAmount); + } + SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256); + } +} + +PalEntry *CocoaFrameBuffer::GetPalette () +{ + return SourcePalette; +} + +void CocoaFrameBuffer::UpdatePalette () +{ + NeedPalUpdate = true; +} + +bool CocoaFrameBuffer::SetGamma (float gamma) +{ + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool CocoaFrameBuffer::SetFlash (PalEntry rgb, int amount) +{ + Flash = rgb; + FlashAmount = amount; + NeedPalUpdate = true; + return true; +} + +void CocoaFrameBuffer::GetFlash (PalEntry &rgb, int &amount) +{ + rgb = Flash; + amount = FlashAmount; +} + +// Q: Should I gamma adjust the returned palette? +void CocoaFrameBuffer::GetFlashedPalette (PalEntry pal[256]) +{ + memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); + } +} + +void CocoaFrameBuffer::SetFullscreen (bool fullscreen) +{ + SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + if (!fullscreen) + { + // Restore proper window size + SDL_SetWindowSize (Screen, Width, Height); + } + + ResetSDLRenderer (); +} + +bool CocoaFrameBuffer::IsFullscreen () +{ + return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +} + +void CocoaFrameBuffer::ResetSDLRenderer () +{ + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + + Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| + (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (!Renderer) + return; + + Uint32 fmt; + switch(vid_displaybits) + { + default: fmt = SDL_PIXELFORMAT_ARGB8888; break; + case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; + case 24: fmt = SDL_PIXELFORMAT_RGB888; break; + case 16: fmt = SDL_PIXELFORMAT_RGB565; break; + case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + } + Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); + + { + NotPaletted = true; + + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); + } + + // Calculate update rectangle + if (IsFullscreen ()) + { + int w, h; + SDL_GetWindowSize (Screen, &w, &h); + UpdateRect.w = w; + UpdateRect.h = h; + ScaleWithAspect (UpdateRect.w, UpdateRect.h, Width, Height); + UpdateRect.x = (w - UpdateRect.w)/2; + UpdateRect.y = (h - UpdateRect.h)/2; + } + else + { + // In windowed mode we just update the whole window. + UpdateRect.x = 0; + UpdateRect.y = 0; + UpdateRect.w = Width; + UpdateRect.h = Height; + } +} + +void CocoaFrameBuffer::SetVSync (bool vsync) +{ + if (CGLContextObj context = CGLGetCurrentContext()) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + // Inconsistency between 10.4 and 10.5 SDKs: + // third argument of CGLSetParameter() is const long* on 10.4 and const GLint* on 10.5 + // So, GLint typedef'ed to long instead of int to workaround this issue + typedef long GLint; +#endif // prior to 10.5 + + const GLint value = vsync ? 1 : 0; + CGLSetParameter(context, kCGLCPSwapInterval, &value); + } +} + +ADD_STAT (blit) +{ + FString out; + out.Format ("blit=%04.1f ms flip=%04.1f ms", + BlitCycles.TimeMS(), SDLFlipCycles.TimeMS()); + return out; +} + + +IVideo *Video; + + +void I_ShutdownGraphics () +{ + if (screen) + { + DFrameBuffer *s = screen; + screen = NULL; + s->ObjectFlags |= OF_YesReallyDelete; + delete s; + } + if (Video) + delete Video, Video = NULL; +} + +void I_InitGraphics () +{ + UCVarValue val; + + val.Bool = !!Args->CheckParm ("-devparm"); + ticker.SetGenericRepDefault (val, CVAR_Bool); + + Video = new CocoaVideo (0); + if (Video == NULL) + I_FatalError ("Failed to initialize display"); + + atterm (I_ShutdownGraphics); + + Video->SetWindowedScale (vid_winscale); +} + +static void I_DeleteRenderer() +{ + if (Renderer != NULL) delete Renderer; +} + +void I_CreateRenderer() +{ + if (Renderer == NULL) + { + Renderer = new FSoftwareRenderer; + atterm(I_DeleteRenderer); + } +} + + +DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) +{ + bool fs = false; + switch (Video->GetDisplayType ()) + { + case DISPLAY_WindowOnly: + fs = false; + break; + case DISPLAY_FullscreenOnly: + fs = true; + break; + case DISPLAY_Both: + fs = fullscreen; + break; + } + + return Video->CreateFrameBuffer (width, height, fs, old); +} + +bool I_CheckResolution (int width, int height, int bits) +{ + int twidth, theight; + + Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); + while (Video->NextMode (&twidth, &theight, NULL)) + { + if (width == twidth && height == theight) + return true; + } + return false; +} + +void I_ClosestResolution (int *width, int *height, int bits) +{ + int twidth, theight; + int cwidth = 0, cheight = 0; + int iteration; + DWORD closest = 4294967295u; + + for (iteration = 0; iteration < 2; iteration++) + { + Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); + while (Video->NextMode (&twidth, &theight, NULL)) + { + if (twidth == *width && theight == *height) + return; + + if (iteration == 0 && (twidth < *width || theight < *height)) + continue; + + DWORD dist = (twidth - *width) * (twidth - *width) + + (theight - *height) * (theight - *height); + + if (dist < closest) + { + closest = dist; + cwidth = twidth; + cheight = theight; + } + } + if (closest != 4294967295u) + { + *width = cwidth; + *height = cheight; + return; + } + } +} + + +EXTERN_CVAR(Int, vid_maxfps); +EXTERN_CVAR(Bool, cl_capfps); + +// So Apple doesn't support POSIX timers and I can't find a good substitute short of +// having Objective-C Cocoa events or something like that. +void I_SetFPSLimit(int limit) +{ +} + +CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (vid_maxfps < TICRATE && vid_maxfps != 0) + { + vid_maxfps = TICRATE; + } + else if (vid_maxfps > 1000) + { + vid_maxfps = 1000; + } + else if (cl_capfps == 0) + { + I_SetFPSLimit(vid_maxfps); + } +} + + +CCMD (vid_listmodes) +{ + static const char *ratios[5] = { "", " - 16:9", " - 16:10", "", " - 5:4" }; + int width, height, bits; + bool letterbox; + + if (Video == NULL) + { + return; + } + for (bits = 1; bits <= 32; bits++) + { + Video->StartModeIterator (bits, screen->IsFullscreen()); + while (Video->NextMode (&width, &height, &letterbox)) + { + bool thisMode = (width == DisplayWidth && height == DisplayHeight && bits == DisplayBits); + int ratio = CheckRatio (width, height); + Printf (thisMode ? PRINT_BOLD : PRINT_HIGH, + "%s%4d x%5d x%3d%s%s\n", + thisMode || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, + width, height, bits, + ratios[ratio], + thisMode || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); + } + } +} + +CCMD (vid_currentmode) +{ + Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); +} + + namespace { From 8bc890c99565732d8dfe707301ebaedfedbf6345 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 15 Dec 2014 17:40:30 +0200 Subject: [PATCH 011/117] Rearranged files to avoid usage of symbolic links --- src/CMakeLists.txt | 34 +- src/cocoa/dikeys.h | 1 - src/cocoa/hardware.h | 1 - src/cocoa/i_cd.cpp | 1 - src/cocoa/i_input.h | 1 - src/cocoa/i_movie.cpp | 1 - src/cocoa/i_system.cpp | 1 - src/cocoa/i_system.h | 1 - src/cocoa/st_start.cpp | 1 - src/{sdl => posix}/dikeys.h | 310 ++++++------ src/{sdl => posix}/hardware.h | 192 ++++---- src/{sdl => posix}/i_cd.cpp | 308 ++++++------ src/{sdl => posix}/i_input.h | 20 +- src/{sdl => posix}/i_movie.cpp | 14 +- src/{sdl => posix}/i_system.cpp | 0 src/{sdl => posix}/i_system.h | 332 ++++++------- src/{sdl => posix}/st_start.cpp | 708 +++++++++++++-------------- src/{cocoa => sdl}/i_system_cocoa.mm | 0 18 files changed, 958 insertions(+), 968 deletions(-) delete mode 120000 src/cocoa/dikeys.h delete mode 120000 src/cocoa/hardware.h delete mode 120000 src/cocoa/i_cd.cpp delete mode 120000 src/cocoa/i_input.h delete mode 120000 src/cocoa/i_movie.cpp delete mode 120000 src/cocoa/i_system.cpp delete mode 120000 src/cocoa/i_system.h delete mode 120000 src/cocoa/st_start.cpp rename src/{sdl => posix}/dikeys.h (96%) rename src/{sdl => posix}/hardware.h (97%) rename src/{sdl => posix}/i_cd.cpp (95%) rename src/{sdl => posix}/i_input.h (95%) rename src/{sdl => posix}/i_movie.cpp (92%) rename src/{sdl => posix}/i_system.cpp (100%) rename src/{sdl => posix}/i_system.h (96%) rename src/{sdl => posix}/st_start.cpp (96%) rename src/{cocoa => sdl}/i_system_cocoa.mm (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 67f7d405b..207094fb6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -545,19 +545,20 @@ set( PLAT_WIN32_SOURCES win32/i_system.cpp win32/st_start.cpp win32/win32video.cpp ) +set( PLAT_POSIX_SOURCES + posix/i_cd.cpp + posix/i_movie.cpp + posix/i_system.cpp + posix/st_start.cpp ) set( PLAT_SDL_SOURCES sdl/crashcatcher.c sdl/hardware.cpp - sdl/i_cd.cpp sdl/i_gui.cpp sdl/i_input.cpp sdl/i_joystick.cpp sdl/i_main.cpp - sdl/i_movie.cpp - sdl/i_system.cpp sdl/i_timer.cpp - sdl/sdlvideo.cpp - sdl/st_start.cpp ) + sdl/sdlvideo.cpp ) set( PLAT_MAC_SOURCES cocoa/iwadpicker_cocoa.mm ) set( PLAT_COCOA_SOURCES @@ -571,18 +572,14 @@ set( PLAT_COCOA_SOURCES cocoa/hid/ImmrHIDUtilAddOn.c cocoa/critsec.cpp cocoa/i_backend_cocoa.mm - cocoa/i_cd.cpp cocoa/i_joystick.cpp - cocoa/i_movie.cpp - cocoa/i_system.cpp cocoa/i_timer.cpp - cocoa/st_start.cpp cocoa/zdoom.icns ) if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -595,23 +592,23 @@ if( WIN32 ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) elseif( APPLE ) if( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES_DIR cocoa ) + set( SYSTEM_SOURCES_DIR posix cocoa ) set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) else( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES_DIR sdl ) + set( SYSTEM_SOURCES_DIR posix sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} cocoa/i_system_cocoa.mm ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_MAC_SOURCES} "${FMOD_LIBRARY}" ) + set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_MAC_SOURCES} "${FMOD_LIBRARY}" ) set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) else( WIN32 ) - set( SYSTEM_SOURCES_DIR sdl ) - set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) + set( SYSTEM_SOURCES_DIR posix sdl ) + set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( WIN32 ) @@ -672,12 +669,12 @@ if( WIN32 ) set( EXTRA_HEADER_DIRS win32/*.h ) elseif( APPLE ) if( OSX_COCOA_BACKEND ) - set( EXTRA_HEADER_DIRS cocoa/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h cocoa/*.h ) else( OSX_COCOA_BACKEND ) - set( EXTRA_HEADER_DIRS sdl/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h sdl/*.h ) endif( OSX_COCOA_BACKEND ) else( WIN32 ) - set( EXTRA_HEADER_DIRS sdl/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h sdl/*.h ) endif( WIN32 ) file( GLOB HEADER_FILES ${EXTRA_HEADER_DIRS} @@ -693,6 +690,7 @@ file( GLOB HEADER_FILES menu/*.h oplsynth/*.h oplsynth/dosbox/*.h + posix/*.h r_data/*.h resourcefiles/*.h sdl/*.h diff --git a/src/cocoa/dikeys.h b/src/cocoa/dikeys.h deleted file mode 120000 index 011f85acb..000000000 --- a/src/cocoa/dikeys.h +++ /dev/null @@ -1 +0,0 @@ -../sdl/dikeys.h \ No newline at end of file diff --git a/src/cocoa/hardware.h b/src/cocoa/hardware.h deleted file mode 120000 index 4261b9477..000000000 --- a/src/cocoa/hardware.h +++ /dev/null @@ -1 +0,0 @@ -../sdl/hardware.h \ No newline at end of file diff --git a/src/cocoa/i_cd.cpp b/src/cocoa/i_cd.cpp deleted file mode 120000 index 5573d7e1e..000000000 --- a/src/cocoa/i_cd.cpp +++ /dev/null @@ -1 +0,0 @@ -../sdl/i_cd.cpp \ No newline at end of file diff --git a/src/cocoa/i_input.h b/src/cocoa/i_input.h deleted file mode 120000 index 208b03106..000000000 --- a/src/cocoa/i_input.h +++ /dev/null @@ -1 +0,0 @@ -../sdl/i_input.h \ No newline at end of file diff --git a/src/cocoa/i_movie.cpp b/src/cocoa/i_movie.cpp deleted file mode 120000 index 04258f3e5..000000000 --- a/src/cocoa/i_movie.cpp +++ /dev/null @@ -1 +0,0 @@ -../sdl/i_movie.cpp \ No newline at end of file diff --git a/src/cocoa/i_system.cpp b/src/cocoa/i_system.cpp deleted file mode 120000 index b95185a13..000000000 --- a/src/cocoa/i_system.cpp +++ /dev/null @@ -1 +0,0 @@ -../sdl/i_system.cpp \ No newline at end of file diff --git a/src/cocoa/i_system.h b/src/cocoa/i_system.h deleted file mode 120000 index ced516311..000000000 --- a/src/cocoa/i_system.h +++ /dev/null @@ -1 +0,0 @@ -../sdl/i_system.h \ No newline at end of file diff --git a/src/cocoa/st_start.cpp b/src/cocoa/st_start.cpp deleted file mode 120000 index 38ce4fa1c..000000000 --- a/src/cocoa/st_start.cpp +++ /dev/null @@ -1 +0,0 @@ -../sdl/st_start.cpp \ No newline at end of file diff --git a/src/sdl/dikeys.h b/src/posix/dikeys.h similarity index 96% rename from src/sdl/dikeys.h rename to src/posix/dikeys.h index 8ce9e95bb..4541b0ffd 100644 --- a/src/sdl/dikeys.h +++ b/src/posix/dikeys.h @@ -1,155 +1,155 @@ -// ZDoom bases its keycodes on DirectInput's scan codes -// Why? Because it was Win32-only before porting to anything else, -// so this made sense. AFAIK, it's primarily used under Win32 now, -// so it still makes sense. -// -// Actually, these key codes may only be used for key bindings now, -// in which case they're not really necessary--if we tweaked c_bind.cpp. - -enum -{ - DIK_ESCAPE = 1, - DIK_1, - DIK_2, - DIK_3, - DIK_4, - DIK_5, - DIK_6, - DIK_7, - DIK_8, - DIK_9, - DIK_0, - DIK_MINUS, /* - on main keyboard */ - DIK_EQUALS, - DIK_BACK, /* backspace */ - DIK_TAB, - DIK_Q, - DIK_W, - DIK_E, - DIK_R, - DIK_T, - DIK_Y, - DIK_U, - DIK_I, - DIK_O, - DIK_P, - DIK_LBRACKET, - DIK_RBRACKET, - DIK_RETURN, /* Enter on main keyboard */ - DIK_LCONTROL, - DIK_A, - DIK_S, - DIK_D, - DIK_F, - DIK_G, - DIK_H, - DIK_J, - DIK_K, - DIK_L, - DIK_SEMICOLON, - DIK_APOSTROPHE, - DIK_GRAVE, /* accent grave */ - DIK_LSHIFT, - DIK_BACKSLASH, - DIK_Z, - DIK_X, - DIK_C, - DIK_V, - DIK_B, - DIK_N, - DIK_M, - DIK_COMMA, - DIK_PERIOD, /* . on main keyboard */ - DIK_SLASH, /* / on main keyboard */ - DIK_RSHIFT, - DIK_MULTIPLY, /* * on numeric keypad */ - DIK_LMENU, /* left Alt */ - DIK_SPACE, - DIK_CAPITAL, - DIK_F1, - DIK_F2, - DIK_F3, - DIK_F4, - DIK_F5, - DIK_F6, - DIK_F7, - DIK_F8, - DIK_F9, - DIK_F10, - DIK_NUMLOCK, - DIK_SCROLL, /* Scroll Lock */ - DIK_NUMPAD7, - DIK_NUMPAD8, - DIK_NUMPAD9, - DIK_SUBTRACT, /* - on numeric keypad */ - DIK_NUMPAD4, - DIK_NUMPAD5, - DIK_NUMPAD6, - DIK_ADD, /* + on numeric keypad */ - DIK_NUMPAD1, - DIK_NUMPAD2, - DIK_NUMPAD3, - DIK_NUMPAD0, - DIK_DECIMAL, /* . on numeric keypad */ - DIK_OEM_102 = 0x56, /* < > | on UK/Germany keyboards */ - DIK_F11, - DIK_F12, - DIK_F13 = 0x64, /* (NEC PC98) */ - DIK_F14, /* (NEC PC98) */ - DIK_F15, /* (NEC PC98) */ - DIK_KANA = 0x70, /* (Japanese keyboard) */ - DIK_ABNT_C1 = 0x73, /* / ? on Portugese (Brazilian) keyboards */ - DIK_CONVERT = 0x79, /* (Japanese keyboard) */ - DIK_NOCONVERT = 0x7B, /* (Japanese keyboard) */ - DIK_YEN = 0x7D, /* (Japanese keyboard) */ - DIK_ABNT_C2 = 0x7E, /* Numpad . on Portugese (Brazilian) keyboards */ - DIK_NUMPAD_EQUALS = 0x8D, /* = on numeric keypad (NEC PC98) */ - DIK_PREVTRACK = 0x90, /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */ - DIK_AT, /* (NEC PC98) */ - DIK_COLON, /* (NEC PC98) */ - DIK_UNDERLINE, /* (NEC PC98) */ - DIK_KANJI, /* (Japanese keyboard) */ - DIK_STOP, /* (NEC PC98) */ - DIK_AX, /* (Japan AX) */ - DIK_UNLABELED, /* (J3100) */ - DIK_NEXTTRACK = 0x99, /* Next Track */ - DIK_NUMPADENTER = 0x9C, /* Enter on numeric keypad */ - DIK_RCONTROL = 0x9D, - DIK_MUTE = 0xA0, /* Mute */ - DIK_CALCULATOR = 0xA1, /* Calculator */ - DIK_PLAYPAUSE = 0xA2, /* Play / Pause */ - DIK_MEDIASTOP = 0xA4, /* Media Stop */ - DIK_VOLUMEDOWN = 0xAE, /* Volume - */ - DIK_VOLUMEUP = 0xB0, /* Volume + */ - DIK_WEBHOME = 0xB2, /* Web home */ - DIK_NUMPADCOMMA = 0xB3, /* , on numeric keypad (NEC PC98) */ - DIK_DIVIDE = 0xB5, /* / on numeric keypad */ - DIK_SYSRQ = 0xB7, - DIK_RMENU = 0xB8, /* right Alt */ - DIK_PAUSE = 0xC5, /* Pause */ - DIK_HOME = 0xC7, /* Home on arrow keypad */ - DIK_UP = 0xC8, /* UpArrow on arrow keypad */ - DIK_PRIOR = 0xC9, /* PgUp on arrow keypad */ - DIK_LEFT = 0xCB, /* LeftArrow on arrow keypad */ - DIK_RIGHT = 0xCD, /* RightArrow on arrow keypad */ - DIK_END = 0xCF, /* End on arrow keypad */ - DIK_DOWN = 0xD0, /* DownArrow on arrow keypad */ - DIK_NEXT = 0xD1, /* PgDn on arrow keypad */ - DIK_INSERT = 0xD2, /* Insert on arrow keypad */ - DIK_DELETE = 0xD3, /* Delete on arrow keypad */ - DIK_LWIN = 0xDB, /* Left Windows key */ - DIK_RWIN = 0xDC, /* Right Windows key */ - DIK_APPS = 0xDD, /* AppMenu key */ - DIK_POWER = 0xDE, /* System Power */ - DIK_SLEEP = 0xDF, /* System Sleep */ - DIK_WAKE = 0xE3, /* System Wake */ - DIK_WEBSEARCH = 0xE5, /* Web Search */ - DIK_WEBFAVORITES = 0xE6, /* Web Favorites */ - DIK_WEBREFRESH = 0xE7, /* Web Refresh */ - DIK_WEBSTOP = 0xE8, /* Web Stop */ - DIK_WEBFORWARD = 0xE9, /* Web Forward */ - DIK_WEBBACK = 0xEA, /* Web Back */ - DIK_MYCOMPUTER = 0xEB, /* My Computer */ - DIK_MAIL = 0xEC, /* Mail */ - DIK_MEDIASELECT = 0xED /* Media Select */ -}; +// ZDoom bases its keycodes on DirectInput's scan codes +// Why? Because it was Win32-only before porting to anything else, +// so this made sense. AFAIK, it's primarily used under Win32 now, +// so it still makes sense. +// +// Actually, these key codes may only be used for key bindings now, +// in which case they're not really necessary--if we tweaked c_bind.cpp. + +enum +{ + DIK_ESCAPE = 1, + DIK_1, + DIK_2, + DIK_3, + DIK_4, + DIK_5, + DIK_6, + DIK_7, + DIK_8, + DIK_9, + DIK_0, + DIK_MINUS, /* - on main keyboard */ + DIK_EQUALS, + DIK_BACK, /* backspace */ + DIK_TAB, + DIK_Q, + DIK_W, + DIK_E, + DIK_R, + DIK_T, + DIK_Y, + DIK_U, + DIK_I, + DIK_O, + DIK_P, + DIK_LBRACKET, + DIK_RBRACKET, + DIK_RETURN, /* Enter on main keyboard */ + DIK_LCONTROL, + DIK_A, + DIK_S, + DIK_D, + DIK_F, + DIK_G, + DIK_H, + DIK_J, + DIK_K, + DIK_L, + DIK_SEMICOLON, + DIK_APOSTROPHE, + DIK_GRAVE, /* accent grave */ + DIK_LSHIFT, + DIK_BACKSLASH, + DIK_Z, + DIK_X, + DIK_C, + DIK_V, + DIK_B, + DIK_N, + DIK_M, + DIK_COMMA, + DIK_PERIOD, /* . on main keyboard */ + DIK_SLASH, /* / on main keyboard */ + DIK_RSHIFT, + DIK_MULTIPLY, /* * on numeric keypad */ + DIK_LMENU, /* left Alt */ + DIK_SPACE, + DIK_CAPITAL, + DIK_F1, + DIK_F2, + DIK_F3, + DIK_F4, + DIK_F5, + DIK_F6, + DIK_F7, + DIK_F8, + DIK_F9, + DIK_F10, + DIK_NUMLOCK, + DIK_SCROLL, /* Scroll Lock */ + DIK_NUMPAD7, + DIK_NUMPAD8, + DIK_NUMPAD9, + DIK_SUBTRACT, /* - on numeric keypad */ + DIK_NUMPAD4, + DIK_NUMPAD5, + DIK_NUMPAD6, + DIK_ADD, /* + on numeric keypad */ + DIK_NUMPAD1, + DIK_NUMPAD2, + DIK_NUMPAD3, + DIK_NUMPAD0, + DIK_DECIMAL, /* . on numeric keypad */ + DIK_OEM_102 = 0x56, /* < > | on UK/Germany keyboards */ + DIK_F11, + DIK_F12, + DIK_F13 = 0x64, /* (NEC PC98) */ + DIK_F14, /* (NEC PC98) */ + DIK_F15, /* (NEC PC98) */ + DIK_KANA = 0x70, /* (Japanese keyboard) */ + DIK_ABNT_C1 = 0x73, /* / ? on Portugese (Brazilian) keyboards */ + DIK_CONVERT = 0x79, /* (Japanese keyboard) */ + DIK_NOCONVERT = 0x7B, /* (Japanese keyboard) */ + DIK_YEN = 0x7D, /* (Japanese keyboard) */ + DIK_ABNT_C2 = 0x7E, /* Numpad . on Portugese (Brazilian) keyboards */ + DIK_NUMPAD_EQUALS = 0x8D, /* = on numeric keypad (NEC PC98) */ + DIK_PREVTRACK = 0x90, /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */ + DIK_AT, /* (NEC PC98) */ + DIK_COLON, /* (NEC PC98) */ + DIK_UNDERLINE, /* (NEC PC98) */ + DIK_KANJI, /* (Japanese keyboard) */ + DIK_STOP, /* (NEC PC98) */ + DIK_AX, /* (Japan AX) */ + DIK_UNLABELED, /* (J3100) */ + DIK_NEXTTRACK = 0x99, /* Next Track */ + DIK_NUMPADENTER = 0x9C, /* Enter on numeric keypad */ + DIK_RCONTROL = 0x9D, + DIK_MUTE = 0xA0, /* Mute */ + DIK_CALCULATOR = 0xA1, /* Calculator */ + DIK_PLAYPAUSE = 0xA2, /* Play / Pause */ + DIK_MEDIASTOP = 0xA4, /* Media Stop */ + DIK_VOLUMEDOWN = 0xAE, /* Volume - */ + DIK_VOLUMEUP = 0xB0, /* Volume + */ + DIK_WEBHOME = 0xB2, /* Web home */ + DIK_NUMPADCOMMA = 0xB3, /* , on numeric keypad (NEC PC98) */ + DIK_DIVIDE = 0xB5, /* / on numeric keypad */ + DIK_SYSRQ = 0xB7, + DIK_RMENU = 0xB8, /* right Alt */ + DIK_PAUSE = 0xC5, /* Pause */ + DIK_HOME = 0xC7, /* Home on arrow keypad */ + DIK_UP = 0xC8, /* UpArrow on arrow keypad */ + DIK_PRIOR = 0xC9, /* PgUp on arrow keypad */ + DIK_LEFT = 0xCB, /* LeftArrow on arrow keypad */ + DIK_RIGHT = 0xCD, /* RightArrow on arrow keypad */ + DIK_END = 0xCF, /* End on arrow keypad */ + DIK_DOWN = 0xD0, /* DownArrow on arrow keypad */ + DIK_NEXT = 0xD1, /* PgDn on arrow keypad */ + DIK_INSERT = 0xD2, /* Insert on arrow keypad */ + DIK_DELETE = 0xD3, /* Delete on arrow keypad */ + DIK_LWIN = 0xDB, /* Left Windows key */ + DIK_RWIN = 0xDC, /* Right Windows key */ + DIK_APPS = 0xDD, /* AppMenu key */ + DIK_POWER = 0xDE, /* System Power */ + DIK_SLEEP = 0xDF, /* System Sleep */ + DIK_WAKE = 0xE3, /* System Wake */ + DIK_WEBSEARCH = 0xE5, /* Web Search */ + DIK_WEBFAVORITES = 0xE6, /* Web Favorites */ + DIK_WEBREFRESH = 0xE7, /* Web Refresh */ + DIK_WEBSTOP = 0xE8, /* Web Stop */ + DIK_WEBFORWARD = 0xE9, /* Web Forward */ + DIK_WEBBACK = 0xEA, /* Web Back */ + DIK_MYCOMPUTER = 0xEB, /* My Computer */ + DIK_MAIL = 0xEC, /* Mail */ + DIK_MEDIASELECT = 0xED /* Media Select */ +}; diff --git a/src/sdl/hardware.h b/src/posix/hardware.h similarity index 97% rename from src/sdl/hardware.h rename to src/posix/hardware.h index 6544f1498..618941fe5 100644 --- a/src/sdl/hardware.h +++ b/src/posix/hardware.h @@ -1,96 +1,96 @@ -/* -** hardware.h -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#ifndef __HARDWARE_H__ -#define __HARDWARE_H__ - -#include "i_video.h" -#include "v_video.h" - -// Semaphores -#ifdef __APPLE__ -#include -#include -#include -typedef semaphore_t Semaphore; -#define SEMAPHORE_WAIT(sem) \ - while(semaphore_wait(sem) != KERN_SUCCESS){} -#define SEMAPHORE_SIGNAL(sem) \ - semaphore_signal(sem); -#define SEMAPHORE_INIT(sem, shared, value) \ - semaphore_create(mach_task_self(), &sem, shared, value); -#else -#include -typedef sem_t Semaphore; -#define SEMAPHORE_WAIT(sem) \ - do { \ - while(sem_wait(&sem) != 0); \ - int semValue; \ - sem_getvalue(&sem, &semValue); \ - if(semValue < 1) \ - break; \ - } while(true); -#define SEMAPHORE_SIGNAL(sem) \ - sem_post(&sem); -#define SEMAPHORE_INIT(sem, shared, value) \ - sem_init(&sem, shared, value); -#endif - -class IVideo -{ - public: - virtual ~IVideo () {} - - virtual EDisplayType GetDisplayType () = 0; - virtual void SetWindowedScale (float scale) = 0; - - virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; - - virtual void StartModeIterator (int bits, bool fs) = 0; - virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; - - virtual bool SetResolution (int width, int height, int bits); - - virtual void DumpAdapters(); -}; - -void I_InitGraphics (); -void I_ShutdownGraphics (); -void I_CreateRenderer(); - -extern Semaphore FPSLimitSemaphore; -void I_SetFPSLimit(int limit); - -extern IVideo *Video; - -#endif // __HARDWARE_H__ +/* +** hardware.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __HARDWARE_H__ +#define __HARDWARE_H__ + +#include "i_video.h" +#include "v_video.h" + +// Semaphores +#ifdef __APPLE__ +#include +#include +#include +typedef semaphore_t Semaphore; +#define SEMAPHORE_WAIT(sem) \ + while(semaphore_wait(sem) != KERN_SUCCESS){} +#define SEMAPHORE_SIGNAL(sem) \ + semaphore_signal(sem); +#define SEMAPHORE_INIT(sem, shared, value) \ + semaphore_create(mach_task_self(), &sem, shared, value); +#else +#include +typedef sem_t Semaphore; +#define SEMAPHORE_WAIT(sem) \ + do { \ + while(sem_wait(&sem) != 0); \ + int semValue; \ + sem_getvalue(&sem, &semValue); \ + if(semValue < 1) \ + break; \ + } while(true); +#define SEMAPHORE_SIGNAL(sem) \ + sem_post(&sem); +#define SEMAPHORE_INIT(sem, shared, value) \ + sem_init(&sem, shared, value); +#endif + +class IVideo +{ + public: + virtual ~IVideo () {} + + virtual EDisplayType GetDisplayType () = 0; + virtual void SetWindowedScale (float scale) = 0; + + virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; + + virtual void StartModeIterator (int bits, bool fs) = 0; + virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; + + virtual bool SetResolution (int width, int height, int bits); + + virtual void DumpAdapters(); +}; + +void I_InitGraphics (); +void I_ShutdownGraphics (); +void I_CreateRenderer(); + +extern Semaphore FPSLimitSemaphore; +void I_SetFPSLimit(int limit); + +extern IVideo *Video; + +#endif // __HARDWARE_H__ diff --git a/src/sdl/i_cd.cpp b/src/posix/i_cd.cpp similarity index 95% rename from src/sdl/i_cd.cpp rename to src/posix/i_cd.cpp index 83f1e8c89..96b59934c 100644 --- a/src/sdl/i_cd.cpp +++ b/src/posix/i_cd.cpp @@ -1,154 +1,154 @@ -#include "i_cd.h" - -//========================================================================== -// -// CD_Init -// -//========================================================================== - -bool CD_Init () -{ - return false; -} - -bool CD_Init (int device) -{ - return false; -} - -//========================================================================== -// -// CD_InitID -// -//========================================================================== - -bool CD_InitID (unsigned int id, int guess) -{ - return false; -} - -//========================================================================== -// -// CD_Close -// -//========================================================================== - -void CD_Close () -{ -} - -//========================================================================== -// -// CD_Eject -// -//========================================================================== - -void CD_Eject () -{ -} - -//========================================================================== -// -// CD_UnEject -// -//========================================================================== - -bool CD_UnEject () -{ - return false; -} - -//========================================================================== -// -// CD_Stop -// -//========================================================================== - -void CD_Stop () -{ -} - -//========================================================================== -// -// CD_Play -// -//========================================================================== - -bool CD_Play (int track, bool looping) -{ - return false; -} - -//========================================================================== -// -// CD_PlayNoWait -// -//========================================================================== - -void CD_PlayNoWait (int track, bool looping) -{ -} - -//========================================================================== -// -// CD_PlayCD -// -//========================================================================== - -bool CD_PlayCD (bool looping) -{ - return false; -} - -//========================================================================== -// -// CD_PlayCDNoWait -// -//========================================================================== - -void CD_PlayCDNoWait (bool looping) -{ -} - -//========================================================================== -// -// CD_Pause -// -//========================================================================== - -void CD_Pause () -{ -} - -//========================================================================== -// -// CD_Resume -// -//========================================================================== - -bool CD_Resume () -{ - return false; -} - -//========================================================================== -// -// CD_GetMode -// -//========================================================================== - -ECDModes CD_GetMode () -{ - return CDMode_Unknown; -} - -//========================================================================== -// -// CD_CheckTrack -// -//========================================================================== - -bool CD_CheckTrack (int track) -{ - return false; -} +#include "i_cd.h" + +//========================================================================== +// +// CD_Init +// +//========================================================================== + +bool CD_Init () +{ + return false; +} + +bool CD_Init (int device) +{ + return false; +} + +//========================================================================== +// +// CD_InitID +// +//========================================================================== + +bool CD_InitID (unsigned int id, int guess) +{ + return false; +} + +//========================================================================== +// +// CD_Close +// +//========================================================================== + +void CD_Close () +{ +} + +//========================================================================== +// +// CD_Eject +// +//========================================================================== + +void CD_Eject () +{ +} + +//========================================================================== +// +// CD_UnEject +// +//========================================================================== + +bool CD_UnEject () +{ + return false; +} + +//========================================================================== +// +// CD_Stop +// +//========================================================================== + +void CD_Stop () +{ +} + +//========================================================================== +// +// CD_Play +// +//========================================================================== + +bool CD_Play (int track, bool looping) +{ + return false; +} + +//========================================================================== +// +// CD_PlayNoWait +// +//========================================================================== + +void CD_PlayNoWait (int track, bool looping) +{ +} + +//========================================================================== +// +// CD_PlayCD +// +//========================================================================== + +bool CD_PlayCD (bool looping) +{ + return false; +} + +//========================================================================== +// +// CD_PlayCDNoWait +// +//========================================================================== + +void CD_PlayCDNoWait (bool looping) +{ +} + +//========================================================================== +// +// CD_Pause +// +//========================================================================== + +void CD_Pause () +{ +} + +//========================================================================== +// +// CD_Resume +// +//========================================================================== + +bool CD_Resume () +{ + return false; +} + +//========================================================================== +// +// CD_GetMode +// +//========================================================================== + +ECDModes CD_GetMode () +{ + return CDMode_Unknown; +} + +//========================================================================== +// +// CD_CheckTrack +// +//========================================================================== + +bool CD_CheckTrack (int track) +{ + return false; +} diff --git a/src/sdl/i_input.h b/src/posix/i_input.h similarity index 95% rename from src/sdl/i_input.h rename to src/posix/i_input.h index 124c2ca85..07ee1115d 100644 --- a/src/sdl/i_input.h +++ b/src/posix/i_input.h @@ -1,10 +1,10 @@ -#ifndef __I_INPUT_H__ -#define __I_INPUT_H__ - -void I_PutInClipboard (const char *str); -FString I_GetFromClipboard (bool use_primary_selection); -void I_SetMouseCapture(); -void I_ReleaseMouseCapture(); - -#endif - +#ifndef __I_INPUT_H__ +#define __I_INPUT_H__ + +void I_PutInClipboard (const char *str); +FString I_GetFromClipboard (bool use_primary_selection); +void I_SetMouseCapture(); +void I_ReleaseMouseCapture(); + +#endif + diff --git a/src/sdl/i_movie.cpp b/src/posix/i_movie.cpp similarity index 92% rename from src/sdl/i_movie.cpp rename to src/posix/i_movie.cpp index 020541516..8aea4b500 100644 --- a/src/sdl/i_movie.cpp +++ b/src/posix/i_movie.cpp @@ -1,7 +1,7 @@ -#include "i_movie.h" - -int I_PlayMovie (const char *movie) -{ - return MOVIE_Failed; -} - +#include "i_movie.h" + +int I_PlayMovie (const char *movie) +{ + return MOVIE_Failed; +} + diff --git a/src/sdl/i_system.cpp b/src/posix/i_system.cpp similarity index 100% rename from src/sdl/i_system.cpp rename to src/posix/i_system.cpp diff --git a/src/sdl/i_system.h b/src/posix/i_system.h similarity index 96% rename from src/sdl/i_system.h rename to src/posix/i_system.h index a3341f4c5..f0d399930 100644 --- a/src/sdl/i_system.h +++ b/src/posix/i_system.h @@ -1,166 +1,166 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// System specific interface stuff. -// -//----------------------------------------------------------------------------- - - -#ifndef __I_SYSTEM__ -#define __I_SYSTEM__ - -#include -#include - -#include "doomtype.h" - -struct ticcmd_t; -struct WadStuff; - -#ifndef SHARE_DIR -#define SHARE_DIR "/usr/local/share/" -#endif - -// Index values into the LanguageIDs array -enum -{ - LANGIDX_UserPreferred, - LANGIDX_UserDefault, - LANGIDX_SysPreferred, - LANGIDX_SysDefault -}; -extern DWORD LanguageIDs[4]; -extern void SetLanguageIDs (); - -// Called by DoomMain. -void I_Init (void); - -// Called by D_DoomLoop, -// returns current time in tics. -extern int (*I_GetTime) (bool saveMS); - -// like I_GetTime, except it waits for a new tic before returning -extern int (*I_WaitForTic) (int); - -// Freezes tic counting temporarily. While frozen, calls to I_GetTime() -// will always return the same value. This does not affect I_MSTime(). -// You must also not call I_WaitForTic() while freezing time, since the -// tic will never arrive (unless it's the current one). -extern void (*I_FreezeTime) (bool frozen); - -fixed_t I_GetTimeFrac (uint32 *ms); - -// Return a seed value for the RNG. -unsigned int I_MakeRNGSeed(); - - -// -// Called by D_DoomLoop, -// called before processing any tics in a frame -// (just after displaying a frame). -// Time consuming syncronous operations -// are performed here (joystick reading). -// Can call D_PostEvent. -// -void I_StartFrame (void); - - -// -// Called by D_DoomLoop, -// called before processing each tic in a frame. -// Quick syncronous operations are performed here. -// Can call D_PostEvent. -void I_StartTic (void); - -// Asynchronous interrupt functions should maintain private queues -// that are read by the synchronous functions -// to be converted into events. - -// Either returns a null ticcmd, -// or calls a loadable driver to build it. -// This ticcmd will then be modified by the gameloop -// for normal input. -ticcmd_t *I_BaseTiccmd (void); - - -// Called by M_Responder when quit is selected. -// Clean exit, displays sell blurb. -void I_Quit (void); - - -void I_Tactile (int on, int off, int total); - -void STACK_ARGS I_Error (const char *error, ...) GCCPRINTF(1,2); -void STACK_ARGS I_FatalError (const char *error, ...) GCCPRINTF(1,2); - -void addterm (void (*func)(void), const char *name); -#define atterm(t) addterm (t, #t) -void popterm (); - -// Print a console string -void I_PrintStr (const char *str); - -// Set the title string of the startup window -void I_SetIWADInfo (); - -// Pick from multiple IWADs to use -int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); - -// The ini could not be saved at exit -bool I_WriteIniFailed (); - -// [RH] Returns millisecond-accurate time -unsigned int I_MSTime (void); -unsigned int I_FPSTime(); - -class FTexture; -bool I_SetCursor(FTexture *); - -// Directory searching routines - -struct findstate_t -{ - int count; - struct dirent **namelist; - int current; -}; - -void *I_FindFirst (const char *filespec, findstate_t *fileinfo); -int I_FindNext (void *handle, findstate_t *fileinfo); -int I_FindClose (void *handle); -int I_FindAttr (findstate_t *fileinfo); - -#define I_FindName(a) ((a)->namelist[(a)->current]->d_name) - -#define FA_RDONLY 1 -#define FA_HIDDEN 2 -#define FA_SYSTEM 4 -#define FA_DIREC 8 -#define FA_ARCH 16 - -static inline char *strlwr(char *str) -{ - char *ptr = str; - while(*ptr) - { - *ptr = tolower(*ptr); - ++ptr; - } - return str; -} - -#endif +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +#include +#include + +#include "doomtype.h" + +struct ticcmd_t; +struct WadStuff; + +#ifndef SHARE_DIR +#define SHARE_DIR "/usr/local/share/" +#endif + +// Index values into the LanguageIDs array +enum +{ + LANGIDX_UserPreferred, + LANGIDX_UserDefault, + LANGIDX_SysPreferred, + LANGIDX_SysDefault +}; +extern DWORD LanguageIDs[4]; +extern void SetLanguageIDs (); + +// Called by DoomMain. +void I_Init (void); + +// Called by D_DoomLoop, +// returns current time in tics. +extern int (*I_GetTime) (bool saveMS); + +// like I_GetTime, except it waits for a new tic before returning +extern int (*I_WaitForTic) (int); + +// Freezes tic counting temporarily. While frozen, calls to I_GetTime() +// will always return the same value. This does not affect I_MSTime(). +// You must also not call I_WaitForTic() while freezing time, since the +// tic will never arrive (unless it's the current one). +extern void (*I_FreezeTime) (bool frozen); + +fixed_t I_GetTimeFrac (uint32 *ms); + +// Return a seed value for the RNG. +unsigned int I_MakeRNGSeed(); + + +// +// Called by D_DoomLoop, +// called before processing any tics in a frame +// (just after displaying a frame). +// Time consuming syncronous operations +// are performed here (joystick reading). +// Can call D_PostEvent. +// +void I_StartFrame (void); + + +// +// Called by D_DoomLoop, +// called before processing each tic in a frame. +// Quick syncronous operations are performed here. +// Can call D_PostEvent. +void I_StartTic (void); + +// Asynchronous interrupt functions should maintain private queues +// that are read by the synchronous functions +// to be converted into events. + +// Either returns a null ticcmd, +// or calls a loadable driver to build it. +// This ticcmd will then be modified by the gameloop +// for normal input. +ticcmd_t *I_BaseTiccmd (void); + + +// Called by M_Responder when quit is selected. +// Clean exit, displays sell blurb. +void I_Quit (void); + + +void I_Tactile (int on, int off, int total); + +void STACK_ARGS I_Error (const char *error, ...) GCCPRINTF(1,2); +void STACK_ARGS I_FatalError (const char *error, ...) GCCPRINTF(1,2); + +void addterm (void (*func)(void), const char *name); +#define atterm(t) addterm (t, #t) +void popterm (); + +// Print a console string +void I_PrintStr (const char *str); + +// Set the title string of the startup window +void I_SetIWADInfo (); + +// Pick from multiple IWADs to use +int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); + +// The ini could not be saved at exit +bool I_WriteIniFailed (); + +// [RH] Returns millisecond-accurate time +unsigned int I_MSTime (void); +unsigned int I_FPSTime(); + +class FTexture; +bool I_SetCursor(FTexture *); + +// Directory searching routines + +struct findstate_t +{ + int count; + struct dirent **namelist; + int current; +}; + +void *I_FindFirst (const char *filespec, findstate_t *fileinfo); +int I_FindNext (void *handle, findstate_t *fileinfo); +int I_FindClose (void *handle); +int I_FindAttr (findstate_t *fileinfo); + +#define I_FindName(a) ((a)->namelist[(a)->current]->d_name) + +#define FA_RDONLY 1 +#define FA_HIDDEN 2 +#define FA_SYSTEM 4 +#define FA_DIREC 8 +#define FA_ARCH 16 + +static inline char *strlwr(char *str) +{ + char *ptr = str; + while(*ptr) + { + *ptr = tolower(*ptr); + ++ptr; + } + return str; +} + +#endif diff --git a/src/sdl/st_start.cpp b/src/posix/st_start.cpp similarity index 96% rename from src/sdl/st_start.cpp rename to src/posix/st_start.cpp index 33a5abe0b..060548b53 100644 --- a/src/sdl/st_start.cpp +++ b/src/posix/st_start.cpp @@ -1,354 +1,354 @@ -/* -** st_start.cpp -** Handles the startup screen. -** -**--------------------------------------------------------------------------- -** Copyright 2006-2007 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -// HEADER FILES ------------------------------------------------------------ - -#include -#include -#include -#include - -#include "st_start.h" -#include "doomdef.h" -#include "i_system.h" -#include "c_cvars.h" - -// MACROS ------------------------------------------------------------------ - -// TYPES ------------------------------------------------------------------- - -class FTTYStartupScreen : public FStartupScreen -{ - public: - FTTYStartupScreen(int max_progress); - ~FTTYStartupScreen(); - - void Progress(); - void NetInit(const char *message, int num_players); - void NetProgress(int count); - void NetMessage(const char *format, ...); // cover for printf - void NetDone(); - bool NetLoop(bool (*timer_callback)(void *), void *userdata); - protected: - bool DidNetInit; - int NetMaxPos, NetCurPos; - const char *TheNetMessage; - termios OldTermIOS; -}; - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -void I_ShutdownJoysticks(); - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void DeleteStartupScreen(); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -FStartupScreen *StartScreen; - -CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 0) self = 0; - else if (self > 2) self=2; -} - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' }; - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// FStartupScreen :: CreateInstance -// -// Initializes the startup screen for the detected game. -// Sets the size of the progress bar and displays the startup screen. -// -//========================================================================== - -FStartupScreen *FStartupScreen::CreateInstance(int max_progress) -{ - atterm(DeleteStartupScreen); - return new FTTYStartupScreen(max_progress); -} - -//=========================================================================== -// -// DeleteStartupScreen -// -// Makes sure the startup screen has been deleted before quitting. -// -//=========================================================================== - -void DeleteStartupScreen() -{ - if (StartScreen != NULL) - { - delete StartScreen; - StartScreen = NULL; - } -} - -//=========================================================================== -// -// FTTYStartupScreen Constructor -// -// Sets the size of the progress bar and displays the startup screen. -// -//=========================================================================== - -FTTYStartupScreen::FTTYStartupScreen(int max_progress) - : FStartupScreen(max_progress) -{ - DidNetInit = false; - NetMaxPos = 0; - NetCurPos = 0; - TheNetMessage = NULL; -} - -//=========================================================================== -// -// FTTYStartupScreen Destructor -// -// Called just before entering graphics mode to deconstruct the startup -// screen. -// -//=========================================================================== - -FTTYStartupScreen::~FTTYStartupScreen() -{ - NetDone(); // Just in case it wasn't called yet and needs to be. -} - -//=========================================================================== -// -// FTTYStartupScreen :: Progress -// -// If there was a progress bar, this would move it. But the basic TTY -// startup screen doesn't have one, so this function does nothing. -// -//=========================================================================== - -void FTTYStartupScreen::Progress() -{ -} - -//=========================================================================== -// -// FTTYStartupScreen :: NetInit -// -// Sets stdin for unbuffered I/O, displays the given message, and shows -// a progress meter. -// -//=========================================================================== - -void FTTYStartupScreen::NetInit(const char *message, int numplayers) -{ - if (!DidNetInit) - { - termios rawtermios; - - fprintf (stderr, "Press 'Q' to abort network game synchronization."); - // Set stdin to raw mode so we can get keypresses in ST_CheckNetAbort() - // immediately without waiting for an EOL. - tcgetattr (STDIN_FILENO, &OldTermIOS); - rawtermios = OldTermIOS; - rawtermios.c_lflag &= ~(ICANON | ECHO); - tcsetattr (STDIN_FILENO, TCSANOW, &rawtermios); - DidNetInit = true; - } - if (numplayers == 1) - { - // Status message without any real progress info. - fprintf (stderr, "\n%s.", message); - } - else - { - fprintf (stderr, "\n%s: ", message); - } - fflush (stderr); - TheNetMessage = message; - NetMaxPos = numplayers; - NetCurPos = 0; - NetProgress(1); // You always know about yourself -} - -//=========================================================================== -// -// FTTYStartupScreen :: NetDone -// -// Restores the old stdin tty settings. -// -//=========================================================================== - -void FTTYStartupScreen::NetDone() -{ - // Restore stdin settings - if (DidNetInit) - { - tcsetattr (STDIN_FILENO, TCSANOW, &OldTermIOS); - printf ("\n"); - DidNetInit = false; - } -} - -//=========================================================================== -// -// FTTYStartupScreen :: NetMessage -// -// Call this between NetInit() and NetDone() instead of Printf() to -// display messages, because the progress meter is mixed in the same output -// stream as normal messages. -// -//=========================================================================== - -void FTTYStartupScreen::NetMessage(const char *format, ...) -{ - FString str; - va_list argptr; - - va_start (argptr, format); - str.VFormat (format, argptr); - va_end (argptr); - fprintf (stderr, "\r%-40s\n", str.GetChars()); -} - -//=========================================================================== -// -// FTTYStartupScreen :: NetProgress -// -// Sets the network progress meter. If count is 0, it gets bumped by 1. -// Otherwise, it is set to count. -// -//=========================================================================== - -void FTTYStartupScreen::NetProgress(int count) -{ - int i; - - if (count == 0) - { - NetCurPos++; - } - else if (count > 0) - { - NetCurPos = count; - } - if (NetMaxPos == 0) - { - // Spinny-type progress meter, because we're a guest waiting for the host. - fprintf (stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]); - fflush (stderr); - } - else if (NetMaxPos > 1) - { - // Dotty-type progress meter. - fprintf (stderr, "\r%s: ", TheNetMessage); - for (i = 0; i < NetCurPos; ++i) - { - fputc ('.', stderr); - } - fprintf (stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos); - fflush (stderr); - } -} - -//=========================================================================== -// -// FTTYStartupScreen :: NetLoop -// -// The timer_callback function is called at least two times per second -// and passed the userdata value. It should return true to stop the loop and -// return control to the caller or false to continue the loop. -// -// ST_NetLoop will return true if the loop was halted by the callback and -// false if the loop was halted because the user wants to abort the -// network synchronization. -// -//=========================================================================== - -bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) -{ - fd_set rfds; - struct timeval tv; - int retval; - char k; - - for (;;) - { - // Don't flood the network with packets on startup. - tv.tv_sec = 0; - tv.tv_usec = 500000; - - FD_ZERO (&rfds); - FD_SET (STDIN_FILENO, &rfds); - - retval = select (1, &rfds, NULL, NULL, &tv); - - if (retval == -1) - { - // Error - } - else if (retval == 0) - { - if (timer_callback (userdata)) - { - fputc ('\n', stderr); - return true; - } - } - else if (read (STDIN_FILENO, &k, 1) == 1) - { - // Check input on stdin - if (k == 'q' || k == 'Q') - { - fprintf (stderr, "\nNetwork game synchronization aborted."); - return false; - } - } - } -} - -void ST_Endoom() -{ - I_ShutdownJoysticks(); - exit(0); -} +/* +** st_start.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include + +#include "st_start.h" +#include "doomdef.h" +#include "i_system.h" +#include "c_cvars.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +class FTTYStartupScreen : public FStartupScreen +{ + public: + FTTYStartupScreen(int max_progress); + ~FTTYStartupScreen(); + + void Progress(); + void NetInit(const char *message, int num_players); + void NetProgress(int count); + void NetMessage(const char *format, ...); // cover for printf + void NetDone(); + bool NetLoop(bool (*timer_callback)(void *), void *userdata); + protected: + bool DidNetInit; + int NetMaxPos, NetCurPos; + const char *TheNetMessage; + termios OldTermIOS; +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void I_ShutdownJoysticks(); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void DeleteStartupScreen(); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +FStartupScreen *StartScreen; + +CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 0) self = 0; + else if (self > 2) self=2; +} + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' }; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// FStartupScreen :: CreateInstance +// +// Initializes the startup screen for the detected game. +// Sets the size of the progress bar and displays the startup screen. +// +//========================================================================== + +FStartupScreen *FStartupScreen::CreateInstance(int max_progress) +{ + atterm(DeleteStartupScreen); + return new FTTYStartupScreen(max_progress); +} + +//=========================================================================== +// +// DeleteStartupScreen +// +// Makes sure the startup screen has been deleted before quitting. +// +//=========================================================================== + +void DeleteStartupScreen() +{ + if (StartScreen != NULL) + { + delete StartScreen; + StartScreen = NULL; + } +} + +//=========================================================================== +// +// FTTYStartupScreen Constructor +// +// Sets the size of the progress bar and displays the startup screen. +// +//=========================================================================== + +FTTYStartupScreen::FTTYStartupScreen(int max_progress) + : FStartupScreen(max_progress) +{ + DidNetInit = false; + NetMaxPos = 0; + NetCurPos = 0; + TheNetMessage = NULL; +} + +//=========================================================================== +// +// FTTYStartupScreen Destructor +// +// Called just before entering graphics mode to deconstruct the startup +// screen. +// +//=========================================================================== + +FTTYStartupScreen::~FTTYStartupScreen() +{ + NetDone(); // Just in case it wasn't called yet and needs to be. +} + +//=========================================================================== +// +// FTTYStartupScreen :: Progress +// +// If there was a progress bar, this would move it. But the basic TTY +// startup screen doesn't have one, so this function does nothing. +// +//=========================================================================== + +void FTTYStartupScreen::Progress() +{ +} + +//=========================================================================== +// +// FTTYStartupScreen :: NetInit +// +// Sets stdin for unbuffered I/O, displays the given message, and shows +// a progress meter. +// +//=========================================================================== + +void FTTYStartupScreen::NetInit(const char *message, int numplayers) +{ + if (!DidNetInit) + { + termios rawtermios; + + fprintf (stderr, "Press 'Q' to abort network game synchronization."); + // Set stdin to raw mode so we can get keypresses in ST_CheckNetAbort() + // immediately without waiting for an EOL. + tcgetattr (STDIN_FILENO, &OldTermIOS); + rawtermios = OldTermIOS; + rawtermios.c_lflag &= ~(ICANON | ECHO); + tcsetattr (STDIN_FILENO, TCSANOW, &rawtermios); + DidNetInit = true; + } + if (numplayers == 1) + { + // Status message without any real progress info. + fprintf (stderr, "\n%s.", message); + } + else + { + fprintf (stderr, "\n%s: ", message); + } + fflush (stderr); + TheNetMessage = message; + NetMaxPos = numplayers; + NetCurPos = 0; + NetProgress(1); // You always know about yourself +} + +//=========================================================================== +// +// FTTYStartupScreen :: NetDone +// +// Restores the old stdin tty settings. +// +//=========================================================================== + +void FTTYStartupScreen::NetDone() +{ + // Restore stdin settings + if (DidNetInit) + { + tcsetattr (STDIN_FILENO, TCSANOW, &OldTermIOS); + printf ("\n"); + DidNetInit = false; + } +} + +//=========================================================================== +// +// FTTYStartupScreen :: NetMessage +// +// Call this between NetInit() and NetDone() instead of Printf() to +// display messages, because the progress meter is mixed in the same output +// stream as normal messages. +// +//=========================================================================== + +void FTTYStartupScreen::NetMessage(const char *format, ...) +{ + FString str; + va_list argptr; + + va_start (argptr, format); + str.VFormat (format, argptr); + va_end (argptr); + fprintf (stderr, "\r%-40s\n", str.GetChars()); +} + +//=========================================================================== +// +// FTTYStartupScreen :: NetProgress +// +// Sets the network progress meter. If count is 0, it gets bumped by 1. +// Otherwise, it is set to count. +// +//=========================================================================== + +void FTTYStartupScreen::NetProgress(int count) +{ + int i; + + if (count == 0) + { + NetCurPos++; + } + else if (count > 0) + { + NetCurPos = count; + } + if (NetMaxPos == 0) + { + // Spinny-type progress meter, because we're a guest waiting for the host. + fprintf (stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]); + fflush (stderr); + } + else if (NetMaxPos > 1) + { + // Dotty-type progress meter. + fprintf (stderr, "\r%s: ", TheNetMessage); + for (i = 0; i < NetCurPos; ++i) + { + fputc ('.', stderr); + } + fprintf (stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos); + fflush (stderr); + } +} + +//=========================================================================== +// +// FTTYStartupScreen :: NetLoop +// +// The timer_callback function is called at least two times per second +// and passed the userdata value. It should return true to stop the loop and +// return control to the caller or false to continue the loop. +// +// ST_NetLoop will return true if the loop was halted by the callback and +// false if the loop was halted because the user wants to abort the +// network synchronization. +// +//=========================================================================== + +bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) +{ + fd_set rfds; + struct timeval tv; + int retval; + char k; + + for (;;) + { + // Don't flood the network with packets on startup. + tv.tv_sec = 0; + tv.tv_usec = 500000; + + FD_ZERO (&rfds); + FD_SET (STDIN_FILENO, &rfds); + + retval = select (1, &rfds, NULL, NULL, &tv); + + if (retval == -1) + { + // Error + } + else if (retval == 0) + { + if (timer_callback (userdata)) + { + fputc ('\n', stderr); + return true; + } + } + else if (read (STDIN_FILENO, &k, 1) == 1) + { + // Check input on stdin + if (k == 'q' || k == 'Q') + { + fprintf (stderr, "\nNetwork game synchronization aborted."); + return false; + } + } + } +} + +void ST_Endoom() +{ + I_ShutdownJoysticks(); + exit(0); +} diff --git a/src/cocoa/i_system_cocoa.mm b/src/sdl/i_system_cocoa.mm similarity index 100% rename from src/cocoa/i_system_cocoa.mm rename to src/sdl/i_system_cocoa.mm From b9d83e88c908518b2104e14db353a38748a7eba2 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 16 Dec 2014 22:46:24 +0200 Subject: [PATCH 012/117] Added missing #include --- src/posix/i_steam.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/posix/i_steam.cpp b/src/posix/i_steam.cpp index be0be227b..9819bc09e 100644 --- a/src/posix/i_steam.cpp +++ b/src/posix/i_steam.cpp @@ -34,6 +34,10 @@ #include +#ifdef __APPLE__ +#include +#endif // __APPLE__ + #include "doomerrors.h" #include "d_main.h" #include "zstring.h" From 7d231c30089c3549ff58376c63b8325287199078 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 16 Dec 2014 22:56:09 +0200 Subject: [PATCH 013/117] Removed unnecessary SDL #include's --- src/sound/i_music.cpp | 1 - src/sound/i_musicinterns.h | 1 - src/tempfiles.h | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 93802c8e8..84d1d8101 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -37,7 +37,6 @@ #include #include #else -#include #include #include #include diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index fa04cfa51..2133e07c6 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -13,7 +13,6 @@ #include #include #else -#include #define FALSE 0 #define TRUE 1 #endif diff --git a/src/tempfiles.h b/src/tempfiles.h index beeb2bc1d..406c54153 100644 --- a/src/tempfiles.h +++ b/src/tempfiles.h @@ -38,6 +38,8 @@ #pragma once #endif +#include + // Returns a file name suitable for use as a temp file. // If you create a file with this name (and presumably you // will), it will be deleted automatically by this class's From 9f01384bc54aacaff4950f15a8f42219c2b3e8d5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 16 Dec 2014 22:58:39 +0200 Subject: [PATCH 014/117] No more SDL dependency in native OS X backend --- src/cocoa/i_backend_cocoa.mm | 337 ++++++++++++++++++++--------------- 1 file changed, 192 insertions(+), 145 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 442a7192a..571c3847b 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -36,7 +36,6 @@ #include #include -#include #include #include @@ -45,8 +44,6 @@ #include #include -#include - // Avoid collision between DObject class and Objective-C #define Class ObjectClass @@ -361,7 +358,7 @@ bool s_restartedFromWADPicker; bool s_nativeMouse = true; - + // TODO: remove this magic! size_t s_skipMouseMoves; @@ -376,8 +373,8 @@ void NewFailure() int OriginalMain(int argc, char** argv) { - printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n", - GetVersionString(), GetGitTime(), __DATE__); + printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n", + GetVersionString(), GetGitTime(), __DATE__); seteuid(getuid()); std::set_new_handler(NewFailure); @@ -388,24 +385,12 @@ int OriginalMain(int argc, char** argv) setenv("LC_NUMERIC", "C", 1); setlocale(LC_ALL, "C"); - if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE|SDL_INIT_JOYSTICK) == -1) - { - fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError()); - return -1; - } - atterm(SDL_Quit); - - printf("\n"); - - SDL_DisplayMode videoInfo = {}; - - if (0 == SDL_GetDesktopDisplayMode(vid_adapter, &videoInfo)) - { - vid_defwidth = videoInfo.w; - vid_defheight = videoInfo.h; - vid_vsync = true; - fullscreen = true; - } + // Set reasonable default values for video settings + const NSSize screenSize = [[NSScreen mainScreen] frame].size; + vid_defwidth = static_cast(screenSize.width); + vid_defheight = static_cast(screenSize.height); + vid_vsync = true; + fullscreen = true; try { @@ -458,7 +443,7 @@ int OriginalMain(int argc, char** argv) fprintf(stderr, "%s\n", message); Mac_I_FatalError(message); } - + exit(-1); } catch(...) @@ -466,7 +451,7 @@ int OriginalMain(int argc, char** argv) call_terms(); throw; } - + return 0; } @@ -479,7 +464,7 @@ void CheckGUICapture() const bool wantCapture = (MENU_Off == menuactive) ? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon) : (menuactive == MENU_On || menuactive == MENU_OnNoPause); - + if (wantCapture != GUICapture) { GUICapture = wantCapture; @@ -499,9 +484,9 @@ void CenterCursor() const NSRect displayRect = [[window screen] frame]; const NSRect windowRect = [window frame]; const CGPoint centerPoint = CGPointMake(NSMidX(windowRect), displayRect.size.height - NSMidY(windowRect)); - + CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); - + if (NULL != eventSource) { CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource, @@ -512,10 +497,10 @@ void CenterCursor() CGEventPost(kCGHIDEventTap, mouseMoveEvent); CFRelease(mouseMoveEvent); } - + CFRelease(eventSource); } - + // TODO: remove this magic! s_skipMouseMoves = 2; } @@ -528,10 +513,10 @@ bool IsInGame() default: case 0: return gamestate == GS_LEVEL; - + case 1: return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; - + case 2: return true; } @@ -547,7 +532,7 @@ void SetNativeMouse(bool wantNative) { CenterCursor(); } - + CGAssociateMouseAndMouseCursorPosition(wantNative); if (wantNative) @@ -565,7 +550,7 @@ void CheckNativeMouse() { bool windowed = (NULL == screen) || !screen->IsFullscreen(); bool wantNative; - + if (windowed) { if (![NSApp isActive] || !use_mouse) @@ -588,7 +573,7 @@ void CheckNativeMouse() wantNative = m_use_mouse && (MENU_On == menuactive || MENU_OnNoPause == menuactive); } - + SetNativeMouse(wantNative); } @@ -608,7 +593,7 @@ void I_StartTic() { CheckGUICapture(); CheckNativeMouse(); - + I_ProcessJoysticks(); I_GetEvent(); } @@ -621,12 +606,12 @@ void I_StartFrame() void I_SetMouseCapture() { - + } void I_ReleaseMouseCapture() { - + } @@ -1104,7 +1089,7 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) } -const Uint16 BYTES_PER_PIXEL = 4; +const size_t BYTES_PER_PIXEL = 4; } // unnamed namespace @@ -1770,7 +1755,7 @@ CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) void I_SetMainWindowVisible(bool visible) { [appCtrl setMainWindowVisible:visible]; - + SetNativeMouse(!visible); } @@ -1849,71 +1834,150 @@ bool I_SetCursor(FTexture* cursorpic) extern "C" { -int SDL_Init(Uint32 flags) -{ - ZD_UNUSED(flags); - - return 0; -} - -void SDL_Quit() -{ - if (NULL != appCtrl) + typedef enum { - [NSApp setDelegate:nil]; - [NSApp deactivate]; + SDL_FALSE = 0, + SDL_TRUE = 1 + } SDL_bool; - [appCtrl release]; - appCtrl = NULL; - } -} +typedef int8_t Sint8; +typedef uint8_t Uint8; +typedef int16_t Sint16; +typedef uint16_t Uint16; +typedef int32_t Sint32; +typedef uint32_t Uint32; + + typedef enum + { + SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ + SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ + SDL_WINDOW_SHOWN = 0x00000004, /**< window is visible */ + SDL_WINDOW_HIDDEN = 0x00000008, /**< window is not visible */ + SDL_WINDOW_BORDERLESS = 0x00000010, /**< no window decoration */ + SDL_WINDOW_RESIZABLE = 0x00000020, /**< window can be resized */ + SDL_WINDOW_MINIMIZED = 0x00000040, /**< window is minimized */ + SDL_WINDOW_MAXIMIZED = 0x00000080, /**< window is maximized */ + SDL_WINDOW_INPUT_GRABBED = 0x00000100, /**< window has grabbed input focus */ + SDL_WINDOW_INPUT_FOCUS = 0x00000200, /**< window has input focus */ + SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ + SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), + SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ + SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000 /**< window should be created in high-DPI mode if supported */ + } SDL_WindowFlags; -const char* SDL_GetError() -{ - static char empty[] = {0}; - return empty; -} + typedef struct SDL_Rect { + Sint16 x, y; + Uint16 w, h; + } SDL_Rect; + + typedef struct SDL_Color { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 unused; + } SDL_Color; -int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode) -{ - // NOTE: Only required fields are assigned - const NSRect displayRect = [[NSScreen mainScreen] frame]; - - mode->w = displayRect.size.width; - mode->h = displayRect.size.height; - - return 0; -} + typedef struct SDL_Palette { + int ncolors; + SDL_Color *colors; + } SDL_Palette; + + typedef struct SDL_PixelFormat { + SDL_Palette *palette; + Uint8 BitsPerPixel; + Uint8 BytesPerPixel; + Uint8 Rloss; + Uint8 Gloss; + Uint8 Bloss; + Uint8 Aloss; + Uint8 Rshift; + Uint8 Gshift; + Uint8 Bshift; + Uint8 Ashift; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint32 Amask; + + /** RGB color key information */ + Uint32 colorkey; + /** Alpha value information (per-surface alpha) */ + Uint8 alpha; + } SDL_PixelFormat; + + /** This structure should be treated as read-only, except for 'pixels', + * which, if not NULL, contains the raw pixel data for the surface. + */ + typedef struct SDL_Surface { + Uint32 flags; /**< Read-only */ + SDL_PixelFormat *format; /**< Read-only */ + int w, h; /**< Read-only */ + Uint16 pitch; /**< Read-only */ + void *pixels; /**< Read-write */ + int offset; /**< Private */ + + /** Hardware-specific surface info */ + struct private_hwdata *hwdata; + + /** clipping information */ + SDL_Rect clip_rect; /**< Read-only */ + Uint32 unused1; /**< for binary compatibility */ + + /** Allow recursive locks */ + Uint32 locked; /**< Private */ + + /** info for fast blit mapping to other surfaces */ + struct SDL_BlitMap *map; /**< Private */ + + /** format version, bumped at every change to invalidate blit maps */ + unsigned int format_version; /**< Private */ + + /** Reference count -- used when freeing surface */ + int refcount; /**< Read-mostly */ + } SDL_Surface; + + typedef enum + { + SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */ + SDL_RENDERER_ACCELERATED = 0x00000002, /**< The renderer uses hardware + acceleration */ + SDL_RENDERER_PRESENTVSYNC = 0x00000004, /**< Present is synchronized + with the refresh rate */ + SDL_RENDERER_TARGETTEXTURE = 0x00000008 /**< The renderer supports + rendering to texture */ + } SDL_RendererFlags; + + /** + * \brief Information on the capabilities of a render driver or context. + */ + typedef struct SDL_RendererInfo + { + const char *name; /**< The name of the renderer */ + Uint32 flags; /**< Supported ::SDL_RendererFlags */ + Uint32 num_texture_formats; /**< The number of available texture formats */ + Uint32 texture_formats[16]; /**< The available texture formats */ + int max_texture_width; /**< The maximimum texture width */ + int max_texture_height; /**< The maximimum texture height */ + } SDL_RendererInfo; + + /** + * \brief The access pattern allowed for a texture. + */ + typedef enum + { + SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */ + SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */ + SDL_TEXTUREACCESS_TARGET /**< Texture can be used as a render target */ + } SDL_TextureAccess; + -static SDL_PixelFormat* GetPixelFormat() -{ - static SDL_PixelFormat result; - - result.palette = NULL; - result.BitsPerPixel = BYTES_PER_PIXEL * 8; - result.BytesPerPixel = BYTES_PER_PIXEL; - result.Rloss = 0; - result.Gloss = 0; - result.Bloss = 0; - result.Aloss = 8; - result.Rshift = 8; - result.Gshift = 16; - result.Bshift = 24; - result.Ashift = 0; - result.Rmask = 0x000000FF; - result.Gmask = 0x0000FF00; - result.Bmask = 0x00FF0000; - result.Amask = 0xFF000000; - - return &result; -} SDL_bool SDL_PixelFormatEnumToMasks(Uint32 format, int* bpp, Uint32* Rmask, Uint32* Gmask, Uint32* Bmask, Uint32* Amask) { - assert(format == SDL_PIXELFORMAT_ABGR8888); + //assert(format == SDL_PIXELFORMAT_ABGR8888); *bpp = 32; *Rmask = 0x000000FF; @@ -2045,42 +2109,13 @@ void SDL_DestroyTexture(SDL_Texture *texture) int SDL_QueryTexture(SDL_Texture *texture, Uint32* format, int* access, int* w, int* h) { - if(format) *format = SDL_PIXELFORMAT_ABGR8888; + if(format) *format = 0; //SDL_PIXELFORMAT_ABGR8888; if(access) *access = SDL_TEXTUREACCESS_STREAMING; if(w) *w = texture->window->w; if(h) *h = texture->window->h; return 0; } -void SDL_GL_SwapBuffers() -{ - [[NSOpenGLContext currentContext] flushBuffer]; -} - -int SDL_GL_SetAttribute(SDL_GLattr attr, int value) -{ - if (SDL_GL_MULTISAMPLESAMPLES == attr) - { - [appCtrl setMultisample:value]; - } - - // Not interested in other attributes - - return 0; -} - - -int SDL_LockSurface(SDL_Surface* surface) -{ - ZD_UNUSED(surface); - - return 0; -} - -void SDL_UnlockSurface(SDL_Surface* surface) -{ - ZD_UNUSED(surface); -} int SDL_LockTexture(SDL_Texture* texture, const SDL_Rect *rect, void** pixels, int *pitch) { @@ -2100,7 +2135,7 @@ void SDL_UnlockTexture(SDL_Texture *texture) int SDL_UpdateWindowSurface(SDL_Window *screen) { assert(NULL != screen); - + if (rbOpts.dirty) { glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); @@ -2112,7 +2147,7 @@ int SDL_UpdateWindowSurface(SDL_Window *screen) rbOpts.dirty = false; } - + const int width = screen->w; const int height = screen->h; @@ -2138,10 +2173,10 @@ int SDL_UpdateWindowSurface(SDL_Window *screen) glEnd(); glFlush(); - - SDL_GL_SwapBuffers(); - - return 0; + + [[NSOpenGLContext currentContext] flushBuffer]; + + return 0; } int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect) @@ -2348,7 +2383,7 @@ static cycle_t SDLFlipCycles; // CODE -------------------------------------------------------------------- -void ScaleWithAspect (int &w, int &h, int Width, int Height) +void ScaleWithAspect (Uint16 &w, Uint16 &h, Uint16 Width, Uint16 Height) { int resRatio = CheckRatio (Width, Height); int screenRatio; @@ -2503,9 +2538,8 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) FString caption; caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); - Screen = SDL_CreateWindow (caption, - SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), - width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); + Screen = SDL_CreateWindow (caption, 0, 0, + width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); if (Screen == NULL) return; @@ -2761,15 +2795,15 @@ void CocoaFrameBuffer::ResetSDLRenderer () if (!Renderer) return; - Uint32 fmt; - switch(vid_displaybits) - { - default: fmt = SDL_PIXELFORMAT_ARGB8888; break; - case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; - case 24: fmt = SDL_PIXELFORMAT_RGB888; break; - case 16: fmt = SDL_PIXELFORMAT_RGB565; break; - case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; - } + Uint32 fmt = 0; +// switch(vid_displaybits) +// { +// default: fmt = SDL_PIXELFORMAT_ARGB8888; break; +// case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; +// case 24: fmt = SDL_PIXELFORMAT_RGB888; break; +// case 16: fmt = SDL_PIXELFORMAT_RGB565; break; +// case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; +// } Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); { @@ -3129,6 +3163,18 @@ DarwinVersion GetDarwinVersion() return result; } +void ReleaseApplicationController() +{ + if (NULL != appCtrl) + { + [NSApp setDelegate:nil]; + [NSApp deactivate]; + + [appCtrl release]; + appCtrl = NULL; + } +} + } // unnamed namespace @@ -3170,9 +3216,10 @@ int main(int argc, char** argv) CreateMenu(); + atterm(ReleaseApplicationController); + appCtrl = [ApplicationController new]; [NSApp setDelegate:appCtrl]; - [NSApp run]; [pool release]; From 3c76e5689a668ed96d5e634ae048c90d703e2f02 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 16 Dec 2014 23:20:14 +0200 Subject: [PATCH 015/117] Fixed path to SDL specific Obj-C++ file --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0855f1de2..a294352f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -601,7 +601,7 @@ elseif( APPLE ) else( OSX_COCOA_BACKEND ) set( SYSTEM_SOURCES_DIR posix sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} cocoa/i_system_cocoa.mm ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/i_system_cocoa.mm ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( OSX_COCOA_BACKEND ) From 84b12d23b54e8f485a9978849068bcf786d4793a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 16 Dec 2014 23:20:54 +0200 Subject: [PATCH 016/117] Native OS X backend no longer requires SDL --- src/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a294352f5..e03593e6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -198,12 +198,12 @@ else( WIN32 ) add_definitions( -DNO_GTK=1 ) endif( NO_GTK ) - # Non-Windows version also needs SDL - find_package( SDL2 REQUIRED ) + # Non-Windows version also needs SDL except native OS X backend if( NOT APPLE OR NOT OSX_COCOA_BACKEND ) + find_package( SDL2 REQUIRED ) + include_directories( "${SDL2_INCLUDE_DIR}" ) set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL2_LIBRARY}" ) endif( NOT APPLE OR NOT OSX_COCOA_BACKEND ) - include_directories( "${SDL2_INCLUDE_DIR}" ) find_path( FPU_CONTROL_DIR fpu_control.h ) if( FPU_CONTROL_DIR ) From 1433bf3f7800f5ce003f6860deba09c104a567d8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 18 Dec 2014 11:52:29 +0200 Subject: [PATCH 017/117] Put all non-Windows source code into posix directory and its subdirectories --- src/CMakeLists.txt | 80 +- src/{ => posix}/cocoa/critsec.cpp | 124 +- src/{ => posix}/cocoa/critsec.h | 106 +- .../cocoa/hid/HID_Config_Utilities.c | 0 src/{ => posix}/cocoa/hid/HID_Error_Handler.c | 0 src/{ => posix}/cocoa/hid/HID_Name_Lookup.c | 0 .../cocoa/hid/HID_Queue_Utilities.c | 0 src/{ => posix}/cocoa/hid/HID_Utilities.c | 0 .../cocoa/hid/HID_Utilities_External.h | 0 src/{ => posix}/cocoa/hid/IOHIDDevice_.c | 0 src/{ => posix}/cocoa/hid/IOHIDDevice_.h | 0 src/{ => posix}/cocoa/hid/IOHIDElement_.c | 0 src/{ => posix}/cocoa/hid/IOHIDElement_.h | 0 src/{ => posix}/cocoa/hid/IOHIDLib_.h | 0 src/{ => posix}/cocoa/hid/ImmrHIDUtilAddOn.c | 0 src/{ => posix}/cocoa/hid/ImmrHIDUtilAddOn.h | 0 src/{ => posix}/cocoa/i_backend_cocoa.mm | 0 src/{ => posix}/cocoa/i_joystick.cpp | 0 src/{ => posix}/cocoa/i_osversion.h | 0 src/{ => posix}/cocoa/i_rbopts.h | 0 src/{ => posix}/cocoa/i_timer.cpp | 0 src/{cocoa => posix/osx}/iwadpicker_cocoa.mm | 0 src/{cocoa => posix/osx}/zdoom-info.plist | 0 src/{cocoa => posix/osx}/zdoom.icns | Bin src/posix/readme.md | 6 + src/{ => posix}/sdl/crashcatcher.c | 858 +++++----- src/{ => posix}/sdl/critsec.h | 96 +- src/{ => posix}/sdl/hardware.cpp | 660 ++++---- src/{ => posix}/sdl/i_gui.cpp | 0 src/{ => posix}/sdl/i_input.cpp | 1028 ++++++------ src/{ => posix}/sdl/i_joystick.cpp | 0 src/{ => posix}/sdl/i_main.cpp | 676 ++++---- .../sdl/i_system.mm} | 0 src/{ => posix}/sdl/i_timer.cpp | 0 src/{ => posix}/sdl/sdlvideo.cpp | 1464 ++++++++--------- src/{ => posix}/sdl/sdlvideo.h | 42 +- src/sdl/SDLMain.m | 387 ----- 37 files changed, 2574 insertions(+), 2953 deletions(-) rename src/{ => posix}/cocoa/critsec.cpp (97%) rename src/{ => posix}/cocoa/critsec.h (97%) rename src/{ => posix}/cocoa/hid/HID_Config_Utilities.c (100%) rename src/{ => posix}/cocoa/hid/HID_Error_Handler.c (100%) rename src/{ => posix}/cocoa/hid/HID_Name_Lookup.c (100%) rename src/{ => posix}/cocoa/hid/HID_Queue_Utilities.c (100%) rename src/{ => posix}/cocoa/hid/HID_Utilities.c (100%) rename src/{ => posix}/cocoa/hid/HID_Utilities_External.h (100%) rename src/{ => posix}/cocoa/hid/IOHIDDevice_.c (100%) rename src/{ => posix}/cocoa/hid/IOHIDDevice_.h (100%) rename src/{ => posix}/cocoa/hid/IOHIDElement_.c (100%) rename src/{ => posix}/cocoa/hid/IOHIDElement_.h (100%) rename src/{ => posix}/cocoa/hid/IOHIDLib_.h (100%) rename src/{ => posix}/cocoa/hid/ImmrHIDUtilAddOn.c (100%) rename src/{ => posix}/cocoa/hid/ImmrHIDUtilAddOn.h (100%) rename src/{ => posix}/cocoa/i_backend_cocoa.mm (100%) rename src/{ => posix}/cocoa/i_joystick.cpp (100%) rename src/{ => posix}/cocoa/i_osversion.h (100%) mode change 100755 => 100644 rename src/{ => posix}/cocoa/i_rbopts.h (100%) rename src/{ => posix}/cocoa/i_timer.cpp (100%) rename src/{cocoa => posix/osx}/iwadpicker_cocoa.mm (100%) rename src/{cocoa => posix/osx}/zdoom-info.plist (100%) rename src/{cocoa => posix/osx}/zdoom.icns (100%) create mode 100644 src/posix/readme.md rename src/{ => posix}/sdl/crashcatcher.c (95%) rename src/{ => posix}/sdl/critsec.h (94%) rename src/{ => posix}/sdl/hardware.cpp (96%) rename src/{ => posix}/sdl/i_gui.cpp (100%) rename src/{ => posix}/sdl/i_input.cpp (97%) rename src/{ => posix}/sdl/i_joystick.cpp (100%) rename src/{ => posix}/sdl/i_main.cpp (96%) rename src/{sdl/i_system_cocoa.mm => posix/sdl/i_system.mm} (100%) rename src/{ => posix}/sdl/i_timer.cpp (100%) rename src/{ => posix}/sdl/sdlvideo.cpp (95%) rename src/{ => posix}/sdl/sdlvideo.h (95%) delete mode 100644 src/sdl/SDLMain.m diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e03593e6e..dd2568f7d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -554,35 +554,35 @@ set( PLAT_POSIX_SOURCES posix/i_system.cpp posix/st_start.cpp ) set( PLAT_SDL_SOURCES - sdl/crashcatcher.c - sdl/hardware.cpp - sdl/i_gui.cpp - sdl/i_input.cpp - sdl/i_joystick.cpp - sdl/i_main.cpp - sdl/i_timer.cpp - sdl/sdlvideo.cpp ) -set( PLAT_MAC_SOURCES - cocoa/iwadpicker_cocoa.mm ) + posix/sdl/crashcatcher.c + posix/sdl/hardware.cpp + posix/sdl/i_gui.cpp + posix/sdl/i_input.cpp + posix/sdl/i_joystick.cpp + posix/sdl/i_main.cpp + posix/sdl/i_timer.cpp + posix/sdl/sdlvideo.cpp ) +set( PLAT_OSX_SOURCES + posix/osx/iwadpicker_cocoa.mm + posix/osx/zdoom.icns ) set( PLAT_COCOA_SOURCES - cocoa/hid/HID_Config_Utilities.c - cocoa/hid/HID_Error_Handler.c - cocoa/hid/HID_Name_Lookup.c - cocoa/hid/HID_Queue_Utilities.c - cocoa/hid/HID_Utilities.c - cocoa/hid/IOHIDDevice_.c - cocoa/hid/IOHIDElement_.c - cocoa/hid/ImmrHIDUtilAddOn.c - cocoa/critsec.cpp - cocoa/i_backend_cocoa.mm - cocoa/i_joystick.cpp - cocoa/i_timer.cpp - cocoa/zdoom.icns ) + posix/cocoa/hid/HID_Config_Utilities.c + posix/cocoa/hid/HID_Error_Handler.c + posix/cocoa/hid/HID_Name_Lookup.c + posix/cocoa/hid/HID_Queue_Utilities.c + posix/cocoa/hid/HID_Utilities.c + posix/cocoa/hid/IOHIDDevice_.c + posix/cocoa/hid/IOHIDElement_.c + posix/cocoa/hid/ImmrHIDUtilAddOn.c + posix/cocoa/critsec.cpp + posix/cocoa/i_backend_cocoa.mm + posix/cocoa/i_joystick.cpp + posix/cocoa/i_timer.cpp ) if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -595,24 +595,24 @@ if( WIN32 ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) elseif( APPLE ) if( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES_DIR posix cocoa ) + set( SYSTEM_SOURCES_DIR posix posix/cocoa ) set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) else( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES_DIR posix sdl ) + set( SYSTEM_SOURCES_DIR posix posix/sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/i_system_cocoa.mm ) + set( PLAT_OSX_SOURCES ${PLAT_OSX_SOURCES} posix/sdl/i_system.mm ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( OSX_COCOA_BACKEND ) - set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_MAC_SOURCES} "${FMOD_LIBRARY}" ) + set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" ) - set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) + set_source_files_properties( posix/osx/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) else( WIN32 ) - set( SYSTEM_SOURCES_DIR posix sdl ) + set( SYSTEM_SOURCES_DIR posix posix/sdl ) set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) endif( WIN32 ) if( NOT ASM_SOURCES ) @@ -672,16 +672,15 @@ if( WIN32 ) set( EXTRA_HEADER_DIRS win32/*.h ) elseif( APPLE ) if( OSX_COCOA_BACKEND ) - set( EXTRA_HEADER_DIRS posix/*.h cocoa/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h posix/cocoa/*.h ) else( OSX_COCOA_BACKEND ) - set( EXTRA_HEADER_DIRS posix/*.h sdl/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h posix/sdl/*.h ) endif( OSX_COCOA_BACKEND ) else( WIN32 ) - set( EXTRA_HEADER_DIRS posix/*.h sdl/*.h ) + set( EXTRA_HEADER_DIRS posix/*.h posix/sdl/*.h ) endif( WIN32 ) file( GLOB HEADER_FILES ${EXTRA_HEADER_DIRS} - cocoa/*.h fragglescript/*.h g_doom/*.h g_heretic/*.h @@ -694,9 +693,10 @@ file( GLOB HEADER_FILES oplsynth/*.h oplsynth/dosbox/*.h posix/*.h + posix/cocoa/*.h + posix/sdl/*.h r_data/*.h resourcefiles/*.h - sdl/*.h sfmt/*.h sound/*.h textures/*.h @@ -1195,7 +1195,7 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) if( APPLE ) set_target_properties(zdoom PROPERTIES LINK_FLAGS "-framework Carbon -framework Cocoa -framework IOKit -framework OpenGL" - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/zdoom-info.plist" ) + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" ) # Fix fmod link so that it can be found in the app bundle. find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) @@ -1218,7 +1218,6 @@ source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURC source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h) source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$") source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$") -source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/cocoa/.+") source_group("Decorate++" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/thingdef/.+") source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+") source_group("Games\\Doom Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_doom/.+") @@ -1235,7 +1234,10 @@ source_group("Render Data\\Resource Sources" REGULAR_EXPRESSION "^${CMAKE_CURREN source_group("Render Data\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/textures/.+") source_group("Render Interface" FILES r_defs.h r_renderer.h r_sky.cpp r_sky.h r_state.h r_utility.cpp r_utility.h) source_group("Resource Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/resourcefiles/.+") -source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sdl/.+") +source_group("POSIX Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/.+") +source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/cocoa/.+") +source_group("OS X Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/.+") +source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/sdl/.+") source_group("SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") source_group("Shared Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_shared/.+") source_group("Versioning" FILES version.h win32/zdoom.rc) diff --git a/src/cocoa/critsec.cpp b/src/posix/cocoa/critsec.cpp similarity index 97% rename from src/cocoa/critsec.cpp rename to src/posix/cocoa/critsec.cpp index 97bc2251c..cbf112491 100644 --- a/src/cocoa/critsec.cpp +++ b/src/posix/cocoa/critsec.cpp @@ -1,62 +1,62 @@ -/* - ** critsec.cpp - ** - **--------------------------------------------------------------------------- - ** Copyright 2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#include "critsec.h" - -// TODO: add error handling - -FCriticalSection::FCriticalSection() -{ - pthread_mutexattr_t attributes; - pthread_mutexattr_init(&attributes); - pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); - - pthread_mutex_init(&m_mutex, &attributes); - - pthread_mutexattr_destroy(&attributes); -} - -FCriticalSection::~FCriticalSection() -{ - pthread_mutex_destroy(&m_mutex); -} - -void FCriticalSection::Enter() -{ - pthread_mutex_lock(&m_mutex); -} - -void FCriticalSection::Leave() -{ - pthread_mutex_unlock(&m_mutex); -} +/* + ** critsec.cpp + ** + **--------------------------------------------------------------------------- + ** Copyright 2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include "critsec.h" + +// TODO: add error handling + +FCriticalSection::FCriticalSection() +{ + pthread_mutexattr_t attributes; + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&m_mutex, &attributes); + + pthread_mutexattr_destroy(&attributes); +} + +FCriticalSection::~FCriticalSection() +{ + pthread_mutex_destroy(&m_mutex); +} + +void FCriticalSection::Enter() +{ + pthread_mutex_lock(&m_mutex); +} + +void FCriticalSection::Leave() +{ + pthread_mutex_unlock(&m_mutex); +} diff --git a/src/cocoa/critsec.h b/src/posix/cocoa/critsec.h similarity index 97% rename from src/cocoa/critsec.h rename to src/posix/cocoa/critsec.h index 20c476ae3..7940dfe32 100644 --- a/src/cocoa/critsec.h +++ b/src/posix/cocoa/critsec.h @@ -1,53 +1,53 @@ -/* - ** critsec.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#ifndef CRITSEC_H -#define CRITSEC_H - -#include - -class FCriticalSection -{ -public: - FCriticalSection(); - ~FCriticalSection(); - - void Enter(); - void Leave(); - -private: - pthread_mutex_t m_mutex; - -}; - -#endif +/* + ** critsec.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#ifndef CRITSEC_H +#define CRITSEC_H + +#include + +class FCriticalSection +{ +public: + FCriticalSection(); + ~FCriticalSection(); + + void Enter(); + void Leave(); + +private: + pthread_mutex_t m_mutex; + +}; + +#endif diff --git a/src/cocoa/hid/HID_Config_Utilities.c b/src/posix/cocoa/hid/HID_Config_Utilities.c similarity index 100% rename from src/cocoa/hid/HID_Config_Utilities.c rename to src/posix/cocoa/hid/HID_Config_Utilities.c diff --git a/src/cocoa/hid/HID_Error_Handler.c b/src/posix/cocoa/hid/HID_Error_Handler.c similarity index 100% rename from src/cocoa/hid/HID_Error_Handler.c rename to src/posix/cocoa/hid/HID_Error_Handler.c diff --git a/src/cocoa/hid/HID_Name_Lookup.c b/src/posix/cocoa/hid/HID_Name_Lookup.c similarity index 100% rename from src/cocoa/hid/HID_Name_Lookup.c rename to src/posix/cocoa/hid/HID_Name_Lookup.c diff --git a/src/cocoa/hid/HID_Queue_Utilities.c b/src/posix/cocoa/hid/HID_Queue_Utilities.c similarity index 100% rename from src/cocoa/hid/HID_Queue_Utilities.c rename to src/posix/cocoa/hid/HID_Queue_Utilities.c diff --git a/src/cocoa/hid/HID_Utilities.c b/src/posix/cocoa/hid/HID_Utilities.c similarity index 100% rename from src/cocoa/hid/HID_Utilities.c rename to src/posix/cocoa/hid/HID_Utilities.c diff --git a/src/cocoa/hid/HID_Utilities_External.h b/src/posix/cocoa/hid/HID_Utilities_External.h similarity index 100% rename from src/cocoa/hid/HID_Utilities_External.h rename to src/posix/cocoa/hid/HID_Utilities_External.h diff --git a/src/cocoa/hid/IOHIDDevice_.c b/src/posix/cocoa/hid/IOHIDDevice_.c similarity index 100% rename from src/cocoa/hid/IOHIDDevice_.c rename to src/posix/cocoa/hid/IOHIDDevice_.c diff --git a/src/cocoa/hid/IOHIDDevice_.h b/src/posix/cocoa/hid/IOHIDDevice_.h similarity index 100% rename from src/cocoa/hid/IOHIDDevice_.h rename to src/posix/cocoa/hid/IOHIDDevice_.h diff --git a/src/cocoa/hid/IOHIDElement_.c b/src/posix/cocoa/hid/IOHIDElement_.c similarity index 100% rename from src/cocoa/hid/IOHIDElement_.c rename to src/posix/cocoa/hid/IOHIDElement_.c diff --git a/src/cocoa/hid/IOHIDElement_.h b/src/posix/cocoa/hid/IOHIDElement_.h similarity index 100% rename from src/cocoa/hid/IOHIDElement_.h rename to src/posix/cocoa/hid/IOHIDElement_.h diff --git a/src/cocoa/hid/IOHIDLib_.h b/src/posix/cocoa/hid/IOHIDLib_.h similarity index 100% rename from src/cocoa/hid/IOHIDLib_.h rename to src/posix/cocoa/hid/IOHIDLib_.h diff --git a/src/cocoa/hid/ImmrHIDUtilAddOn.c b/src/posix/cocoa/hid/ImmrHIDUtilAddOn.c similarity index 100% rename from src/cocoa/hid/ImmrHIDUtilAddOn.c rename to src/posix/cocoa/hid/ImmrHIDUtilAddOn.c diff --git a/src/cocoa/hid/ImmrHIDUtilAddOn.h b/src/posix/cocoa/hid/ImmrHIDUtilAddOn.h similarity index 100% rename from src/cocoa/hid/ImmrHIDUtilAddOn.h rename to src/posix/cocoa/hid/ImmrHIDUtilAddOn.h diff --git a/src/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm similarity index 100% rename from src/cocoa/i_backend_cocoa.mm rename to src/posix/cocoa/i_backend_cocoa.mm diff --git a/src/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp similarity index 100% rename from src/cocoa/i_joystick.cpp rename to src/posix/cocoa/i_joystick.cpp diff --git a/src/cocoa/i_osversion.h b/src/posix/cocoa/i_osversion.h old mode 100755 new mode 100644 similarity index 100% rename from src/cocoa/i_osversion.h rename to src/posix/cocoa/i_osversion.h diff --git a/src/cocoa/i_rbopts.h b/src/posix/cocoa/i_rbopts.h similarity index 100% rename from src/cocoa/i_rbopts.h rename to src/posix/cocoa/i_rbopts.h diff --git a/src/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp similarity index 100% rename from src/cocoa/i_timer.cpp rename to src/posix/cocoa/i_timer.cpp diff --git a/src/cocoa/iwadpicker_cocoa.mm b/src/posix/osx/iwadpicker_cocoa.mm similarity index 100% rename from src/cocoa/iwadpicker_cocoa.mm rename to src/posix/osx/iwadpicker_cocoa.mm diff --git a/src/cocoa/zdoom-info.plist b/src/posix/osx/zdoom-info.plist similarity index 100% rename from src/cocoa/zdoom-info.plist rename to src/posix/osx/zdoom-info.plist diff --git a/src/cocoa/zdoom.icns b/src/posix/osx/zdoom.icns similarity index 100% rename from src/cocoa/zdoom.icns rename to src/posix/osx/zdoom.icns diff --git a/src/posix/readme.md b/src/posix/readme.md new file mode 100644 index 000000000..ce66dab2c --- /dev/null +++ b/src/posix/readme.md @@ -0,0 +1,6 @@ +This directory contains files required to support POSIX-compatible OSes, like GNU/Linux, OS X or BSD. + +Common files are placed in this directory directly. +SDL backend files are in `sdl` subdirectory. +Native OS X backend files are in `cocoa` subdirectory. +Shared files for both OS X backends are in `osx` subdirectory. diff --git a/src/sdl/crashcatcher.c b/src/posix/sdl/crashcatcher.c similarity index 95% rename from src/sdl/crashcatcher.c rename to src/posix/sdl/crashcatcher.c index a4f68d9a3..4754a369a 100644 --- a/src/sdl/crashcatcher.c +++ b/src/posix/sdl/crashcatcher.c @@ -1,429 +1,429 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -#include -#ifndef PR_SET_PTRACER -#define PR_SET_PTRACER 0x59616d61 -#endif -#elif defined (__APPLE__) -#include -#endif - - -static const char crash_switch[] = "--cc-handle-crash"; - -static const char fatal_err[] = "\n\n*** Fatal Error ***\n"; -static const char pipe_err[] = "!!! Failed to create pipe\n"; -static const char fork_err[] = "!!! Failed to fork debug process\n"; -static const char exec_err[] = "!!! Failed to exec debug process\n"; - -static char argv0[PATH_MAX]; - -static char altstack[SIGSTKSZ]; - - -static struct { - int signum; - pid_t pid; - int has_siginfo; - siginfo_t siginfo; - char buf[1024]; -} crash_info; - - -static const struct { - const char *name; - int signum; -} signals[] = { - { "Segmentation fault", SIGSEGV }, - { "Illegal instruction", SIGILL }, - { "FPU exception", SIGFPE }, - { "System BUS error", SIGBUS }, - { NULL, 0 } -}; - -static const struct { - int code; - const char *name; -} sigill_codes[] = { -#ifndef __FreeBSD__ - { ILL_ILLOPC, "Illegal opcode" }, - { ILL_ILLOPN, "Illegal operand" }, - { ILL_ILLADR, "Illegal addressing mode" }, - { ILL_ILLTRP, "Illegal trap" }, - { ILL_PRVOPC, "Privileged opcode" }, - { ILL_PRVREG, "Privileged register" }, - { ILL_COPROC, "Coprocessor error" }, - { ILL_BADSTK, "Internal stack error" }, -#endif - { 0, NULL } -}; - -static const struct { - int code; - const char *name; -} sigfpe_codes[] = { - { FPE_INTDIV, "Integer divide by zero" }, - { FPE_INTOVF, "Integer overflow" }, - { FPE_FLTDIV, "Floating point divide by zero" }, - { FPE_FLTOVF, "Floating point overflow" }, - { FPE_FLTUND, "Floating point underflow" }, - { FPE_FLTRES, "Floating point inexact result" }, - { FPE_FLTINV, "Floating point invalid operation" }, - { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } -}; - -static const struct { - int code; - const char *name; -} sigsegv_codes[] = { -#ifndef __FreeBSD__ - { SEGV_MAPERR, "Address not mapped to object" }, - { SEGV_ACCERR, "Invalid permissions for mapped object" }, -#endif - { 0, NULL } -}; - -static const struct { - int code; - const char *name; -} sigbus_codes[] = { -#ifndef __FreeBSD__ - { BUS_ADRALN, "Invalid address alignment" }, - { BUS_ADRERR, "Non-existent physical address" }, - { BUS_OBJERR, "Object specific hardware error" }, -#endif - { 0, NULL } -}; - -static int (*cc_user_info)(char*, char*); - - -static void gdb_info(pid_t pid) -{ - char respfile[64]; - char cmd_buf[128]; - FILE *f; - int fd; - - /* Create a temp file to put gdb commands into */ - strcpy(respfile, "gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) - { - fprintf(f, "attach %d\n" - "shell echo \"\"\n" - "shell echo \"* Loaded Libraries\"\n" - "info sharedlibrary\n" - "shell echo \"\"\n" - "shell echo \"* Threads\"\n" - "info threads\n" - "shell echo \"\"\n" - "shell echo \"* FPU Status\"\n" - "info float\n" - "shell echo \"\"\n" - "shell echo \"* Registers\"\n" - "info registers\n" - "shell echo \"\"\n" - "shell echo \"* Backtrace\"\n" - "thread apply all backtrace full\n" - "detach\n" - "quit\n", pid); - fclose(f); - - /* Run gdb and print process info. */ - snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); - printf("Executing: %s\n", cmd_buf); - fflush(stdout); - - system(cmd_buf); - /* Clean up */ - remove(respfile); - } - else - { - /* Error creating temp file */ - if(fd >= 0) - { - close(fd); - remove(respfile); - } - printf("!!! Could not create gdb command file\n"); - } - fflush(stdout); -} - -static void sys_info(void) -{ -#ifdef __unix__ - system("echo \"System: `uname -a`\""); - putchar('\n'); - fflush(stdout); -#endif -} - - -static size_t safe_write(int fd, const void *buf, size_t len) -{ - size_t ret = 0; - while(ret < len) - { - ssize_t rem; - if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) - { - if(errno == EINTR) - continue; - break; - } - ret += rem; - } - return ret; -} - -static void crash_catcher(int signum, siginfo_t *siginfo, void *context) -{ - //ucontext_t *ucontext = (ucontext_t*)context; - pid_t dbg_pid; - int fd[2]; - - /* Make sure the effective uid is the real uid */ - if(getuid() != geteuid()) - { - raise(signum); - return; - } - - safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); - if(pipe(fd) == -1) - { - safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); - raise(signum); - return; - } - - crash_info.signum = signum; - crash_info.pid = getpid(); - crash_info.has_siginfo = !!siginfo; - if(siginfo) - crash_info.siginfo = *siginfo; - if(cc_user_info) - cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); - - /* Fork off to start a crash handler */ - switch((dbg_pid=fork())) - { - /* Error */ - case -1: - safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); - raise(signum); - return; - - case 0: - dup2(fd[0], STDIN_FILENO); - close(fd[0]); - close(fd[1]); - - execl(argv0, argv0, crash_switch, NULL); - - safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); - _exit(1); - - default: -#ifdef __linux__ - prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); -#endif - safe_write(fd[1], &crash_info, sizeof(crash_info)); - close(fd[0]); - close(fd[1]); - - /* Wait; we'll be killed when gdb is done */ - do { - int status; - if(waitpid(dbg_pid, &status, 0) == dbg_pid && - (WIFEXITED(status) || WIFSIGNALED(status))) - { - /* The debug process died before it could kill us */ - raise(signum); - break; - } - } while(1); - } -} - -static void crash_handler(const char *logfile) -{ - const char *sigdesc = ""; - int i; - - if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) - { - fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); - exit(1); - } - - /* Get the signal description */ - for(i = 0;signals[i].name;++i) - { - if(signals[i].signum == crash_info.signum) - { - sigdesc = signals[i].name; - break; - } - } - - if(crash_info.has_siginfo) - { - switch(crash_info.signum) - { - case SIGSEGV: - for(i = 0;sigsegv_codes[i].name;++i) - { - if(sigsegv_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigsegv_codes[i].name; - break; - } - } - break; - - case SIGFPE: - for(i = 0;sigfpe_codes[i].name;++i) - { - if(sigfpe_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigfpe_codes[i].name; - break; - } - } - break; - - case SIGILL: - for(i = 0;sigill_codes[i].name;++i) - { - if(sigill_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigill_codes[i].name; - break; - } - } - break; - - case SIGBUS: - for(i = 0;sigbus_codes[i].name;++i) - { - if(sigbus_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigbus_codes[i].name; - break; - } - } - break; - } - } - fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stderr); - - if(logfile) - { - /* Create crash log file and redirect shell output to it */ - if(freopen(logfile, "wa", stdout) != stdout) - { - fprintf(stderr, "!!! Could not create %s following signal\n", logfile); - exit(1); - } - fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); - - printf("*** Fatal Error ***\n" - "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - printf("Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stdout); - fflush(stdout); - } - - sys_info(); - - crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; - printf("%s\n", crash_info.buf); - fflush(stdout); - - if(crash_info.pid > 0) - { - gdb_info(crash_info.pid); - kill(crash_info.pid, SIGKILL); - } - - if(logfile) - { - const char *str; - char buf[512]; - - if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0) - snprintf(buf, sizeof(buf), "kdialog --title \"Very Fatal Error\" --textbox \"%s\" 800 600", logfile); - else if((str=getenv("GNOME_DESKTOP_SESSION_ID")) && str[0] != '\0') - snprintf(buf, sizeof(buf), "gxmessage -buttons \"Okay:0\" -geometry 800x600 -title \"Very Fatal Error\" -center -file \"%s\"", logfile); - else - snprintf(buf, sizeof(buf), "xmessage -buttons \"Okay:0\" -center -file \"%s\"", logfile); - - system(buf); - } - exit(0); -} - -int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) -{ - struct sigaction sa; - stack_t altss; - int retval; - - if(argc == 2 && strcmp(argv[1], crash_switch) == 0) - crash_handler(logfile); - - cc_user_info = user_info; - - if(argv[0][0] == '/') - snprintf(argv0, sizeof(argv0), "%s", argv[0]); - else - { - getcwd(argv0, sizeof(argv0)); - retval = strlen(argv0); - snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); - } - - /* Set an alternate signal stack so SIGSEGVs caused by stack overflows - * still run */ - altss.ss_sp = altstack; - altss.ss_flags = 0; - altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); - - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = crash_catcher; - sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&sa.sa_mask); - - retval = 0; - while(num_signals--) - { - if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) - { - *signals = 0; - retval = -1; - } - ++signals; - } - return retval; -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif +#elif defined (__APPLE__) +#include +#endif + + +static const char crash_switch[] = "--cc-handle-crash"; + +static const char fatal_err[] = "\n\n*** Fatal Error ***\n"; +static const char pipe_err[] = "!!! Failed to create pipe\n"; +static const char fork_err[] = "!!! Failed to fork debug process\n"; +static const char exec_err[] = "!!! Failed to exec debug process\n"; + +static char argv0[PATH_MAX]; + +static char altstack[SIGSTKSZ]; + + +static struct { + int signum; + pid_t pid; + int has_siginfo; + siginfo_t siginfo; + char buf[1024]; +} crash_info; + + +static const struct { + const char *name; + int signum; +} signals[] = { + { "Segmentation fault", SIGSEGV }, + { "Illegal instruction", SIGILL }, + { "FPU exception", SIGFPE }, + { "System BUS error", SIGBUS }, + { NULL, 0 } +}; + +static const struct { + int code; + const char *name; +} sigill_codes[] = { +#ifndef __FreeBSD__ + { ILL_ILLOPC, "Illegal opcode" }, + { ILL_ILLOPN, "Illegal operand" }, + { ILL_ILLADR, "Illegal addressing mode" }, + { ILL_ILLTRP, "Illegal trap" }, + { ILL_PRVOPC, "Privileged opcode" }, + { ILL_PRVREG, "Privileged register" }, + { ILL_COPROC, "Coprocessor error" }, + { ILL_BADSTK, "Internal stack error" }, +#endif + { 0, NULL } +}; + +static const struct { + int code; + const char *name; +} sigfpe_codes[] = { + { FPE_INTDIV, "Integer divide by zero" }, + { FPE_INTOVF, "Integer overflow" }, + { FPE_FLTDIV, "Floating point divide by zero" }, + { FPE_FLTOVF, "Floating point overflow" }, + { FPE_FLTUND, "Floating point underflow" }, + { FPE_FLTRES, "Floating point inexact result" }, + { FPE_FLTINV, "Floating point invalid operation" }, + { FPE_FLTSUB, "Subscript out of range" }, + { 0, NULL } +}; + +static const struct { + int code; + const char *name; +} sigsegv_codes[] = { +#ifndef __FreeBSD__ + { SEGV_MAPERR, "Address not mapped to object" }, + { SEGV_ACCERR, "Invalid permissions for mapped object" }, +#endif + { 0, NULL } +}; + +static const struct { + int code; + const char *name; +} sigbus_codes[] = { +#ifndef __FreeBSD__ + { BUS_ADRALN, "Invalid address alignment" }, + { BUS_ADRERR, "Non-existent physical address" }, + { BUS_OBJERR, "Object specific hardware error" }, +#endif + { 0, NULL } +}; + +static int (*cc_user_info)(char*, char*); + + +static void gdb_info(pid_t pid) +{ + char respfile[64]; + char cmd_buf[128]; + FILE *f; + int fd; + + /* Create a temp file to put gdb commands into */ + strcpy(respfile, "gdb-respfile-XXXXXX"); + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + { + fprintf(f, "attach %d\n" + "shell echo \"\"\n" + "shell echo \"* Loaded Libraries\"\n" + "info sharedlibrary\n" + "shell echo \"\"\n" + "shell echo \"* Threads\"\n" + "info threads\n" + "shell echo \"\"\n" + "shell echo \"* FPU Status\"\n" + "info float\n" + "shell echo \"\"\n" + "shell echo \"* Registers\"\n" + "info registers\n" + "shell echo \"\"\n" + "shell echo \"* Backtrace\"\n" + "thread apply all backtrace full\n" + "detach\n" + "quit\n", pid); + fclose(f); + + /* Run gdb and print process info. */ + snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); + printf("Executing: %s\n", cmd_buf); + fflush(stdout); + + system(cmd_buf); + /* Clean up */ + remove(respfile); + } + else + { + /* Error creating temp file */ + if(fd >= 0) + { + close(fd); + remove(respfile); + } + printf("!!! Could not create gdb command file\n"); + } + fflush(stdout); +} + +static void sys_info(void) +{ +#ifdef __unix__ + system("echo \"System: `uname -a`\""); + putchar('\n'); + fflush(stdout); +#endif +} + + +static size_t safe_write(int fd, const void *buf, size_t len) +{ + size_t ret = 0; + while(ret < len) + { + ssize_t rem; + if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) + { + if(errno == EINTR) + continue; + break; + } + ret += rem; + } + return ret; +} + +static void crash_catcher(int signum, siginfo_t *siginfo, void *context) +{ + //ucontext_t *ucontext = (ucontext_t*)context; + pid_t dbg_pid; + int fd[2]; + + /* Make sure the effective uid is the real uid */ + if(getuid() != geteuid()) + { + raise(signum); + return; + } + + safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); + if(pipe(fd) == -1) + { + safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); + raise(signum); + return; + } + + crash_info.signum = signum; + crash_info.pid = getpid(); + crash_info.has_siginfo = !!siginfo; + if(siginfo) + crash_info.siginfo = *siginfo; + if(cc_user_info) + cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); + + /* Fork off to start a crash handler */ + switch((dbg_pid=fork())) + { + /* Error */ + case -1: + safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); + raise(signum); + return; + + case 0: + dup2(fd[0], STDIN_FILENO); + close(fd[0]); + close(fd[1]); + + execl(argv0, argv0, crash_switch, NULL); + + safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); + _exit(1); + + default: +#ifdef __linux__ + prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); +#endif + safe_write(fd[1], &crash_info, sizeof(crash_info)); + close(fd[0]); + close(fd[1]); + + /* Wait; we'll be killed when gdb is done */ + do { + int status; + if(waitpid(dbg_pid, &status, 0) == dbg_pid && + (WIFEXITED(status) || WIFSIGNALED(status))) + { + /* The debug process died before it could kill us */ + raise(signum); + break; + } + } while(1); + } +} + +static void crash_handler(const char *logfile) +{ + const char *sigdesc = ""; + int i; + + if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) + { + fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); + exit(1); + } + + /* Get the signal description */ + for(i = 0;signals[i].name;++i) + { + if(signals[i].signum == crash_info.signum) + { + sigdesc = signals[i].name; + break; + } + } + + if(crash_info.has_siginfo) + { + switch(crash_info.signum) + { + case SIGSEGV: + for(i = 0;sigsegv_codes[i].name;++i) + { + if(sigsegv_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigsegv_codes[i].name; + break; + } + } + break; + + case SIGFPE: + for(i = 0;sigfpe_codes[i].name;++i) + { + if(sigfpe_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigfpe_codes[i].name; + break; + } + } + break; + + case SIGILL: + for(i = 0;sigill_codes[i].name;++i) + { + if(sigill_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigill_codes[i].name; + break; + } + } + break; + + case SIGBUS: + for(i = 0;sigbus_codes[i].name;++i) + { + if(sigbus_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigbus_codes[i].name; + break; + } + } + break; + } + } + fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stderr); + + if(logfile) + { + /* Create crash log file and redirect shell output to it */ + if(freopen(logfile, "wa", stdout) != stdout) + { + fprintf(stderr, "!!! Could not create %s following signal\n", logfile); + exit(1); + } + fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); + + printf("*** Fatal Error ***\n" + "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + printf("Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stdout); + fflush(stdout); + } + + sys_info(); + + crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; + printf("%s\n", crash_info.buf); + fflush(stdout); + + if(crash_info.pid > 0) + { + gdb_info(crash_info.pid); + kill(crash_info.pid, SIGKILL); + } + + if(logfile) + { + const char *str; + char buf[512]; + + if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0) + snprintf(buf, sizeof(buf), "kdialog --title \"Very Fatal Error\" --textbox \"%s\" 800 600", logfile); + else if((str=getenv("GNOME_DESKTOP_SESSION_ID")) && str[0] != '\0') + snprintf(buf, sizeof(buf), "gxmessage -buttons \"Okay:0\" -geometry 800x600 -title \"Very Fatal Error\" -center -file \"%s\"", logfile); + else + snprintf(buf, sizeof(buf), "xmessage -buttons \"Okay:0\" -center -file \"%s\"", logfile); + + system(buf); + } + exit(0); +} + +int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) +{ + struct sigaction sa; + stack_t altss; + int retval; + + if(argc == 2 && strcmp(argv[1], crash_switch) == 0) + crash_handler(logfile); + + cc_user_info = user_info; + + if(argv[0][0] == '/') + snprintf(argv0, sizeof(argv0), "%s", argv[0]); + else + { + getcwd(argv0, sizeof(argv0)); + retval = strlen(argv0); + snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); + } + + /* Set an alternate signal stack so SIGSEGVs caused by stack overflows + * still run */ + altss.ss_sp = altstack; + altss.ss_flags = 0; + altss.ss_size = sizeof(altstack); + sigaltstack(&altss, NULL); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = crash_catcher; + sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); + + retval = 0; + while(num_signals--) + { + if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && + *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + { + *signals = 0; + retval = -1; + } + ++signals; + } + return retval; +} diff --git a/src/sdl/critsec.h b/src/posix/sdl/critsec.h similarity index 94% rename from src/sdl/critsec.h rename to src/posix/sdl/critsec.h index daaf30f7d..a3d6210af 100644 --- a/src/sdl/critsec.h +++ b/src/posix/sdl/critsec.h @@ -1,48 +1,48 @@ -// Wraps an SDL mutex object. (A critical section is a Windows synchronization -// object similar to a mutex but optimized for access by threads belonging to -// only one process, hence the class name.) - -#ifndef CRITSEC_H -#define CRITSEC_H - -#include "SDL.h" -#include "SDL_thread.h" -#include "i_system.h" - -class FCriticalSection -{ -public: - FCriticalSection() - { - CritSec = SDL_CreateMutex(); - if (CritSec == NULL) - { - I_FatalError("Failed to create a critical section mutex."); - } - } - ~FCriticalSection() - { - if (CritSec != NULL) - { - SDL_DestroyMutex(CritSec); - } - } - void Enter() - { - if (SDL_mutexP(CritSec) != 0) - { - I_FatalError("Failed entering a critical section."); - } - } - void Leave() - { - if (SDL_mutexV(CritSec) != 0) - { - I_FatalError("Failed to leave a critical section."); - } - } -private: - SDL_mutex *CritSec; -}; - -#endif +// Wraps an SDL mutex object. (A critical section is a Windows synchronization +// object similar to a mutex but optimized for access by threads belonging to +// only one process, hence the class name.) + +#ifndef CRITSEC_H +#define CRITSEC_H + +#include "SDL.h" +#include "SDL_thread.h" +#include "i_system.h" + +class FCriticalSection +{ +public: + FCriticalSection() + { + CritSec = SDL_CreateMutex(); + if (CritSec == NULL) + { + I_FatalError("Failed to create a critical section mutex."); + } + } + ~FCriticalSection() + { + if (CritSec != NULL) + { + SDL_DestroyMutex(CritSec); + } + } + void Enter() + { + if (SDL_mutexP(CritSec) != 0) + { + I_FatalError("Failed entering a critical section."); + } + } + void Leave() + { + if (SDL_mutexV(CritSec) != 0) + { + I_FatalError("Failed to leave a critical section."); + } + } +private: + SDL_mutex *CritSec; +}; + +#endif diff --git a/src/sdl/hardware.cpp b/src/posix/sdl/hardware.cpp similarity index 96% rename from src/sdl/hardware.cpp rename to src/posix/sdl/hardware.cpp index 352fe8558..25e19ff21 100644 --- a/src/sdl/hardware.cpp +++ b/src/posix/sdl/hardware.cpp @@ -1,330 +1,330 @@ -/* -** hardware.cpp -** Somewhat OS-independant interface to the screen, mouse, keyboard, and stick -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include -#include -#include - -#include "hardware.h" -#include "i_video.h" -#include "i_system.h" -#include "c_console.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "sdlvideo.h" -#include "v_text.h" -#include "doomstat.h" -#include "m_argv.h" -#include "r_renderer.h" -#include "r_swrenderer.h" - -EXTERN_CVAR (Bool, ticker) -EXTERN_CVAR (Bool, fullscreen) -EXTERN_CVAR (Float, vid_winscale) - -IVideo *Video; - -void I_ShutdownGraphics () -{ - if (screen) - { - DFrameBuffer *s = screen; - screen = NULL; - s->ObjectFlags |= OF_YesReallyDelete; - delete s; - } - if (Video) - delete Video, Video = NULL; -} - -void I_InitGraphics () -{ - UCVarValue val; - - val.Bool = !!Args->CheckParm ("-devparm"); - ticker.SetGenericRepDefault (val, CVAR_Bool); - - Video = new SDLVideo (0); - if (Video == NULL) - I_FatalError ("Failed to initialize display"); - - atterm (I_ShutdownGraphics); - - Video->SetWindowedScale (vid_winscale); -} - -static void I_DeleteRenderer() -{ - if (Renderer != NULL) delete Renderer; -} - -void I_CreateRenderer() -{ - if (Renderer == NULL) - { - Renderer = new FSoftwareRenderer; - atterm(I_DeleteRenderer); - } -} - - -/** Remaining code is common to Win32 and Linux **/ - -// VIDEO WRAPPERS --------------------------------------------------------- - -DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) -{ - bool fs = false; - switch (Video->GetDisplayType ()) - { - case DISPLAY_WindowOnly: - fs = false; - break; - case DISPLAY_FullscreenOnly: - fs = true; - break; - case DISPLAY_Both: - fs = fullscreen; - break; - } - DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); - - /* Right now, CreateFrameBuffer cannot return NULL - if (res == NULL) - { - I_FatalError ("Mode %dx%d is unavailable\n", width, height); - } - */ - return res; -} - -bool I_CheckResolution (int width, int height, int bits) -{ - int twidth, theight; - - Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); - while (Video->NextMode (&twidth, &theight, NULL)) - { - if (width == twidth && height == theight) - return true; - } - return false; -} - -void I_ClosestResolution (int *width, int *height, int bits) -{ - int twidth, theight; - int cwidth = 0, cheight = 0; - int iteration; - DWORD closest = 4294967295u; - - for (iteration = 0; iteration < 2; iteration++) - { - Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); - while (Video->NextMode (&twidth, &theight, NULL)) - { - if (twidth == *width && theight == *height) - return; - - if (iteration == 0 && (twidth < *width || theight < *height)) - continue; - - DWORD dist = (twidth - *width) * (twidth - *width) - + (theight - *height) * (theight - *height); - - if (dist < closest) - { - closest = dist; - cwidth = twidth; - cheight = theight; - } - } - if (closest != 4294967295u) - { - *width = cwidth; - *height = cheight; - return; - } - } -} - -//========================================================================== -// -// SetFPSLimit -// -// Initializes an event timer to fire at a rate of /sec. The video -// update will wait for this timer to trigger before updating. -// -// Pass 0 as the limit for unlimited. -// Pass a negative value for the limit to use the value of vid_maxfps. -// -//========================================================================== - -EXTERN_CVAR(Int, vid_maxfps); -EXTERN_CVAR(Bool, cl_capfps); - -#ifndef __APPLE__ -Semaphore FPSLimitSemaphore; - -static void FPSLimitNotify(sigval val) -{ - SEMAPHORE_SIGNAL(FPSLimitSemaphore) -} - -void I_SetFPSLimit(int limit) -{ - static sigevent FPSLimitEvent; - static timer_t FPSLimitTimer; - static bool FPSLimitTimerEnabled = false; - static bool EventSetup = false; - if(!EventSetup) - { - EventSetup = true; - FPSLimitEvent.sigev_notify = SIGEV_THREAD; - FPSLimitEvent.sigev_signo = 0; - FPSLimitEvent.sigev_value.sival_int = 0; - FPSLimitEvent.sigev_notify_function = FPSLimitNotify; - FPSLimitEvent.sigev_notify_attributes = NULL; - - SEMAPHORE_INIT(FPSLimitSemaphore, 0, 0) - } - - if (limit < 0) - { - limit = vid_maxfps; - } - // Kill any leftover timer. - if (FPSLimitTimerEnabled) - { - timer_delete(FPSLimitTimer); - FPSLimitTimerEnabled = false; - } - if (limit == 0) - { // no limit - DPrintf("FPS timer disabled\n"); - } - else - { - FPSLimitTimerEnabled = true; - if(timer_create(CLOCK_REALTIME, &FPSLimitEvent, &FPSLimitTimer) == -1) - Printf("Failed to create FPS limitter event\n"); - itimerspec period = { {0, 0}, {0, 0} }; - period.it_value.tv_nsec = period.it_interval.tv_nsec = 1000000000 / limit; - if(timer_settime(FPSLimitTimer, 0, &period, NULL) == -1) - Printf("Failed to set FPS limitter timer\n"); - DPrintf("FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000); - } -} -#else -// So Apple doesn't support POSIX timers and I can't find a good substitute short of -// having Objective-C Cocoa events or something like that. -void I_SetFPSLimit(int limit) -{ -} -#endif - -CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (vid_maxfps < TICRATE && vid_maxfps != 0) - { - vid_maxfps = TICRATE; - } - else if (vid_maxfps > 1000) - { - vid_maxfps = 1000; - } - else if (cl_capfps == 0) - { - I_SetFPSLimit(vid_maxfps); - } -} - -extern int NewWidth, NewHeight, NewBits, DisplayBits; - -CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - NewWidth = screen->GetWidth(); - NewHeight = screen->GetHeight(); - NewBits = DisplayBits; - setmodeneeded = true; -} - -CUSTOM_CVAR (Float, vid_winscale, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 1.f) - { - self = 1.f; - } - else if (Video) - { - Video->SetWindowedScale (self); - NewWidth = screen->GetWidth(); - NewHeight = screen->GetHeight(); - NewBits = DisplayBits; - setmodeneeded = true; - } -} - -CCMD (vid_listmodes) -{ - static const char *ratios[5] = { "", " - 16:9", " - 16:10", "", " - 5:4" }; - int width, height, bits; - bool letterbox; - - if (Video == NULL) - { - return; - } - for (bits = 1; bits <= 32; bits++) - { - Video->StartModeIterator (bits, screen->IsFullscreen()); - while (Video->NextMode (&width, &height, &letterbox)) - { - bool thisMode = (width == DisplayWidth && height == DisplayHeight && bits == DisplayBits); - int ratio = CheckRatio (width, height); - Printf (thisMode ? PRINT_BOLD : PRINT_HIGH, - "%s%4d x%5d x%3d%s%s\n", - thisMode || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, - width, height, bits, - ratios[ratio], - thisMode || !letterbox ? "" : TEXTCOLOR_BROWN " LB" - ); - } - } -} - -CCMD (vid_currentmode) -{ - Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); -} +/* +** hardware.cpp +** Somewhat OS-independant interface to the screen, mouse, keyboard, and stick +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include + +#include "hardware.h" +#include "i_video.h" +#include "i_system.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "sdlvideo.h" +#include "v_text.h" +#include "doomstat.h" +#include "m_argv.h" +#include "r_renderer.h" +#include "r_swrenderer.h" + +EXTERN_CVAR (Bool, ticker) +EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Float, vid_winscale) + +IVideo *Video; + +void I_ShutdownGraphics () +{ + if (screen) + { + DFrameBuffer *s = screen; + screen = NULL; + s->ObjectFlags |= OF_YesReallyDelete; + delete s; + } + if (Video) + delete Video, Video = NULL; +} + +void I_InitGraphics () +{ + UCVarValue val; + + val.Bool = !!Args->CheckParm ("-devparm"); + ticker.SetGenericRepDefault (val, CVAR_Bool); + + Video = new SDLVideo (0); + if (Video == NULL) + I_FatalError ("Failed to initialize display"); + + atterm (I_ShutdownGraphics); + + Video->SetWindowedScale (vid_winscale); +} + +static void I_DeleteRenderer() +{ + if (Renderer != NULL) delete Renderer; +} + +void I_CreateRenderer() +{ + if (Renderer == NULL) + { + Renderer = new FSoftwareRenderer; + atterm(I_DeleteRenderer); + } +} + + +/** Remaining code is common to Win32 and Linux **/ + +// VIDEO WRAPPERS --------------------------------------------------------- + +DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) +{ + bool fs = false; + switch (Video->GetDisplayType ()) + { + case DISPLAY_WindowOnly: + fs = false; + break; + case DISPLAY_FullscreenOnly: + fs = true; + break; + case DISPLAY_Both: + fs = fullscreen; + break; + } + DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); + + /* Right now, CreateFrameBuffer cannot return NULL + if (res == NULL) + { + I_FatalError ("Mode %dx%d is unavailable\n", width, height); + } + */ + return res; +} + +bool I_CheckResolution (int width, int height, int bits) +{ + int twidth, theight; + + Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); + while (Video->NextMode (&twidth, &theight, NULL)) + { + if (width == twidth && height == theight) + return true; + } + return false; +} + +void I_ClosestResolution (int *width, int *height, int bits) +{ + int twidth, theight; + int cwidth = 0, cheight = 0; + int iteration; + DWORD closest = 4294967295u; + + for (iteration = 0; iteration < 2; iteration++) + { + Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); + while (Video->NextMode (&twidth, &theight, NULL)) + { + if (twidth == *width && theight == *height) + return; + + if (iteration == 0 && (twidth < *width || theight < *height)) + continue; + + DWORD dist = (twidth - *width) * (twidth - *width) + + (theight - *height) * (theight - *height); + + if (dist < closest) + { + closest = dist; + cwidth = twidth; + cheight = theight; + } + } + if (closest != 4294967295u) + { + *width = cwidth; + *height = cheight; + return; + } + } +} + +//========================================================================== +// +// SetFPSLimit +// +// Initializes an event timer to fire at a rate of /sec. The video +// update will wait for this timer to trigger before updating. +// +// Pass 0 as the limit for unlimited. +// Pass a negative value for the limit to use the value of vid_maxfps. +// +//========================================================================== + +EXTERN_CVAR(Int, vid_maxfps); +EXTERN_CVAR(Bool, cl_capfps); + +#ifndef __APPLE__ +Semaphore FPSLimitSemaphore; + +static void FPSLimitNotify(sigval val) +{ + SEMAPHORE_SIGNAL(FPSLimitSemaphore) +} + +void I_SetFPSLimit(int limit) +{ + static sigevent FPSLimitEvent; + static timer_t FPSLimitTimer; + static bool FPSLimitTimerEnabled = false; + static bool EventSetup = false; + if(!EventSetup) + { + EventSetup = true; + FPSLimitEvent.sigev_notify = SIGEV_THREAD; + FPSLimitEvent.sigev_signo = 0; + FPSLimitEvent.sigev_value.sival_int = 0; + FPSLimitEvent.sigev_notify_function = FPSLimitNotify; + FPSLimitEvent.sigev_notify_attributes = NULL; + + SEMAPHORE_INIT(FPSLimitSemaphore, 0, 0) + } + + if (limit < 0) + { + limit = vid_maxfps; + } + // Kill any leftover timer. + if (FPSLimitTimerEnabled) + { + timer_delete(FPSLimitTimer); + FPSLimitTimerEnabled = false; + } + if (limit == 0) + { // no limit + DPrintf("FPS timer disabled\n"); + } + else + { + FPSLimitTimerEnabled = true; + if(timer_create(CLOCK_REALTIME, &FPSLimitEvent, &FPSLimitTimer) == -1) + Printf("Failed to create FPS limitter event\n"); + itimerspec period = { {0, 0}, {0, 0} }; + period.it_value.tv_nsec = period.it_interval.tv_nsec = 1000000000 / limit; + if(timer_settime(FPSLimitTimer, 0, &period, NULL) == -1) + Printf("Failed to set FPS limitter timer\n"); + DPrintf("FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000); + } +} +#else +// So Apple doesn't support POSIX timers and I can't find a good substitute short of +// having Objective-C Cocoa events or something like that. +void I_SetFPSLimit(int limit) +{ +} +#endif + +CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (vid_maxfps < TICRATE && vid_maxfps != 0) + { + vid_maxfps = TICRATE; + } + else if (vid_maxfps > 1000) + { + vid_maxfps = 1000; + } + else if (cl_capfps == 0) + { + I_SetFPSLimit(vid_maxfps); + } +} + +extern int NewWidth, NewHeight, NewBits, DisplayBits; + +CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + +CUSTOM_CVAR (Float, vid_winscale, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 1.f) + { + self = 1.f; + } + else if (Video) + { + Video->SetWindowedScale (self); + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; + } +} + +CCMD (vid_listmodes) +{ + static const char *ratios[5] = { "", " - 16:9", " - 16:10", "", " - 5:4" }; + int width, height, bits; + bool letterbox; + + if (Video == NULL) + { + return; + } + for (bits = 1; bits <= 32; bits++) + { + Video->StartModeIterator (bits, screen->IsFullscreen()); + while (Video->NextMode (&width, &height, &letterbox)) + { + bool thisMode = (width == DisplayWidth && height == DisplayHeight && bits == DisplayBits); + int ratio = CheckRatio (width, height); + Printf (thisMode ? PRINT_BOLD : PRINT_HIGH, + "%s%4d x%5d x%3d%s%s\n", + thisMode || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, + width, height, bits, + ratios[ratio], + thisMode || !letterbox ? "" : TEXTCOLOR_BROWN " LB" + ); + } + } +} + +CCMD (vid_currentmode) +{ + Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); +} diff --git a/src/sdl/i_gui.cpp b/src/posix/sdl/i_gui.cpp similarity index 100% rename from src/sdl/i_gui.cpp rename to src/posix/sdl/i_gui.cpp diff --git a/src/sdl/i_input.cpp b/src/posix/sdl/i_input.cpp similarity index 97% rename from src/sdl/i_input.cpp rename to src/posix/sdl/i_input.cpp index 210cf2e2c..6fff2d2a9 100644 --- a/src/sdl/i_input.cpp +++ b/src/posix/sdl/i_input.cpp @@ -1,514 +1,514 @@ -#include -#include -#include "doomtype.h" -#include "c_dispatch.h" -#include "doomdef.h" -#include "doomstat.h" -#include "m_argv.h" -#include "i_input.h" -#include "v_video.h" - -#include "d_main.h" -#include "d_event.h" -#include "d_gui.h" -#include "c_console.h" -#include "c_cvars.h" -#include "i_system.h" -#include "dikeys.h" -#include "templates.h" -#include "s_sound.h" - -void ScaleWithAspect (int &w, int &h, int Width, int Height); - -static void I_CheckGUICapture (); -static void I_CheckNativeMouse (); - -bool GUICapture; -static bool NativeMouse = true; - -extern int paused; - -CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -EXTERN_CVAR (Bool, fullscreen) - -extern int WaitingForKey, chatmodeon; -extern constate_e ConsoleState; - -static bool DownState[SDL_NUM_SCANCODES]; - -static const SDL_Keycode DIKToKeySym[256] = -{ - 0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, - SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB, - SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, - SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s, - SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, - SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v, - SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY, - SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, - SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7, - SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1, - SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11, - SDLK_F12, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0, - 0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0, - 0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ, - SDLK_RALT, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME, - SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END, - SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0, - 0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP, - 0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH, - SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const SDL_Scancode DIKToKeyScan[256] = -{ - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, - SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, - SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, - SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, - SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, - SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, - SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY, - SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, - SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7, - SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1, - SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11, - SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ, - SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME, - SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END, - SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH, - SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN -}; - -static TMap InitKeySymMap () -{ - TMap KeySymToDIK; - - for (int i = 0; i < 256; ++i) - { - KeySymToDIK[DIKToKeySym[i]] = i; - } - KeySymToDIK[0] = 0; - KeySymToDIK[SDLK_RSHIFT] = DIK_LSHIFT; - KeySymToDIK[SDLK_RCTRL] = DIK_LCONTROL; - KeySymToDIK[SDLK_RALT] = DIK_LMENU; - // Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ - KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ; - - return KeySymToDIK; -} -static const TMap KeySymToDIK(InitKeySymMap()); - -static TMap InitKeyScanMap () -{ - TMap KeyScanToDIK; - - for (int i = 0; i < 256; ++i) - { - KeyScanToDIK[DIKToKeyScan[i]] = i; - } - - return KeyScanToDIK; -} -static const TMap KeyScanToDIK(InitKeyScanMap()); - -static void I_CheckGUICapture () -{ - bool wantCapt; - - if (menuactive == MENU_Off) - { - wantCapt = ConsoleState == c_down || ConsoleState == c_falling || chatmodeon; - } - else - { - wantCapt = (menuactive == MENU_On || menuactive == MENU_OnNoPause); - } - - if (wantCapt != GUICapture) - { - GUICapture = wantCapt; - if (wantCapt) - { - memset (DownState, 0, sizeof(DownState)); - } - } -} - -void I_SetMouseCapture() -{ - // Clear out any mouse movement. - SDL_GetRelativeMouseState (NULL, NULL); - SDL_SetRelativeMouseMode (SDL_TRUE); -} - -void I_ReleaseMouseCapture() -{ - SDL_SetRelativeMouseMode (SDL_FALSE); -} - -static void PostMouseMove (int x, int y) -{ - static int lastx = 0, lasty = 0; - event_t ev = { 0,0,0,0,0,0,0 }; - - if (m_filter) - { - ev.x = (x + lastx) / 2; - ev.y = (y + lasty) / 2; - } - else - { - ev.x = x; - ev.y = y; - } - lastx = x; - lasty = y; - if (ev.x | ev.y) - { - ev.type = EV_Mouse; - D_PostEvent (&ev); - } -} - -static void MouseRead () -{ - int x, y; - - if (NativeMouse) - { - return; - } - - SDL_GetRelativeMouseState (&x, &y); - if (!m_noprescale) - { - x *= 3; - y *= 2; - } - if (x | y) - { - PostMouseMove (x, -y); - } -} - -CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) -{ - if (self < 0) self = 0; - else if (self > 2) self = 2; -} - -static bool inGame() -{ - switch (mouse_capturemode) - { - default: - case 0: - return gamestate == GS_LEVEL; - case 1: - return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; - case 2: - return true; - } -} - -static void I_CheckNativeMouse () -{ - bool focus = SDL_GetKeyboardFocus() != NULL; - bool fs = screen->IsFullscreen(); - - bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame()); - - if (wantNative != NativeMouse) - { - NativeMouse = wantNative; - SDL_ShowCursor (wantNative); - if (wantNative) - I_ReleaseMouseCapture (); - else - I_SetMouseCapture (); - } -} - -void MessagePump (const SDL_Event &sev) -{ - static int lastx = 0, lasty = 0; - int x, y; - event_t event = { 0,0,0,0,0,0,0 }; - - switch (sev.type) - { - case SDL_QUIT: - exit (0); - - case SDL_WINDOWEVENT: - switch (sev.window.event) - { - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_FOCUS_LOST: - S_SetSoundPaused(sev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); - break; - } - break; - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEMOTION: - if (!GUICapture || sev.button.button == 4 || sev.button.button == 5) - { - if(sev.type != SDL_MOUSEMOTION) - { - event.type = sev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp; - /* These button mappings work with my Gentoo system using the - * evdev driver and a Logitech MX510 mouse. Whether or not they - * carry over to other Linux systems, I have no idea, but I sure - * hope so. (Though buttons 11 and 12 are kind of useless, since - * they also trigger buttons 4 and 5.) - */ - switch (sev.button.button) - { - case SDL_BUTTON_LEFT: event.data1 = KEY_MOUSE1; break; - case SDL_BUTTON_MIDDLE: event.data1 = KEY_MOUSE3; break; - case SDL_BUTTON_RIGHT: event.data1 = KEY_MOUSE2; break; - case 8: event.data1 = KEY_MOUSE4; break; // For whatever reason my side mouse buttons are here. - case 9: event.data1 = KEY_MOUSE5; break; - case SDL_BUTTON_X1: event.data1 = KEY_MOUSE6; break; // And these don't exist - case SDL_BUTTON_X2: event.data1 = KEY_MOUSE7; break; - case 6: event.data1 = KEY_MOUSE8; break; - default: printf("SDL mouse button %s %d\n", - sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break; - } - if (event.data1 != 0) - { - D_PostEvent(&event); - } - } - } - else if (sev.type == SDL_MOUSEMOTION || (sev.button.button >= 1 && sev.button.button <= 3)) - { - int x, y; - SDL_GetMouseState (&x, &y); - - // Detect if we're doing scaling in the Window and adjust the mouse - // coordinates accordingly. This could be more efficent, but I - // don't think performance is an issue in the menus. - SDL_Window *focus; - if (screen->IsFullscreen() && (focus = SDL_GetMouseFocus ())) - { - int w, h; - SDL_GetWindowSize (focus, &w, &h); - int realw = w, realh = h; - ScaleWithAspect (realw, realh, SCREENWIDTH, SCREENHEIGHT); - if (realw != SCREENWIDTH || realh != SCREENHEIGHT) - { - double xratio = (double)SCREENWIDTH/realw; - double yratio = (double)SCREENHEIGHT/realh; - if (realw < w) - { - x = (x - (w - realw)/2)*xratio; - y *= yratio; - } - else - { - y = (y - (h - realh)/2)*yratio; - x *= xratio; - } - } - } - - event.data1 = x; - event.data2 = y; - event.type = EV_GUI_Event; - if(sev.type == SDL_MOUSEMOTION) - event.subtype = EV_GUI_MouseMove; - else - { - event.subtype = sev.type == SDL_MOUSEBUTTONDOWN ? EV_GUI_LButtonDown : EV_GUI_LButtonUp; - event.subtype += (sev.button.button - 1) * 3; - } - D_PostEvent(&event); - } - break; - - case SDL_MOUSEWHEEL: - if (GUICapture) - { - event.type = EV_GUI_Event; - event.subtype = sev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown; - D_PostEvent (&event); - } - else - { - event.type = EV_KeyDown; - event.data1 = sev.wheel.y > 0 ? KEY_MWHEELUP : KEY_MWHEELDOWN; - D_PostEvent (&event); - event.type = EV_KeyUp; - D_PostEvent (&event); - } - break; - - case SDL_KEYDOWN: - case SDL_KEYUP: - if (!GUICapture) - { - event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp; - - // Try to look up our key mapped key for conversion to DirectInput. - // If that fails, then we'll do a lookup against the scan code, - // which may not return the right key, but at least the key should - // work in the game. - if (const BYTE *dik = KeySymToDIK.CheckKey (sev.key.keysym.sym)) - event.data1 = *dik; - else if (const BYTE *dik = KeyScanToDIK.CheckKey (sev.key.keysym.scancode)) - event.data1 = *dik; - - if (event.data1) - { - if (sev.key.keysym.sym < 256) - { - event.data2 = sev.key.keysym.sym; - } - D_PostEvent (&event); - } - } - else - { - event.type = EV_GUI_Event; - event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp; - event.data3 = ((sev.key.keysym.mod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0); - - if (event.subtype == EV_GUI_KeyDown) - { - if (DownState[sev.key.keysym.scancode]) - { - event.subtype = EV_GUI_KeyRepeat; - } - DownState[sev.key.keysym.scancode] = 1; - } - else - { - DownState[sev.key.keysym.scancode] = 0; - } - - switch (sev.key.keysym.sym) - { - case SDLK_KP_ENTER: event.data1 = GK_RETURN; break; - case SDLK_PAGEUP: event.data1 = GK_PGUP; break; - case SDLK_PAGEDOWN: event.data1 = GK_PGDN; break; - case SDLK_END: event.data1 = GK_END; break; - case SDLK_HOME: event.data1 = GK_HOME; break; - case SDLK_LEFT: event.data1 = GK_LEFT; break; - case SDLK_RIGHT: event.data1 = GK_RIGHT; break; - case SDLK_UP: event.data1 = GK_UP; break; - case SDLK_DOWN: event.data1 = GK_DOWN; break; - case SDLK_DELETE: event.data1 = GK_DEL; break; - case SDLK_ESCAPE: event.data1 = GK_ESCAPE; break; - case SDLK_F1: event.data1 = GK_F1; break; - case SDLK_F2: event.data1 = GK_F2; break; - case SDLK_F3: event.data1 = GK_F3; break; - case SDLK_F4: event.data1 = GK_F4; break; - case SDLK_F5: event.data1 = GK_F5; break; - case SDLK_F6: event.data1 = GK_F6; break; - case SDLK_F7: event.data1 = GK_F7; break; - case SDLK_F8: event.data1 = GK_F8; break; - case SDLK_F9: event.data1 = GK_F9; break; - case SDLK_F10: event.data1 = GK_F10; break; - case SDLK_F11: event.data1 = GK_F11; break; - case SDLK_F12: event.data1 = GK_F12; break; - default: - if (sev.key.keysym.sym < 256) - { - event.data1 = sev.key.keysym.sym; - } - break; - } - if (event.data1 < 128) - { - event.data1 = toupper(event.data1); - D_PostEvent (&event); - } - } - break; - - case SDL_TEXTINPUT: - if (GUICapture) - { - event.type = EV_GUI_Event; - event.subtype = EV_GUI_Char; - event.data1 = sev.text.text[0]; - D_PostEvent (&event); - } - break; - - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - if (!GUICapture) - { - event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp; - event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button; - if(event.data1 != 0) - D_PostEvent(&event); - } - break; - } -} - -void I_GetEvent () -{ - SDL_Event sev; - - while (SDL_PollEvent (&sev)) - { - MessagePump (sev); - } - if (use_mouse) - { - MouseRead (); - } -} - -void I_StartTic () -{ - I_CheckGUICapture (); - I_CheckNativeMouse (); - I_GetEvent (); -} - -void I_ProcessJoysticks (); -void I_StartFrame () -{ - I_ProcessJoysticks(); -} +#include +#include +#include "doomtype.h" +#include "c_dispatch.h" +#include "doomdef.h" +#include "doomstat.h" +#include "m_argv.h" +#include "i_input.h" +#include "v_video.h" + +#include "d_main.h" +#include "d_event.h" +#include "d_gui.h" +#include "c_console.h" +#include "c_cvars.h" +#include "i_system.h" +#include "dikeys.h" +#include "templates.h" +#include "s_sound.h" + +void ScaleWithAspect (int &w, int &h, int Width, int Height); + +static void I_CheckGUICapture (); +static void I_CheckNativeMouse (); + +bool GUICapture; +static bool NativeMouse = true; + +extern int paused; + +CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +EXTERN_CVAR (Bool, fullscreen) + +extern int WaitingForKey, chatmodeon; +extern constate_e ConsoleState; + +static bool DownState[SDL_NUM_SCANCODES]; + +static const SDL_Keycode DIKToKeySym[256] = +{ + 0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, + SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB, + SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, + SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s, + SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, + SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v, + SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY, + SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, + SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7, + SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1, + SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11, + SDLK_F12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0, + 0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0, + 0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ, + SDLK_RALT, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME, + SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END, + SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0, + 0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP, + 0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH, + SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const SDL_Scancode DIKToKeyScan[256] = +{ + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, + SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, + SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, + SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, + SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, + SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, + SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY, + SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, + SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7, + SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1, + SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11, + SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ, + SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME, + SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END, + SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH, + SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN +}; + +static TMap InitKeySymMap () +{ + TMap KeySymToDIK; + + for (int i = 0; i < 256; ++i) + { + KeySymToDIK[DIKToKeySym[i]] = i; + } + KeySymToDIK[0] = 0; + KeySymToDIK[SDLK_RSHIFT] = DIK_LSHIFT; + KeySymToDIK[SDLK_RCTRL] = DIK_LCONTROL; + KeySymToDIK[SDLK_RALT] = DIK_LMENU; + // Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ + KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ; + + return KeySymToDIK; +} +static const TMap KeySymToDIK(InitKeySymMap()); + +static TMap InitKeyScanMap () +{ + TMap KeyScanToDIK; + + for (int i = 0; i < 256; ++i) + { + KeyScanToDIK[DIKToKeyScan[i]] = i; + } + + return KeyScanToDIK; +} +static const TMap KeyScanToDIK(InitKeyScanMap()); + +static void I_CheckGUICapture () +{ + bool wantCapt; + + if (menuactive == MENU_Off) + { + wantCapt = ConsoleState == c_down || ConsoleState == c_falling || chatmodeon; + } + else + { + wantCapt = (menuactive == MENU_On || menuactive == MENU_OnNoPause); + } + + if (wantCapt != GUICapture) + { + GUICapture = wantCapt; + if (wantCapt) + { + memset (DownState, 0, sizeof(DownState)); + } + } +} + +void I_SetMouseCapture() +{ + // Clear out any mouse movement. + SDL_GetRelativeMouseState (NULL, NULL); + SDL_SetRelativeMouseMode (SDL_TRUE); +} + +void I_ReleaseMouseCapture() +{ + SDL_SetRelativeMouseMode (SDL_FALSE); +} + +static void PostMouseMove (int x, int y) +{ + static int lastx = 0, lasty = 0; + event_t ev = { 0,0,0,0,0,0,0 }; + + if (m_filter) + { + ev.x = (x + lastx) / 2; + ev.y = (y + lasty) / 2; + } + else + { + ev.x = x; + ev.y = y; + } + lastx = x; + lasty = y; + if (ev.x | ev.y) + { + ev.type = EV_Mouse; + D_PostEvent (&ev); + } +} + +static void MouseRead () +{ + int x, y; + + if (NativeMouse) + { + return; + } + + SDL_GetRelativeMouseState (&x, &y); + if (!m_noprescale) + { + x *= 3; + y *= 2; + } + if (x | y) + { + PostMouseMove (x, -y); + } +} + +CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +{ + if (self < 0) self = 0; + else if (self > 2) self = 2; +} + +static bool inGame() +{ + switch (mouse_capturemode) + { + default: + case 0: + return gamestate == GS_LEVEL; + case 1: + return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; + case 2: + return true; + } +} + +static void I_CheckNativeMouse () +{ + bool focus = SDL_GetKeyboardFocus() != NULL; + bool fs = screen->IsFullscreen(); + + bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame()); + + if (wantNative != NativeMouse) + { + NativeMouse = wantNative; + SDL_ShowCursor (wantNative); + if (wantNative) + I_ReleaseMouseCapture (); + else + I_SetMouseCapture (); + } +} + +void MessagePump (const SDL_Event &sev) +{ + static int lastx = 0, lasty = 0; + int x, y; + event_t event = { 0,0,0,0,0,0,0 }; + + switch (sev.type) + { + case SDL_QUIT: + exit (0); + + case SDL_WINDOWEVENT: + switch (sev.window.event) + { + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + S_SetSoundPaused(sev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); + break; + } + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEMOTION: + if (!GUICapture || sev.button.button == 4 || sev.button.button == 5) + { + if(sev.type != SDL_MOUSEMOTION) + { + event.type = sev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + /* These button mappings work with my Gentoo system using the + * evdev driver and a Logitech MX510 mouse. Whether or not they + * carry over to other Linux systems, I have no idea, but I sure + * hope so. (Though buttons 11 and 12 are kind of useless, since + * they also trigger buttons 4 and 5.) + */ + switch (sev.button.button) + { + case SDL_BUTTON_LEFT: event.data1 = KEY_MOUSE1; break; + case SDL_BUTTON_MIDDLE: event.data1 = KEY_MOUSE3; break; + case SDL_BUTTON_RIGHT: event.data1 = KEY_MOUSE2; break; + case 8: event.data1 = KEY_MOUSE4; break; // For whatever reason my side mouse buttons are here. + case 9: event.data1 = KEY_MOUSE5; break; + case SDL_BUTTON_X1: event.data1 = KEY_MOUSE6; break; // And these don't exist + case SDL_BUTTON_X2: event.data1 = KEY_MOUSE7; break; + case 6: event.data1 = KEY_MOUSE8; break; + default: printf("SDL mouse button %s %d\n", + sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break; + } + if (event.data1 != 0) + { + D_PostEvent(&event); + } + } + } + else if (sev.type == SDL_MOUSEMOTION || (sev.button.button >= 1 && sev.button.button <= 3)) + { + int x, y; + SDL_GetMouseState (&x, &y); + + // Detect if we're doing scaling in the Window and adjust the mouse + // coordinates accordingly. This could be more efficent, but I + // don't think performance is an issue in the menus. + SDL_Window *focus; + if (screen->IsFullscreen() && (focus = SDL_GetMouseFocus ())) + { + int w, h; + SDL_GetWindowSize (focus, &w, &h); + int realw = w, realh = h; + ScaleWithAspect (realw, realh, SCREENWIDTH, SCREENHEIGHT); + if (realw != SCREENWIDTH || realh != SCREENHEIGHT) + { + double xratio = (double)SCREENWIDTH/realw; + double yratio = (double)SCREENHEIGHT/realh; + if (realw < w) + { + x = (x - (w - realw)/2)*xratio; + y *= yratio; + } + else + { + y = (y - (h - realh)/2)*yratio; + x *= xratio; + } + } + } + + event.data1 = x; + event.data2 = y; + event.type = EV_GUI_Event; + if(sev.type == SDL_MOUSEMOTION) + event.subtype = EV_GUI_MouseMove; + else + { + event.subtype = sev.type == SDL_MOUSEBUTTONDOWN ? EV_GUI_LButtonDown : EV_GUI_LButtonUp; + event.subtype += (sev.button.button - 1) * 3; + } + D_PostEvent(&event); + } + break; + + case SDL_MOUSEWHEEL: + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = sev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown; + D_PostEvent (&event); + } + else + { + event.type = EV_KeyDown; + event.data1 = sev.wheel.y > 0 ? KEY_MWHEELUP : KEY_MWHEELDOWN; + D_PostEvent (&event); + event.type = EV_KeyUp; + D_PostEvent (&event); + } + break; + + case SDL_KEYDOWN: + case SDL_KEYUP: + if (!GUICapture) + { + event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp; + + // Try to look up our key mapped key for conversion to DirectInput. + // If that fails, then we'll do a lookup against the scan code, + // which may not return the right key, but at least the key should + // work in the game. + if (const BYTE *dik = KeySymToDIK.CheckKey (sev.key.keysym.sym)) + event.data1 = *dik; + else if (const BYTE *dik = KeyScanToDIK.CheckKey (sev.key.keysym.scancode)) + event.data1 = *dik; + + if (event.data1) + { + if (sev.key.keysym.sym < 256) + { + event.data2 = sev.key.keysym.sym; + } + D_PostEvent (&event); + } + } + else + { + event.type = EV_GUI_Event; + event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp; + event.data3 = ((sev.key.keysym.mod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0); + + if (event.subtype == EV_GUI_KeyDown) + { + if (DownState[sev.key.keysym.scancode]) + { + event.subtype = EV_GUI_KeyRepeat; + } + DownState[sev.key.keysym.scancode] = 1; + } + else + { + DownState[sev.key.keysym.scancode] = 0; + } + + switch (sev.key.keysym.sym) + { + case SDLK_KP_ENTER: event.data1 = GK_RETURN; break; + case SDLK_PAGEUP: event.data1 = GK_PGUP; break; + case SDLK_PAGEDOWN: event.data1 = GK_PGDN; break; + case SDLK_END: event.data1 = GK_END; break; + case SDLK_HOME: event.data1 = GK_HOME; break; + case SDLK_LEFT: event.data1 = GK_LEFT; break; + case SDLK_RIGHT: event.data1 = GK_RIGHT; break; + case SDLK_UP: event.data1 = GK_UP; break; + case SDLK_DOWN: event.data1 = GK_DOWN; break; + case SDLK_DELETE: event.data1 = GK_DEL; break; + case SDLK_ESCAPE: event.data1 = GK_ESCAPE; break; + case SDLK_F1: event.data1 = GK_F1; break; + case SDLK_F2: event.data1 = GK_F2; break; + case SDLK_F3: event.data1 = GK_F3; break; + case SDLK_F4: event.data1 = GK_F4; break; + case SDLK_F5: event.data1 = GK_F5; break; + case SDLK_F6: event.data1 = GK_F6; break; + case SDLK_F7: event.data1 = GK_F7; break; + case SDLK_F8: event.data1 = GK_F8; break; + case SDLK_F9: event.data1 = GK_F9; break; + case SDLK_F10: event.data1 = GK_F10; break; + case SDLK_F11: event.data1 = GK_F11; break; + case SDLK_F12: event.data1 = GK_F12; break; + default: + if (sev.key.keysym.sym < 256) + { + event.data1 = sev.key.keysym.sym; + } + break; + } + if (event.data1 < 128) + { + event.data1 = toupper(event.data1); + D_PostEvent (&event); + } + } + break; + + case SDL_TEXTINPUT: + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = EV_GUI_Char; + event.data1 = sev.text.text[0]; + D_PostEvent (&event); + } + break; + + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + if (!GUICapture) + { + event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button; + if(event.data1 != 0) + D_PostEvent(&event); + } + break; + } +} + +void I_GetEvent () +{ + SDL_Event sev; + + while (SDL_PollEvent (&sev)) + { + MessagePump (sev); + } + if (use_mouse) + { + MouseRead (); + } +} + +void I_StartTic () +{ + I_CheckGUICapture (); + I_CheckNativeMouse (); + I_GetEvent (); +} + +void I_ProcessJoysticks (); +void I_StartFrame () +{ + I_ProcessJoysticks(); +} diff --git a/src/sdl/i_joystick.cpp b/src/posix/sdl/i_joystick.cpp similarity index 100% rename from src/sdl/i_joystick.cpp rename to src/posix/sdl/i_joystick.cpp diff --git a/src/sdl/i_main.cpp b/src/posix/sdl/i_main.cpp similarity index 96% rename from src/sdl/i_main.cpp rename to src/posix/sdl/i_main.cpp index 10a9b8c75..ff700eff4 100644 --- a/src/sdl/i_main.cpp +++ b/src/posix/sdl/i_main.cpp @@ -1,338 +1,338 @@ -/* -** i_main.cpp -** System-specific startup code. Eventually calls D_DoomMain. -** -**--------------------------------------------------------------------------- -** Copyright 1998-2007 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -// HEADER FILES ------------------------------------------------------------ - -#include -#include -#include -#include -#include -#include -#ifndef NO_GTK -#include -#endif -#include -#if defined(__MACH__) && !defined(NOASM) -#include -#include -#endif - -#include "doomerrors.h" -#include "m_argv.h" -#include "d_main.h" -#include "i_system.h" -#include "i_video.h" -#include "c_console.h" -#include "errors.h" -#include "version.h" -#include "w_wad.h" -#include "g_level.h" -#include "r_state.h" -#include "cmdlib.h" -#include "r_utility.h" -#include "doomstat.h" - -// MACROS ------------------------------------------------------------------ - -// The maximum number of functions that can be registered with atterm. -#define MAX_TERMS 64 - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -extern "C" int cc_install_handlers(int, char**, int, int*, const char*, int(*)(char*, char*)); - -#ifdef __APPLE__ -void Mac_I_FatalError(const char* errortext); -#endif - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -#ifndef NO_GTK -bool GtkAvailable; -#endif - -// The command line arguments. -DArgs *Args; - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static void (*TermFuncs[MAX_TERMS]) (); -static const char *TermNames[MAX_TERMS]; -static int NumTerms; - -// CODE -------------------------------------------------------------------- - -void addterm (void (*func) (), const char *name) -{ - // Make sure this function wasn't already registered. - for (int i = 0; i < NumTerms; ++i) - { - if (TermFuncs[i] == func) - { - return; - } - } - if (NumTerms == MAX_TERMS) - { - func (); - I_FatalError ( - "Too many exit functions registered.\n" - "Increase MAX_TERMS in i_main.cpp"); - } - TermNames[NumTerms] = name; - TermFuncs[NumTerms++] = func; -} - -void popterm () -{ - if (NumTerms) - NumTerms--; -} - -void STACK_ARGS call_terms () -{ - while (NumTerms > 0) - { -// printf ("term %d - %s\n", NumTerms, TermNames[NumTerms-1]); - TermFuncs[--NumTerms] (); - } -} - -static void STACK_ARGS NewFailure () -{ - I_FatalError ("Failed to allocate memory from system heap"); -} - -static int DoomSpecificInfo (char *buffer, char *end) -{ - const char *arg; - int size = end-buffer-2; - int i, p; - - p = 0; - p += snprintf (buffer+p, size-p, GAMENAME" version %s (%s)\n", GetVersionString(), GetGitHash()); -#ifdef __VERSION__ - p += snprintf (buffer+p, size-p, "Compiler version: %s\n", __VERSION__); -#endif - p += snprintf (buffer+p, size-p, "\nCommand line:"); - for (i = 0; i < Args->NumArgs(); ++i) - { - p += snprintf (buffer+p, size-p, " %s", Args->GetArg(i)); - } - p += snprintf (buffer+p, size-p, "\n"); - - for (i = 0; (arg = Wads.GetWadName (i)) != NULL; ++i) - { - p += snprintf (buffer+p, size-p, "\nWad %d: %s", i, arg); - } - - if (gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL) - { - p += snprintf (buffer+p, size-p, "\n\nNot in a level."); - } - else - { - p += snprintf (buffer+p, size-p, "\n\nCurrent map: %s", level.MapName.GetChars()); - - if (!viewactive) - { - p += snprintf (buffer+p, size-p, "\n\nView not active."); - } - else - { - p += snprintf (buffer+p, size-p, "\n\nviewx = %d", (int)viewx); - p += snprintf (buffer+p, size-p, "\nviewy = %d", (int)viewy); - p += snprintf (buffer+p, size-p, "\nviewz = %d", (int)viewz); - p += snprintf (buffer+p, size-p, "\nviewangle = %x", (unsigned int)viewangle); - } - } - buffer[p++] = '\n'; - buffer[p++] = '\0'; - - return p; -} - -#if defined(__MACH__) && !defined(NOASM) -// NASM won't let us create custom sections for Mach-O. Whether that's a limitation of NASM -// or of Mach-O, I don't know, but since we're using NASM for the assembly, it doesn't much -// matter. -extern "C" -{ - extern void *rtext_a_start, *rtext_a_end; - extern void *rtext_tmap_start, *rtext_tmap_end; - extern void *rtext_tmap2_start, *rtext_tmap2_end; - extern void *rtext_tmap3_start, *rtext_tmap3_end; -}; - -static void unprotect_pages(long pagesize, void *start, void *end) -{ - char *page = (char *)((intptr_t)start & ~(pagesize - 1)); - size_t len = (char *)end - (char *)start; - if (mprotect(page, len, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) - { - fprintf(stderr, "mprotect failed\n"); - exit(1); - } -} - -static void unprotect_rtext() -{ - static void *const pages[] = - { - rtext_a_start, rtext_a_end, - rtext_tmap_start, rtext_tmap_end, - rtext_tmap2_start, rtext_tmap2_end, - rtext_tmap3_start, rtext_tmap3_end - }; - long pagesize = sysconf(_SC_PAGESIZE); - for (void *const *p = pages; p < &pages[countof(pages)]; p += 2) - { - unprotect_pages(pagesize, p[0], p[1]); - } -} -#endif - -void I_StartupJoysticks(); -void I_ShutdownJoysticks(); - -int main (int argc, char **argv) -{ -#if !defined (__APPLE__) - { - int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS }; - cc_install_handlers(argc, argv, 4, s, "zdoom-crash.log", DoomSpecificInfo); - } -#endif // !__APPLE__ - - printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n", - GetVersionString(), GetGitTime(), __DATE__); - - seteuid (getuid ()); - std::set_new_handler (NewFailure); - -#if defined(__MACH__) && !defined(NOASM) - unprotect_rtext(); -#endif - - // Set LC_NUMERIC environment variable in case some library decides to - // clear the setlocale call at least this will be correct. - // Note that the LANG environment variable is overridden by LC_* - setenv ("LC_NUMERIC", "C", 1); - -#ifndef NO_GTK - GtkAvailable = gtk_init_check (&argc, &argv); -#endif - - setlocale (LC_ALL, "C"); - - if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE|SDL_INIT_JOYSTICK) == -1) - { - fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError()); - return -1; - } - atterm (SDL_Quit); - - printf("Using video driver %s\n", SDL_GetCurrentVideoDriver()); - printf("\n"); - - try - { - Args = new DArgs(argc, argv); - - /* - killough 1/98: - - This fixes some problems with exit handling - during abnormal situations. - - The old code called I_Quit() to end program, - while now I_Quit() is installed as an exit - handler and exit() is called to exit, either - normally or abnormally. Seg faults are caught - and the error handler is used, to prevent - being left in graphics mode or having very - loud SFX noise because the sound card is - left in an unstable state. - */ - - atexit (call_terms); - atterm (I_Quit); - - // Should we even be doing anything with progdir on Unix systems? - char program[PATH_MAX]; - if (realpath (argv[0], program) == NULL) - strcpy (program, argv[0]); - char *slash = strrchr (program, '/'); - if (slash != NULL) - { - *(slash + 1) = '\0'; - progdir = program; - } - else - { - progdir = "./"; - } - - I_StartupJoysticks(); - C_InitConsole (80*8, 25*8, false); - D_DoomMain (); - } - catch (class CDoomError &error) - { - I_ShutdownJoysticks(); - if (error.GetMessage ()) - fprintf (stderr, "%s\n", error.GetMessage ()); - -#ifdef __APPLE__ - Mac_I_FatalError(error.GetMessage()); -#endif // __APPLE__ - - exit (-1); - } - catch (...) - { - call_terms (); - throw; - } - return 0; -} +/* +** i_main.cpp +** System-specific startup code. Eventually calls D_DoomMain. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2007 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#ifndef NO_GTK +#include +#endif +#include +#if defined(__MACH__) && !defined(NOASM) +#include +#include +#endif + +#include "doomerrors.h" +#include "m_argv.h" +#include "d_main.h" +#include "i_system.h" +#include "i_video.h" +#include "c_console.h" +#include "errors.h" +#include "version.h" +#include "w_wad.h" +#include "g_level.h" +#include "r_state.h" +#include "cmdlib.h" +#include "r_utility.h" +#include "doomstat.h" + +// MACROS ------------------------------------------------------------------ + +// The maximum number of functions that can be registered with atterm. +#define MAX_TERMS 64 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +extern "C" int cc_install_handlers(int, char**, int, int*, const char*, int(*)(char*, char*)); + +#ifdef __APPLE__ +void Mac_I_FatalError(const char* errortext); +#endif + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +#ifndef NO_GTK +bool GtkAvailable; +#endif + +// The command line arguments. +DArgs *Args; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static void (*TermFuncs[MAX_TERMS]) (); +static const char *TermNames[MAX_TERMS]; +static int NumTerms; + +// CODE -------------------------------------------------------------------- + +void addterm (void (*func) (), const char *name) +{ + // Make sure this function wasn't already registered. + for (int i = 0; i < NumTerms; ++i) + { + if (TermFuncs[i] == func) + { + return; + } + } + if (NumTerms == MAX_TERMS) + { + func (); + I_FatalError ( + "Too many exit functions registered.\n" + "Increase MAX_TERMS in i_main.cpp"); + } + TermNames[NumTerms] = name; + TermFuncs[NumTerms++] = func; +} + +void popterm () +{ + if (NumTerms) + NumTerms--; +} + +void STACK_ARGS call_terms () +{ + while (NumTerms > 0) + { +// printf ("term %d - %s\n", NumTerms, TermNames[NumTerms-1]); + TermFuncs[--NumTerms] (); + } +} + +static void STACK_ARGS NewFailure () +{ + I_FatalError ("Failed to allocate memory from system heap"); +} + +static int DoomSpecificInfo (char *buffer, char *end) +{ + const char *arg; + int size = end-buffer-2; + int i, p; + + p = 0; + p += snprintf (buffer+p, size-p, GAMENAME" version %s (%s)\n", GetVersionString(), GetGitHash()); +#ifdef __VERSION__ + p += snprintf (buffer+p, size-p, "Compiler version: %s\n", __VERSION__); +#endif + p += snprintf (buffer+p, size-p, "\nCommand line:"); + for (i = 0; i < Args->NumArgs(); ++i) + { + p += snprintf (buffer+p, size-p, " %s", Args->GetArg(i)); + } + p += snprintf (buffer+p, size-p, "\n"); + + for (i = 0; (arg = Wads.GetWadName (i)) != NULL; ++i) + { + p += snprintf (buffer+p, size-p, "\nWad %d: %s", i, arg); + } + + if (gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL) + { + p += snprintf (buffer+p, size-p, "\n\nNot in a level."); + } + else + { + p += snprintf (buffer+p, size-p, "\n\nCurrent map: %s", level.MapName.GetChars()); + + if (!viewactive) + { + p += snprintf (buffer+p, size-p, "\n\nView not active."); + } + else + { + p += snprintf (buffer+p, size-p, "\n\nviewx = %d", (int)viewx); + p += snprintf (buffer+p, size-p, "\nviewy = %d", (int)viewy); + p += snprintf (buffer+p, size-p, "\nviewz = %d", (int)viewz); + p += snprintf (buffer+p, size-p, "\nviewangle = %x", (unsigned int)viewangle); + } + } + buffer[p++] = '\n'; + buffer[p++] = '\0'; + + return p; +} + +#if defined(__MACH__) && !defined(NOASM) +// NASM won't let us create custom sections for Mach-O. Whether that's a limitation of NASM +// or of Mach-O, I don't know, but since we're using NASM for the assembly, it doesn't much +// matter. +extern "C" +{ + extern void *rtext_a_start, *rtext_a_end; + extern void *rtext_tmap_start, *rtext_tmap_end; + extern void *rtext_tmap2_start, *rtext_tmap2_end; + extern void *rtext_tmap3_start, *rtext_tmap3_end; +}; + +static void unprotect_pages(long pagesize, void *start, void *end) +{ + char *page = (char *)((intptr_t)start & ~(pagesize - 1)); + size_t len = (char *)end - (char *)start; + if (mprotect(page, len, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) + { + fprintf(stderr, "mprotect failed\n"); + exit(1); + } +} + +static void unprotect_rtext() +{ + static void *const pages[] = + { + rtext_a_start, rtext_a_end, + rtext_tmap_start, rtext_tmap_end, + rtext_tmap2_start, rtext_tmap2_end, + rtext_tmap3_start, rtext_tmap3_end + }; + long pagesize = sysconf(_SC_PAGESIZE); + for (void *const *p = pages; p < &pages[countof(pages)]; p += 2) + { + unprotect_pages(pagesize, p[0], p[1]); + } +} +#endif + +void I_StartupJoysticks(); +void I_ShutdownJoysticks(); + +int main (int argc, char **argv) +{ +#if !defined (__APPLE__) + { + int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS }; + cc_install_handlers(argc, argv, 4, s, "zdoom-crash.log", DoomSpecificInfo); + } +#endif // !__APPLE__ + + printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n", + GetVersionString(), GetGitTime(), __DATE__); + + seteuid (getuid ()); + std::set_new_handler (NewFailure); + +#if defined(__MACH__) && !defined(NOASM) + unprotect_rtext(); +#endif + + // Set LC_NUMERIC environment variable in case some library decides to + // clear the setlocale call at least this will be correct. + // Note that the LANG environment variable is overridden by LC_* + setenv ("LC_NUMERIC", "C", 1); + +#ifndef NO_GTK + GtkAvailable = gtk_init_check (&argc, &argv); +#endif + + setlocale (LC_ALL, "C"); + + if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE|SDL_INIT_JOYSTICK) == -1) + { + fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError()); + return -1; + } + atterm (SDL_Quit); + + printf("Using video driver %s\n", SDL_GetCurrentVideoDriver()); + printf("\n"); + + try + { + Args = new DArgs(argc, argv); + + /* + killough 1/98: + + This fixes some problems with exit handling + during abnormal situations. + + The old code called I_Quit() to end program, + while now I_Quit() is installed as an exit + handler and exit() is called to exit, either + normally or abnormally. Seg faults are caught + and the error handler is used, to prevent + being left in graphics mode or having very + loud SFX noise because the sound card is + left in an unstable state. + */ + + atexit (call_terms); + atterm (I_Quit); + + // Should we even be doing anything with progdir on Unix systems? + char program[PATH_MAX]; + if (realpath (argv[0], program) == NULL) + strcpy (program, argv[0]); + char *slash = strrchr (program, '/'); + if (slash != NULL) + { + *(slash + 1) = '\0'; + progdir = program; + } + else + { + progdir = "./"; + } + + I_StartupJoysticks(); + C_InitConsole (80*8, 25*8, false); + D_DoomMain (); + } + catch (class CDoomError &error) + { + I_ShutdownJoysticks(); + if (error.GetMessage ()) + fprintf (stderr, "%s\n", error.GetMessage ()); + +#ifdef __APPLE__ + Mac_I_FatalError(error.GetMessage()); +#endif // __APPLE__ + + exit (-1); + } + catch (...) + { + call_terms (); + throw; + } + return 0; +} diff --git a/src/sdl/i_system_cocoa.mm b/src/posix/sdl/i_system.mm similarity index 100% rename from src/sdl/i_system_cocoa.mm rename to src/posix/sdl/i_system.mm diff --git a/src/sdl/i_timer.cpp b/src/posix/sdl/i_timer.cpp similarity index 100% rename from src/sdl/i_timer.cpp rename to src/posix/sdl/i_timer.cpp diff --git a/src/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp similarity index 95% rename from src/sdl/sdlvideo.cpp rename to src/posix/sdl/sdlvideo.cpp index e477df526..7a04ce901 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -1,732 +1,732 @@ - -// HEADER FILES ------------------------------------------------------------ - -#include "doomtype.h" - -#include "templates.h" -#include "i_system.h" -#include "i_video.h" -#include "v_video.h" -#include "v_pfx.h" -#include "stats.h" -#include "v_palette.h" -#include "sdlvideo.h" -#include "r_swrenderer.h" -#include "version.h" - -#include - -#ifdef __APPLE__ -#include -#endif // __APPLE__ - -// MACROS ------------------------------------------------------------------ - -// TYPES ------------------------------------------------------------------- - -class SDLFB : public DFrameBuffer -{ - DECLARE_CLASS(SDLFB, DFrameBuffer) -public: - SDLFB (int width, int height, bool fullscreen); - ~SDLFB (); - - bool Lock (bool buffer); - void Unlock (); - bool Relock (); - void ForceBuffering (bool force); - bool IsValid (); - void Update (); - PalEntry *GetPalette (); - void GetFlashedPalette (PalEntry pal[256]); - void UpdatePalette (); - bool SetGamma (float gamma); - bool SetFlash (PalEntry rgb, int amount); - void GetFlash (PalEntry &rgb, int &amount); - void SetFullscreen (bool fullscreen); - int GetPageCount (); - bool IsFullscreen (); - - friend class SDLVideo; - - virtual void SetVSync (bool vsync); - -private: - PalEntry SourcePalette[256]; - BYTE GammaTable[3][256]; - PalEntry Flash; - int FlashAmount; - float Gamma; - bool UpdatePending; - - SDL_Window *Screen; - SDL_Renderer *Renderer; - union - { - SDL_Texture *Texture; - SDL_Surface *Surface; - }; - SDL_Rect UpdateRect; - - bool UsingRenderer; - bool NeedPalUpdate; - bool NeedGammaUpdate; - bool NotPaletted; - - void UpdateColors (); - void ResetSDLRenderer (); - - SDLFB () {} -}; -IMPLEMENT_CLASS(SDLFB) - -struct MiniModeInfo -{ - WORD Width, Height; -}; - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -extern IVideo *Video; -extern bool GUICapture; - -EXTERN_CVAR (Float, Gamma) -EXTERN_CVAR (Int, vid_maxfps) -EXTERN_CVAR (Bool, cl_capfps) -EXTERN_CVAR (Bool, vid_vsync) - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -// Dummy screen sizes to pass when windowed -static MiniModeInfo WinModes[] = -{ - { 320, 200 }, - { 320, 240 }, - { 400, 225 }, // 16:9 - { 400, 300 }, - { 480, 270 }, // 16:9 - { 480, 360 }, - { 512, 288 }, // 16:9 - { 512, 384 }, - { 640, 360 }, // 16:9 - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, // 16:10 - { 720, 540 }, - { 800, 450 }, // 16:9 - { 800, 480 }, - { 800, 500 }, // 16:10 - { 800, 600 }, - { 848, 480 }, // 16:9 - { 960, 600 }, // 16:10 - { 960, 720 }, - { 1024, 576 }, // 16:9 - { 1024, 600 }, // 17:10 - { 1024, 640 }, // 16:10 - { 1024, 768 }, - { 1088, 612 }, // 16:9 - { 1152, 648 }, // 16:9 - { 1152, 720 }, // 16:10 - { 1152, 864 }, - { 1280, 720 }, // 16:9 - { 1280, 854 }, - { 1280, 800 }, // 16:10 - { 1280, 960 }, - { 1280, 1024 }, // 5:4 - { 1360, 768 }, // 16:9 - { 1366, 768 }, - { 1400, 787 }, // 16:9 - { 1400, 875 }, // 16:10 - { 1400, 1050 }, - { 1440, 900 }, - { 1440, 960 }, - { 1440, 1080 }, - { 1600, 900 }, // 16:9 - { 1600, 1000 }, // 16:10 - { 1600, 1200 }, - { 1920, 1080 }, - { 1920, 1200 }, - { 2048, 1536 }, - { 2560, 1440 }, - { 2560, 1600 }, - { 2560, 2048 }, - { 2880, 1800 }, - { 3200, 1800 }, - { 3840, 2160 }, - { 3840, 2400 }, - { 4096, 2160 }, - { 5120, 2880 } -}; - -static cycle_t BlitCycles; -static cycle_t SDLFlipCycles; - -// CODE -------------------------------------------------------------------- - -void ScaleWithAspect (int &w, int &h, int Width, int Height) -{ - int resRatio = CheckRatio (Width, Height); - int screenRatio; - CheckRatio (w, h, &screenRatio); - if (resRatio == screenRatio) - return; - - double yratio; - switch(resRatio) - { - case 0: yratio = 4./3.; break; - case 1: yratio = 16./9.; break; - case 2: yratio = 16./10.; break; - case 3: yratio = 17./10.; break; - case 4: yratio = 5./4.; break; - default: return; - } - double y = w/yratio; - if (y > h) - w = h*yratio; - else - h = y; -} - -SDLVideo::SDLVideo (int parm) -{ - IteratorBits = 0; -} - -SDLVideo::~SDLVideo () -{ -} - -void SDLVideo::StartModeIterator (int bits, bool fs) -{ - IteratorMode = 0; - IteratorBits = bits; -} - -bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) -{ - if (IteratorBits != 8) - return false; - - if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) - { - *width = WinModes[IteratorMode].Width; - *height = WinModes[IteratorMode].Height; - ++IteratorMode; - return true; - } - return false; -} - -DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) -{ - static int retry = 0; - static int owidth, oheight; - - PalEntry flashColor; - int flashAmount; - - if (old != NULL) - { // Reuse the old framebuffer if its attributes are the same - SDLFB *fb = static_cast (old); - if (fb->Width == width && - fb->Height == height) - { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; - - if (fsnow != fullscreen) - { - fb->SetFullscreen (fullscreen); - } - return old; - } - old->GetFlash (flashColor, flashAmount); - old->ObjectFlags |= OF_YesReallyDelete; - if (screen == old) screen = NULL; - delete old; - } - else - { - flashColor = 0; - flashAmount = 0; - } - - SDLFB *fb = new SDLFB (width, height, fullscreen); - retry = 0; - - // If we could not create the framebuffer, try again with slightly - // different parameters in this order: - // 1. Try with the closest size - // 2. Try in the opposite screen mode with the original size - // 3. Try in the opposite screen mode with the closest size - // This is a somewhat confusing mass of recursion here. - - while (fb == NULL || !fb->IsValid ()) - { - if (fb != NULL) - { - delete fb; - } - - switch (retry) - { - case 0: - owidth = width; - oheight = height; - case 2: - // Try a different resolution. Hopefully that will work. - I_ClosestResolution (&width, &height, 8); - break; - - case 1: - // Try changing fullscreen mode. Maybe that will work. - width = owidth; - height = oheight; - fullscreen = !fullscreen; - break; - - default: - // I give up! - I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); - } - - ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); - } - - fb->SetFlash (flashColor, flashAmount); - - return fb; -} - -void SDLVideo::SetWindowedScale (float scale) -{ -} - -// FrameBuffer implementation ----------------------------------------------- - -SDLFB::SDLFB (int width, int height, bool fullscreen) - : DFrameBuffer (width, height) -{ - int i; - - NeedPalUpdate = false; - NeedGammaUpdate = false; - UpdatePending = false; - NotPaletted = false; - FlashAmount = 0; - - FString caption; - caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); - - Screen = SDL_CreateWindow (caption, - SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), - width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); - - if (Screen == NULL) - return; - - Renderer = NULL; - Texture = NULL; - ResetSDLRenderer (); - - for (i = 0; i < 256; i++) - { - GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; - } - - memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); - UpdateColors (); - -#ifdef __APPLE__ - SetVSync (vid_vsync); -#endif -} - - -SDLFB::~SDLFB () -{ - if(Screen) - { - if (Renderer) - { - if (Texture) - SDL_DestroyTexture (Texture); - SDL_DestroyRenderer (Renderer); - } - - SDL_DestroyWindow (Screen); - } -} - -bool SDLFB::IsValid () -{ - return DFrameBuffer::IsValid() && Screen != NULL; -} - -int SDLFB::GetPageCount () -{ - return 1; -} - -bool SDLFB::Lock (bool buffered) -{ - return DSimpleCanvas::Lock (); -} - -bool SDLFB::Relock () -{ - return DSimpleCanvas::Lock (); -} - -void SDLFB::Unlock () -{ - if (UpdatePending && LockCount == 1) - { - Update (); - } - else if (--LockCount <= 0) - { - Buffer = NULL; - LockCount = 0; - } -} - -void SDLFB::Update () -{ - if (LockCount != 1) - { - if (LockCount > 0) - { - UpdatePending = true; - --LockCount; - } - return; - } - - DrawRateStuff (); - -#ifndef __APPLE__ - if(vid_maxfps && !cl_capfps) - { - SEMAPHORE_WAIT(FPSLimitSemaphore) - } -#endif - - Buffer = NULL; - LockCount = 0; - UpdatePending = false; - - BlitCycles.Reset(); - SDLFlipCycles.Reset(); - BlitCycles.Clock(); - - void *pixels; - int pitch; - if (UsingRenderer) - { - if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) - return; - } - else - { - if (SDL_LockSurface (Surface)) - return; - - pixels = Surface->pixels; - pitch = Surface->pitch; - } - - if (NotPaletted) - { - GPfx.Convert (MemBuffer, Pitch, - pixels, pitch, Width, Height, - FRACUNIT, FRACUNIT, 0, 0); - } - else - { - if (pitch == Pitch) - { - memcpy (pixels, MemBuffer, Width*Height); - } - else - { - for (int y = 0; y < Height; ++y) - { - memcpy ((BYTE *)pixels+y*pitch, MemBuffer+y*Pitch, Width); - } - } - } - - if (UsingRenderer) - { - SDL_UnlockTexture (Texture); - - SDLFlipCycles.Clock(); - SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); - SDL_RenderPresent(Renderer); - SDLFlipCycles.Unclock(); - } - else - { - SDL_UnlockSurface (Surface); - - SDLFlipCycles.Clock(); - SDL_UpdateWindowSurface (Screen); - SDLFlipCycles.Unclock(); - } - - BlitCycles.Unclock(); - - if (NeedGammaUpdate) - { - bool Windowed = false; - NeedGammaUpdate = false; - CalcGamma ((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); - CalcGamma ((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); - CalcGamma ((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); - NeedPalUpdate = true; - } - - if (NeedPalUpdate) - { - NeedPalUpdate = false; - UpdateColors (); - } -} - -void SDLFB::UpdateColors () -{ - if (NotPaletted) - { - PalEntry palette[256]; - - for (int i = 0; i < 256; ++i) - { - palette[i].r = GammaTable[0][SourcePalette[i].r]; - palette[i].g = GammaTable[1][SourcePalette[i].g]; - palette[i].b = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending (palette, palette, - 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], - FlashAmount); - } - GPfx.SetPalette (palette); - } - else - { - SDL_Color colors[256]; - - for (int i = 0; i < 256; ++i) - { - colors[i].r = GammaTable[0][SourcePalette[i].r]; - colors[i].g = GammaTable[1][SourcePalette[i].g]; - colors[i].b = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending ((PalEntry *)colors, (PalEntry *)colors, - 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], - FlashAmount); - } - SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256); - } -} - -PalEntry *SDLFB::GetPalette () -{ - return SourcePalette; -} - -void SDLFB::UpdatePalette () -{ - NeedPalUpdate = true; -} - -bool SDLFB::SetGamma (float gamma) -{ - Gamma = gamma; - NeedGammaUpdate = true; - return true; -} - -bool SDLFB::SetFlash (PalEntry rgb, int amount) -{ - Flash = rgb; - FlashAmount = amount; - NeedPalUpdate = true; - return true; -} - -void SDLFB::GetFlash (PalEntry &rgb, int &amount) -{ - rgb = Flash; - amount = FlashAmount; -} - -// Q: Should I gamma adjust the returned palette? -void SDLFB::GetFlashedPalette (PalEntry pal[256]) -{ - memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); - if (FlashAmount) - { - DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); - } -} - -void SDLFB::SetFullscreen (bool fullscreen) -{ - SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - if (!fullscreen) - { - // Restore proper window size - SDL_SetWindowSize (Screen, Width, Height); - } - - ResetSDLRenderer (); -} - -bool SDLFB::IsFullscreen () -{ - return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; -} - -void SDLFB::ResetSDLRenderer () -{ - if (Renderer) - { - if (Texture) - SDL_DestroyTexture (Texture); - SDL_DestroyRenderer (Renderer); - } - - UsingRenderer = !vid_forcesurface; - if (UsingRenderer) - { - Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| - (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); - if (!Renderer) - return; - - Uint32 fmt; - switch(vid_displaybits) - { - default: fmt = SDL_PIXELFORMAT_ARGB8888; break; - case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; - case 24: fmt = SDL_PIXELFORMAT_RGB888; break; - case 16: fmt = SDL_PIXELFORMAT_RGB565; break; - case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; - } - Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); - - { - NotPaletted = true; - - Uint32 format; - SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); - - Uint32 Rmask, Gmask, Bmask, Amask; - int bpp; - SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); - } - } - else - { - Surface = SDL_GetWindowSurface (Screen); - - if (Surface->format->palette == NULL) - { - NotPaletted = true; - GPfx.SetFormat (Surface->format->BitsPerPixel, Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask); - } - else - NotPaletted = false; - } - - // Calculate update rectangle - if (IsFullscreen ()) - { - int w, h; - SDL_GetWindowSize (Screen, &w, &h); - UpdateRect.w = w; - UpdateRect.h = h; - ScaleWithAspect (UpdateRect.w, UpdateRect.h, Width, Height); - UpdateRect.x = (w - UpdateRect.w)/2; - UpdateRect.y = (h - UpdateRect.h)/2; - } - else - { - // In windowed mode we just update the whole window. - UpdateRect.x = 0; - UpdateRect.y = 0; - UpdateRect.w = Width; - UpdateRect.h = Height; - } -} - -void SDLFB::SetVSync (bool vsync) -{ -#ifdef __APPLE__ - if (CGLContextObj context = CGLGetCurrentContext()) - { - // Apply vsync for native backend only (where OpenGL context is set) - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - // Inconsistency between 10.4 and 10.5 SDKs: - // third argument of CGLSetParameter() is const long* on 10.4 and const GLint* on 10.5 - // So, GLint typedef'ed to long instead of int to workaround this issue - typedef long GLint; -#endif // prior to 10.5 - - const GLint value = vsync ? 1 : 0; - CGLSetParameter(context, kCGLCPSwapInterval, &value); - } -#else - ResetSDLRenderer (); -#endif // __APPLE__ -} - -ADD_STAT (blit) -{ - FString out; - out.Format ("blit=%04.1f ms flip=%04.1f ms", - BlitCycles.TimeMS(), SDLFlipCycles.TimeMS()); - return out; -} + +// HEADER FILES ------------------------------------------------------------ + +#include "doomtype.h" + +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "v_video.h" +#include "v_pfx.h" +#include "stats.h" +#include "v_palette.h" +#include "sdlvideo.h" +#include "r_swrenderer.h" +#include "version.h" + +#include + +#ifdef __APPLE__ +#include +#endif // __APPLE__ + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +class SDLFB : public DFrameBuffer +{ + DECLARE_CLASS(SDLFB, DFrameBuffer) +public: + SDLFB (int width, int height, bool fullscreen); + ~SDLFB (); + + bool Lock (bool buffer); + void Unlock (); + bool Relock (); + void ForceBuffering (bool force); + bool IsValid (); + void Update (); + PalEntry *GetPalette (); + void GetFlashedPalette (PalEntry pal[256]); + void UpdatePalette (); + bool SetGamma (float gamma); + bool SetFlash (PalEntry rgb, int amount); + void GetFlash (PalEntry &rgb, int &amount); + void SetFullscreen (bool fullscreen); + int GetPageCount (); + bool IsFullscreen (); + + friend class SDLVideo; + + virtual void SetVSync (bool vsync); + +private: + PalEntry SourcePalette[256]; + BYTE GammaTable[3][256]; + PalEntry Flash; + int FlashAmount; + float Gamma; + bool UpdatePending; + + SDL_Window *Screen; + SDL_Renderer *Renderer; + union + { + SDL_Texture *Texture; + SDL_Surface *Surface; + }; + SDL_Rect UpdateRect; + + bool UsingRenderer; + bool NeedPalUpdate; + bool NeedGammaUpdate; + bool NotPaletted; + + void UpdateColors (); + void ResetSDLRenderer (); + + SDLFB () {} +}; +IMPLEMENT_CLASS(SDLFB) + +struct MiniModeInfo +{ + WORD Width, Height; +}; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern IVideo *Video; +extern bool GUICapture; + +EXTERN_CVAR (Float, Gamma) +EXTERN_CVAR (Int, vid_maxfps) +EXTERN_CVAR (Bool, cl_capfps) +EXTERN_CVAR (Bool, vid_vsync) + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// Dummy screen sizes to pass when windowed +static MiniModeInfo WinModes[] = +{ + { 320, 200 }, + { 320, 240 }, + { 400, 225 }, // 16:9 + { 400, 300 }, + { 480, 270 }, // 16:9 + { 480, 360 }, + { 512, 288 }, // 16:9 + { 512, 384 }, + { 640, 360 }, // 16:9 + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, // 16:10 + { 720, 540 }, + { 800, 450 }, // 16:9 + { 800, 480 }, + { 800, 500 }, // 16:10 + { 800, 600 }, + { 848, 480 }, // 16:9 + { 960, 600 }, // 16:10 + { 960, 720 }, + { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 + { 1024, 640 }, // 16:10 + { 1024, 768 }, + { 1088, 612 }, // 16:9 + { 1152, 648 }, // 16:9 + { 1152, 720 }, // 16:10 + { 1152, 864 }, + { 1280, 720 }, // 16:9 + { 1280, 854 }, + { 1280, 800 }, // 16:10 + { 1280, 960 }, + { 1280, 1024 }, // 5:4 + { 1360, 768 }, // 16:9 + { 1366, 768 }, + { 1400, 787 }, // 16:9 + { 1400, 875 }, // 16:10 + { 1400, 1050 }, + { 1440, 900 }, + { 1440, 960 }, + { 1440, 1080 }, + { 1600, 900 }, // 16:9 + { 1600, 1000 }, // 16:10 + { 1600, 1200 }, + { 1920, 1080 }, + { 1920, 1200 }, + { 2048, 1536 }, + { 2560, 1440 }, + { 2560, 1600 }, + { 2560, 2048 }, + { 2880, 1800 }, + { 3200, 1800 }, + { 3840, 2160 }, + { 3840, 2400 }, + { 4096, 2160 }, + { 5120, 2880 } +}; + +static cycle_t BlitCycles; +static cycle_t SDLFlipCycles; + +// CODE -------------------------------------------------------------------- + +void ScaleWithAspect (int &w, int &h, int Width, int Height) +{ + int resRatio = CheckRatio (Width, Height); + int screenRatio; + CheckRatio (w, h, &screenRatio); + if (resRatio == screenRatio) + return; + + double yratio; + switch(resRatio) + { + case 0: yratio = 4./3.; break; + case 1: yratio = 16./9.; break; + case 2: yratio = 16./10.; break; + case 3: yratio = 17./10.; break; + case 4: yratio = 5./4.; break; + default: return; + } + double y = w/yratio; + if (y > h) + w = h*yratio; + else + h = y; +} + +SDLVideo::SDLVideo (int parm) +{ + IteratorBits = 0; +} + +SDLVideo::~SDLVideo () +{ +} + +void SDLVideo::StartModeIterator (int bits, bool fs) +{ + IteratorMode = 0; + IteratorBits = bits; +} + +bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) +{ + if (IteratorBits != 8) + return false; + + if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) + { + *width = WinModes[IteratorMode].Width; + *height = WinModes[IteratorMode].Height; + ++IteratorMode; + return true; + } + return false; +} + +DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +{ + static int retry = 0; + static int owidth, oheight; + + PalEntry flashColor; + int flashAmount; + + if (old != NULL) + { // Reuse the old framebuffer if its attributes are the same + SDLFB *fb = static_cast (old); + if (fb->Width == width && + fb->Height == height) + { + bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; + + if (fsnow != fullscreen) + { + fb->SetFullscreen (fullscreen); + } + return old; + } + old->GetFlash (flashColor, flashAmount); + old->ObjectFlags |= OF_YesReallyDelete; + if (screen == old) screen = NULL; + delete old; + } + else + { + flashColor = 0; + flashAmount = 0; + } + + SDLFB *fb = new SDLFB (width, height, fullscreen); + retry = 0; + + // If we could not create the framebuffer, try again with slightly + // different parameters in this order: + // 1. Try with the closest size + // 2. Try in the opposite screen mode with the original size + // 3. Try in the opposite screen mode with the closest size + // This is a somewhat confusing mass of recursion here. + + while (fb == NULL || !fb->IsValid ()) + { + if (fb != NULL) + { + delete fb; + } + + switch (retry) + { + case 0: + owidth = width; + oheight = height; + case 2: + // Try a different resolution. Hopefully that will work. + I_ClosestResolution (&width, &height, 8); + break; + + case 1: + // Try changing fullscreen mode. Maybe that will work. + width = owidth; + height = oheight; + fullscreen = !fullscreen; + break; + + default: + // I give up! + I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); + } + + ++retry; + fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + } + + fb->SetFlash (flashColor, flashAmount); + + return fb; +} + +void SDLVideo::SetWindowedScale (float scale) +{ +} + +// FrameBuffer implementation ----------------------------------------------- + +SDLFB::SDLFB (int width, int height, bool fullscreen) + : DFrameBuffer (width, height) +{ + int i; + + NeedPalUpdate = false; + NeedGammaUpdate = false; + UpdatePending = false; + NotPaletted = false; + FlashAmount = 0; + + FString caption; + caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + + Screen = SDL_CreateWindow (caption, + SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), + width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); + + if (Screen == NULL) + return; + + Renderer = NULL; + Texture = NULL; + ResetSDLRenderer (); + + for (i = 0; i < 256; i++) + { + GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; + } + + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + UpdateColors (); + +#ifdef __APPLE__ + SetVSync (vid_vsync); +#endif +} + + +SDLFB::~SDLFB () +{ + if(Screen) + { + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + + SDL_DestroyWindow (Screen); + } +} + +bool SDLFB::IsValid () +{ + return DFrameBuffer::IsValid() && Screen != NULL; +} + +int SDLFB::GetPageCount () +{ + return 1; +} + +bool SDLFB::Lock (bool buffered) +{ + return DSimpleCanvas::Lock (); +} + +bool SDLFB::Relock () +{ + return DSimpleCanvas::Lock (); +} + +void SDLFB::Unlock () +{ + if (UpdatePending && LockCount == 1) + { + Update (); + } + else if (--LockCount <= 0) + { + Buffer = NULL; + LockCount = 0; + } +} + +void SDLFB::Update () +{ + if (LockCount != 1) + { + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff (); + +#ifndef __APPLE__ + if(vid_maxfps && !cl_capfps) + { + SEMAPHORE_WAIT(FPSLimitSemaphore) + } +#endif + + Buffer = NULL; + LockCount = 0; + UpdatePending = false; + + BlitCycles.Reset(); + SDLFlipCycles.Reset(); + BlitCycles.Clock(); + + void *pixels; + int pitch; + if (UsingRenderer) + { + if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) + return; + } + else + { + if (SDL_LockSurface (Surface)) + return; + + pixels = Surface->pixels; + pitch = Surface->pitch; + } + + if (NotPaletted) + { + GPfx.Convert (MemBuffer, Pitch, + pixels, pitch, Width, Height, + FRACUNIT, FRACUNIT, 0, 0); + } + else + { + if (pitch == Pitch) + { + memcpy (pixels, MemBuffer, Width*Height); + } + else + { + for (int y = 0; y < Height; ++y) + { + memcpy ((BYTE *)pixels+y*pitch, MemBuffer+y*Pitch, Width); + } + } + } + + if (UsingRenderer) + { + SDL_UnlockTexture (Texture); + + SDLFlipCycles.Clock(); + SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); + SDL_RenderPresent(Renderer); + SDLFlipCycles.Unclock(); + } + else + { + SDL_UnlockSurface (Surface); + + SDLFlipCycles.Clock(); + SDL_UpdateWindowSurface (Screen); + SDLFlipCycles.Unclock(); + } + + BlitCycles.Unclock(); + + if (NeedGammaUpdate) + { + bool Windowed = false; + NeedGammaUpdate = false; + CalcGamma ((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); + CalcGamma ((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); + CalcGamma ((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); + NeedPalUpdate = true; + } + + if (NeedPalUpdate) + { + NeedPalUpdate = false; + UpdateColors (); + } +} + +void SDLFB::UpdateColors () +{ + if (NotPaletted) + { + PalEntry palette[256]; + + for (int i = 0; i < 256; ++i) + { + palette[i].r = GammaTable[0][SourcePalette[i].r]; + palette[i].g = GammaTable[1][SourcePalette[i].g]; + palette[i].b = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending (palette, palette, + 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], + FlashAmount); + } + GPfx.SetPalette (palette); + } + else + { + SDL_Color colors[256]; + + for (int i = 0; i < 256; ++i) + { + colors[i].r = GammaTable[0][SourcePalette[i].r]; + colors[i].g = GammaTable[1][SourcePalette[i].g]; + colors[i].b = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending ((PalEntry *)colors, (PalEntry *)colors, + 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], + FlashAmount); + } + SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256); + } +} + +PalEntry *SDLFB::GetPalette () +{ + return SourcePalette; +} + +void SDLFB::UpdatePalette () +{ + NeedPalUpdate = true; +} + +bool SDLFB::SetGamma (float gamma) +{ + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool SDLFB::SetFlash (PalEntry rgb, int amount) +{ + Flash = rgb; + FlashAmount = amount; + NeedPalUpdate = true; + return true; +} + +void SDLFB::GetFlash (PalEntry &rgb, int &amount) +{ + rgb = Flash; + amount = FlashAmount; +} + +// Q: Should I gamma adjust the returned palette? +void SDLFB::GetFlashedPalette (PalEntry pal[256]) +{ + memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); + } +} + +void SDLFB::SetFullscreen (bool fullscreen) +{ + SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + if (!fullscreen) + { + // Restore proper window size + SDL_SetWindowSize (Screen, Width, Height); + } + + ResetSDLRenderer (); +} + +bool SDLFB::IsFullscreen () +{ + return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +} + +void SDLFB::ResetSDLRenderer () +{ + if (Renderer) + { + if (Texture) + SDL_DestroyTexture (Texture); + SDL_DestroyRenderer (Renderer); + } + + UsingRenderer = !vid_forcesurface; + if (UsingRenderer) + { + Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| + (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (!Renderer) + return; + + Uint32 fmt; + switch(vid_displaybits) + { + default: fmt = SDL_PIXELFORMAT_ARGB8888; break; + case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; + case 24: fmt = SDL_PIXELFORMAT_RGB888; break; + case 16: fmt = SDL_PIXELFORMAT_RGB565; break; + case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + } + Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); + + { + NotPaletted = true; + + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); + + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); + } + } + else + { + Surface = SDL_GetWindowSurface (Screen); + + if (Surface->format->palette == NULL) + { + NotPaletted = true; + GPfx.SetFormat (Surface->format->BitsPerPixel, Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask); + } + else + NotPaletted = false; + } + + // Calculate update rectangle + if (IsFullscreen ()) + { + int w, h; + SDL_GetWindowSize (Screen, &w, &h); + UpdateRect.w = w; + UpdateRect.h = h; + ScaleWithAspect (UpdateRect.w, UpdateRect.h, Width, Height); + UpdateRect.x = (w - UpdateRect.w)/2; + UpdateRect.y = (h - UpdateRect.h)/2; + } + else + { + // In windowed mode we just update the whole window. + UpdateRect.x = 0; + UpdateRect.y = 0; + UpdateRect.w = Width; + UpdateRect.h = Height; + } +} + +void SDLFB::SetVSync (bool vsync) +{ +#ifdef __APPLE__ + if (CGLContextObj context = CGLGetCurrentContext()) + { + // Apply vsync for native backend only (where OpenGL context is set) + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + // Inconsistency between 10.4 and 10.5 SDKs: + // third argument of CGLSetParameter() is const long* on 10.4 and const GLint* on 10.5 + // So, GLint typedef'ed to long instead of int to workaround this issue + typedef long GLint; +#endif // prior to 10.5 + + const GLint value = vsync ? 1 : 0; + CGLSetParameter(context, kCGLCPSwapInterval, &value); + } +#else + ResetSDLRenderer (); +#endif // __APPLE__ +} + +ADD_STAT (blit) +{ + FString out; + out.Format ("blit=%04.1f ms flip=%04.1f ms", + BlitCycles.TimeMS(), SDLFlipCycles.TimeMS()); + return out; +} diff --git a/src/sdl/sdlvideo.h b/src/posix/sdl/sdlvideo.h similarity index 95% rename from src/sdl/sdlvideo.h rename to src/posix/sdl/sdlvideo.h index 8869f6fa7..072167b5a 100644 --- a/src/sdl/sdlvideo.h +++ b/src/posix/sdl/sdlvideo.h @@ -1,21 +1,21 @@ -#include "hardware.h" -#include "v_video.h" - -class SDLVideo : public IVideo -{ - public: - SDLVideo (int parm); - ~SDLVideo (); - - EDisplayType GetDisplayType () { return DISPLAY_Both; } - void SetWindowedScale (float scale); - - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); - - void StartModeIterator (int bits, bool fs); - bool NextMode (int *width, int *height, bool *letterbox); - -private: - int IteratorMode; - int IteratorBits; -}; +#include "hardware.h" +#include "v_video.h" + +class SDLVideo : public IVideo +{ + public: + SDLVideo (int parm); + ~SDLVideo (); + + EDisplayType GetDisplayType () { return DISPLAY_Both; } + void SetWindowedScale (float scale); + + DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + + void StartModeIterator (int bits, bool fs); + bool NextMode (int *width, int *height, bool *letterbox); + +private: + int IteratorMode; + int IteratorBits; +}; diff --git a/src/sdl/SDLMain.m b/src/sdl/SDLMain.m deleted file mode 100644 index 78c4e3d2b..000000000 --- a/src/sdl/SDLMain.m +++ /dev/null @@ -1,387 +0,0 @@ -/* SDLMain.m - main entry point for our Cocoa-ized SDL app - Initial Version: Darrell Walisser - Non-NIB-Code & other changes: Max Horn - - Feel free to customize this file to suit your needs -*/ - -#import "SDL.h" -#import -#import /* for MAXPATHLEN */ -#import - -@interface SDLMain : NSObject -@end - -/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, - but the method still is there and works. To avoid warnings, we declare - it ourselves here. */ -@interface NSApplication(SDL_Missing_Methods) -- (void)setAppleMenu:(NSMenu *)menu; -@end - -/* Use this flag to determine whether we use SDLMain.nib or not */ -#define SDL_USE_NIB_FILE 0 - -/* Use this flag to determine whether we use CPS (docking) or not */ -#define SDL_USE_CPS 1 -#ifdef SDL_USE_CPS -/* Portions of CPS.h */ -typedef struct CPSProcessSerNum -{ - UInt32 lo; - UInt32 hi; -} CPSProcessSerNum; - -extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); -extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); -extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); - -#endif /* SDL_USE_CPS */ - -static int gArgc; -static char **gArgv; -static BOOL gFinderLaunch; -static BOOL gCalledAppMainline = FALSE; - -static NSString *getApplicationName(void) -{ - NSDictionary *dict; - NSString *appName = 0; - - /* Determine the application name */ - dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); - if (dict) - appName = [dict objectForKey: @"CFBundleName"]; - - if (![appName length]) - appName = [[NSProcessInfo processInfo] processName]; - - return appName; -} - -#if SDL_USE_NIB_FILE -/* A helper category for NSString */ -@interface NSString (ReplaceSubString) -- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; -@end -#endif - -@interface SDLApplication : NSApplication -@end - -@implementation SDLApplication -/* Invoked from the Quit menu item */ -- (void)terminate:(id)sender -{ - /* Post a SDL_QUIT event */ - SDL_Event event; - event.type = SDL_QUIT; - SDL_PushEvent(&event); -} -@end - -/* The main class of the application, the application's delegate */ -@implementation SDLMain - -/* Set the working directory to the .app's parent directory */ -- (void) setupWorkingDirectory:(BOOL)shouldChdir -{ - if (shouldChdir) - { - char parentdir[MAXPATHLEN]; - CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); - if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) { - assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ - } - CFRelease(url); - CFRelease(url2); - } - -} - -#if SDL_USE_NIB_FILE - -/* Fix menu to contain the real app name instead of "SDL App" */ -- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName -{ - NSRange aRange; - NSEnumerator *enumerator; - NSMenuItem *menuItem; - - aRange = [[aMenu title] rangeOfString:@"SDL App"]; - if (aRange.length != 0) - [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; - - enumerator = [[aMenu itemArray] objectEnumerator]; - while ((menuItem = [enumerator nextObject])) - { - aRange = [[menuItem title] rangeOfString:@"SDL App"]; - if (aRange.length != 0) - [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; - if ([menuItem hasSubmenu]) - [self fixMenu:[menuItem submenu] withAppName:appName]; - } - [ aMenu sizeToFit ]; -} - -#else - -static void setApplicationMenu(void) -{ - /* warning: this code is very odd */ - NSMenu *appleMenu; - NSMenuItem *menuItem; - NSString *title; - NSString *appName; - - appName = getApplicationName(); - appleMenu = [[NSMenu alloc] initWithTitle:@""]; - - /* Add menu items */ - title = [@"About " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; - - [appleMenu addItem:[NSMenuItem separatorItem]]; - - title = [@"Hide " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; - - menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; - [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; - - [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; - - [appleMenu addItem:[NSMenuItem separatorItem]]; - - title = [@"Quit " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; - - - /* Put menu into the menubar */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; - [menuItem setSubmenu:appleMenu]; - [[NSApp mainMenu] addItem:menuItem]; - - /* Tell the application object that this is now the application menu */ - [NSApp setAppleMenu:appleMenu]; - - /* Finally give up our references to the objects */ - [appleMenu release]; - [menuItem release]; -} - -/* Create a window menu */ -static void setupWindowMenu(void) -{ - NSMenu *windowMenu; - NSMenuItem *windowMenuItem; - NSMenuItem *menuItem; - - windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; - - /* "Minimize" item */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; - [windowMenu addItem:menuItem]; - [menuItem release]; - - /* Put menu into the menubar */ - windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; - [windowMenuItem setSubmenu:windowMenu]; - [[NSApp mainMenu] addItem:windowMenuItem]; - - /* Tell the application object that this is now the window menu */ - [NSApp setWindowsMenu:windowMenu]; - - /* Finally give up our references to the objects */ - [windowMenu release]; - [windowMenuItem release]; -} - -/* Replacement for NSApplicationMain */ -static void CustomApplicationMain (int argc, char **argv) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - SDLMain *sdlMain; - - /* Ensure the application object is initialised */ - [SDLApplication sharedApplication]; - -#ifdef SDL_USE_CPS - { - CPSProcessSerNum PSN; - /* Tell the dock about us */ - if (!CPSGetCurrentProcess(&PSN)) - if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) - if (!CPSSetFrontProcess(&PSN)) - [SDLApplication sharedApplication]; - } -#endif /* SDL_USE_CPS */ - - /* Set up the menubar */ - [NSApp setMainMenu:[[NSMenu alloc] init]]; - setApplicationMenu(); - setupWindowMenu(); - - /* Create SDLMain and make it the app delegate */ - sdlMain = [[SDLMain alloc] init]; - [NSApp setDelegate:sdlMain]; - - /* Start the main event loop */ - [NSApp run]; - - [sdlMain release]; - [pool release]; -} - -#endif - - -/* - * Catch document open requests...this lets us notice files when the app - * was launched by double-clicking a document, or when a document was - * dragged/dropped on the app's icon. You need to have a - * CFBundleDocumentsType section in your Info.plist to get this message, - * apparently. - * - * Files are added to gArgv, so to the app, they'll look like command line - * arguments. Previously, apps launched from the finder had nothing but - * an argv[0]. - * - * This message may be received multiple times to open several docs on launch. - * - * This message is ignored once the app's mainline has been called. - */ -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename -{ - const char *temparg; - size_t arglen; - char *arg; - char **newargv; - - if (!gFinderLaunch) /* MacOS is passing command line args. */ - return FALSE; - - if (gCalledAppMainline) /* app has started, ignore this document. */ - return FALSE; - - temparg = [filename UTF8String]; - arglen = SDL_strlen(temparg) + 1; - arg = (char *) SDL_malloc(arglen); - if (arg == NULL) - return FALSE; - - newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); - if (newargv == NULL) - { - SDL_free(arg); - return FALSE; - } - gArgv = newargv; - - SDL_strlcpy(arg, temparg, arglen); - gArgv[gArgc++] = arg; - gArgv[gArgc] = NULL; - return TRUE; -} - - -/* Called when the internal event loop has just started running */ -- (void) applicationDidFinishLaunching: (NSNotification *) note -{ - int status; - - /* Set the working directory to the .app's parent directory */ - [self setupWorkingDirectory:gFinderLaunch]; - -#if SDL_USE_NIB_FILE - /* Set the main menu to contain the real app name instead of "SDL App" */ - [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; -#endif - - /* Hand off to main application code */ - gCalledAppMainline = TRUE; - status = SDL_main (gArgc, gArgv); - - /* We're done, thank you for playing */ - exit(status); -} -@end - - -@implementation NSString (ReplaceSubString) - -- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString -{ - unsigned int bufferSize; - unsigned int selfLen = [self length]; - unsigned int aStringLen = [aString length]; - unichar *buffer; - NSRange localRange; - NSString *result; - - bufferSize = selfLen + aStringLen - aRange.length; - buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar)); - - /* Get first part into buffer */ - localRange.location = 0; - localRange.length = aRange.location; - [self getCharacters:buffer range:localRange]; - - /* Get middle part into buffer */ - localRange.location = 0; - localRange.length = aStringLen; - [aString getCharacters:(buffer+aRange.location) range:localRange]; - - /* Get last part into buffer */ - localRange.location = aRange.location + aRange.length; - localRange.length = selfLen - localRange.location; - [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; - - /* Build output string */ - result = [NSString stringWithCharacters:buffer length:bufferSize]; - - NSDeallocateMemoryPages(buffer, bufferSize); - - return result; -} - -@end - - - -#ifdef main -# undef main -#endif - - -/* Main entry point to executable - should *not* be SDL_main! */ -int main (int argc, char **argv) -{ - /* Copy the arguments into a global variable */ - /* This is passed if we are launched by double-clicking */ - if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { - gArgv = (char **) SDL_malloc(sizeof (char *) * 2); - gArgv[0] = argv[0]; - gArgv[1] = NULL; - gArgc = 1; - gFinderLaunch = YES; - } else { - int i; - gArgc = argc; - gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); - for (i = 0; i <= argc; i++) - gArgv[i] = argv[i]; - gFinderLaunch = NO; - } - -#if SDL_USE_NIB_FILE - [SDLApplication poseAsClass:[NSApplication class]]; - NSApplicationMain (argc, argv); -#else - CustomApplicationMain (argc, argv); -#endif - return 0; -} - From 195ed09c5efd6c40820b5b14a03e1b7ca28a7861 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 18 Dec 2014 11:53:04 +0200 Subject: [PATCH 018/117] Fixed build of SDL output plug-in --- output_sdl/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/output_sdl/CMakeLists.txt b/output_sdl/CMakeLists.txt index 289f6f48d..cc3614656 100644 --- a/output_sdl/CMakeLists.txt +++ b/output_sdl/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required( VERSION 2.4 ) add_library( output_sdl MODULE output_sdl.c ) -include_directories( ${FMOD_INCLUDE_DIR} ${SDL_INCLUDE_DIR} ) -target_link_libraries( output_sdl SDL ) +include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ) +target_link_libraries( output_sdl ${SDL2_LIBRARY} ) FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" ) add_custom_command( TARGET output_sdl POST_BUILD From 41c949f8ac84b1969d548edd7827f34c4c080569 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Dec 2014 13:18:47 +0200 Subject: [PATCH 019/117] Cleanup video part of native OS X backend, phase 1 --- src/posix/cocoa/i_backend_cocoa.mm | 291 +++++++---------------------- 1 file changed, 67 insertions(+), 224 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index 571c3847b..76ec0e322 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -247,22 +247,6 @@ CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) setmodeneeded = true; } -CUSTOM_CVAR(Float, vid_winscale, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 1.f) - { - self = 1.f; - } - else if (Video) - { - Video->SetWindowedScale (self); - NewWidth = screen->GetWidth(); - NewHeight = screen->GetHeight(); - NewBits = DisplayBits; - setmodeneeded = true; - } -} - // --------------------------------------------------------------------------- @@ -2117,21 +2101,6 @@ int SDL_QueryTexture(SDL_Texture *texture, Uint32* format, int* access, int* w, } -int SDL_LockTexture(SDL_Texture* texture, const SDL_Rect *rect, void** pixels, int *pitch) -{ - assert(NULL == rect); - - *pixels = texture->window->pixels; - *pitch = texture->window->pitch; - - return 0; -} - -void SDL_UnlockTexture(SDL_Texture *texture) -{ - ZD_UNUSED(texture); -} - int SDL_UpdateWindowSurface(SDL_Window *screen) { assert(NULL != screen); @@ -2179,31 +2148,11 @@ int SDL_UpdateWindowSurface(SDL_Window *screen) return 0; } -int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect) -{ - ZD_UNUSED(renderer); - ZD_UNUSED(texture); - ZD_UNUSED(srcrect); - ZD_UNUSED(dstrect); - - return 0; -} - void SDL_RenderPresent(SDL_Renderer *renderer) { SDL_UpdateWindowSurface(renderer->window); } -int SDL_SetPaletteColors(SDL_Palette* palette, const SDL_Color* colors, int firstcolor, int ncolors) -{ - ZD_UNUSED(palette); - ZD_UNUSED(colors); - ZD_UNUSED(firstcolor); - ZD_UNUSED(ncolors); - - return 0; -} - } // extern "C" @@ -2213,27 +2162,25 @@ int SDL_SetPaletteColors(SDL_Palette* palette, const SDL_Color* colors, int firs class CocoaVideo : public IVideo { public: - CocoaVideo(int parm); - ~CocoaVideo(); + explicit CocoaVideo(int dummy); - EDisplayType GetDisplayType () { return DISPLAY_Both; } - void SetWindowedScale (float scale); + virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } + virtual void SetWindowedScale(float scale); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); - void StartModeIterator (int bits, bool fs); - bool NextMode (int *width, int *height, bool *letterbox); + virtual void StartModeIterator(int bits, bool fs); + virtual bool NextMode(int* width, int* height, bool* letterbox); private: - int IteratorMode; - int IteratorBits; + size_t m_modeIterator; }; class CocoaFrameBuffer : public DFrameBuffer { public: - CocoaFrameBuffer (int width, int height, bool fullscreen); - ~CocoaFrameBuffer (); + CocoaFrameBuffer(int width, int height, bool fullscreen); + ~CocoaFrameBuffer(); bool Lock (bool buffer); void Unlock (); @@ -2270,11 +2217,9 @@ private: SDL_Texture *Texture; SDL_Surface *Surface; }; - SDL_Rect UpdateRect; bool NeedPalUpdate; bool NeedGammaUpdate; - bool NotPaletted; void UpdateColors (); void ResetSDLRenderer (); @@ -2379,66 +2324,40 @@ static MiniModeInfo WinModes[] = }; static cycle_t BlitCycles; -static cycle_t SDLFlipCycles; +static cycle_t FlipCycles; -// CODE -------------------------------------------------------------------- -void ScaleWithAspect (Uint16 &w, Uint16 &h, Uint16 Width, Uint16 Height) +CocoaVideo::CocoaVideo(int dummy) +: m_modeIterator(0) { - int resRatio = CheckRatio (Width, Height); - int screenRatio; - CheckRatio (w, h, &screenRatio); - if (resRatio == screenRatio) - return; + ZD_UNUSED(dummy); +} - double yratio; - switch(resRatio) +void CocoaVideo::StartModeIterator(int bits, bool fs) +{ + ZD_UNUSED(bits); + ZD_UNUSED(fs); + + m_modeIterator = 0; +} + +bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) +{ + ZD_UNUSED(letterbox); + + if (m_modeIterator < sizeof(WinModes) / sizeof(WinModes[0])) { - case 0: yratio = 4./3.; break; - case 1: yratio = 16./9.; break; - case 2: yratio = 16./10.; break; - case 3: yratio = 17./10.; break; - case 4: yratio = 5./4.; break; - default: return; - } - double y = w/yratio; - if (y > h) - w = h*yratio; - else - h = y; -} + *width = WinModes[m_modeIterator].Width; + *height = WinModes[m_modeIterator].Height; -CocoaVideo::CocoaVideo (int parm) -{ - IteratorBits = 0; -} - -CocoaVideo::~CocoaVideo () -{ -} - -void CocoaVideo::StartModeIterator (int bits, bool fs) -{ - IteratorMode = 0; - IteratorBits = bits; -} - -bool CocoaVideo::NextMode (int *width, int *height, bool *letterbox) -{ - if (IteratorBits != 8) - return false; - - if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) - { - *width = WinModes[IteratorMode].Width; - *height = WinModes[IteratorMode].Height; - ++IteratorMode; + ++m_modeIterator; return true; } + return false; } -DFrameBuffer *CocoaVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +DFrameBuffer *CocoaVideo::CreateFrameBuffer(int width, int height, bool fullscreen, DFrameBuffer* old) { static int retry = 0; static int owidth, oheight; @@ -2525,16 +2444,13 @@ void CocoaVideo::SetWindowedScale (float scale) CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) -: DFrameBuffer (width, height) +: DFrameBuffer(width, height) +, FlashAmount(0) +, Gamma(0.0f) +, UpdatePending(false) +, NeedPalUpdate(false) +, NeedGammaUpdate(false) { - int i; - - NeedPalUpdate = false; - NeedGammaUpdate = false; - UpdatePending = false; - NotPaletted = false; - FlashAmount = 0; - FString caption; caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); @@ -2548,7 +2464,7 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) Texture = NULL; ResetSDLRenderer (); - for (i = 0; i < 256; i++) + for (size_t i = 0; i < 256; ++i) { GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; } @@ -2627,42 +2543,15 @@ void CocoaFrameBuffer::Update () UpdatePending = false; BlitCycles.Reset(); - SDLFlipCycles.Reset(); + FlipCycles.Reset(); BlitCycles.Clock(); - void *pixels; - int pitch; + GPfx.Convert(MemBuffer, Pitch, Texture->window->pixels, Texture->window->pitch, + Width, Height, FRACUNIT, FRACUNIT, 0, 0); - if (SDL_LockTexture (Texture, NULL, &pixels, &pitch)) - return; - - if (NotPaletted) - { - GPfx.Convert (MemBuffer, Pitch, - pixels, pitch, Width, Height, - FRACUNIT, FRACUNIT, 0, 0); - } - else - { - if (pitch == Pitch) - { - memcpy (pixels, MemBuffer, Width*Height); - } - else - { - for (int y = 0; y < Height; ++y) - { - memcpy ((BYTE *)pixels+y*pitch, MemBuffer+y*Pitch, Width); - } - } - } - - SDL_UnlockTexture (Texture); - - SDLFlipCycles.Clock(); - SDL_RenderCopy(Renderer, Texture, NULL, &UpdateRect); + FlipCycles.Clock(); SDL_RenderPresent(Renderer); - SDLFlipCycles.Unclock(); + FlipCycles.Unclock(); BlitCycles.Unclock(); @@ -2685,42 +2574,23 @@ void CocoaFrameBuffer::Update () void CocoaFrameBuffer::UpdateColors () { - if (NotPaletted) - { - PalEntry palette[256]; + PalEntry palette[256]; - for (int i = 0; i < 256; ++i) - { - palette[i].r = GammaTable[0][SourcePalette[i].r]; - palette[i].g = GammaTable[1][SourcePalette[i].g]; - palette[i].b = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending (palette, palette, - 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], - FlashAmount); - } - GPfx.SetPalette (palette); - } - else + for (size_t i = 0; i < 256; ++i) { - SDL_Color colors[256]; - - for (int i = 0; i < 256; ++i) - { - colors[i].r = GammaTable[0][SourcePalette[i].r]; - colors[i].g = GammaTable[1][SourcePalette[i].g]; - colors[i].b = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending ((PalEntry *)colors, (PalEntry *)colors, - 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], - FlashAmount); - } - SDL_SetPaletteColors (Surface->format->palette, colors, 0, 256); + palette[i].r = GammaTable[0][SourcePalette[i].r]; + palette[i].g = GammaTable[1][SourcePalette[i].g]; + palette[i].b = GammaTable[2][SourcePalette[i].b]; } + + if (FlashAmount) + { + DoBlending(palette, palette, 256, + GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], + FlashAmount); + } + + GPfx.SetPalette (palette); } PalEntry *CocoaFrameBuffer::GetPalette () @@ -2728,7 +2598,7 @@ PalEntry *CocoaFrameBuffer::GetPalette () return SourcePalette; } -void CocoaFrameBuffer::UpdatePalette () +void CocoaFrameBuffer::UpdatePalette() { NeedPalUpdate = true; } @@ -2806,37 +2676,13 @@ void CocoaFrameBuffer::ResetSDLRenderer () // } Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); - { - NotPaletted = true; + Uint32 format; + SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); - Uint32 format; - SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); - - Uint32 Rmask, Gmask, Bmask, Amask; - int bpp; - SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); - } - - // Calculate update rectangle - if (IsFullscreen ()) - { - int w, h; - SDL_GetWindowSize (Screen, &w, &h); - UpdateRect.w = w; - UpdateRect.h = h; - ScaleWithAspect (UpdateRect.w, UpdateRect.h, Width, Height); - UpdateRect.x = (w - UpdateRect.w)/2; - UpdateRect.y = (h - UpdateRect.h)/2; - } - else - { - // In windowed mode we just update the whole window. - UpdateRect.x = 0; - UpdateRect.y = 0; - UpdateRect.w = Width; - UpdateRect.h = Height; - } + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); + GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); } void CocoaFrameBuffer::SetVSync (bool vsync) @@ -2855,11 +2701,10 @@ void CocoaFrameBuffer::SetVSync (bool vsync) } } -ADD_STAT (blit) +ADD_STAT(blit) { FString out; - out.Format ("blit=%04.1f ms flip=%04.1f ms", - BlitCycles.TimeMS(), SDLFlipCycles.TimeMS()); + out.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); return out; } @@ -2892,8 +2737,6 @@ void I_InitGraphics () I_FatalError ("Failed to initialize display"); atterm (I_ShutdownGraphics); - - Video->SetWindowedScale (vid_winscale); } static void I_DeleteRenderer() From 797cf624df2d401fe8f9a5f996489329ace36fa4 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Dec 2014 15:03:36 +0200 Subject: [PATCH 020/117] Cleanup video part of native OS X backend, phase 2 --- src/posix/cocoa/i_backend_cocoa.mm | 364 +++++++++-------------------- 1 file changed, 115 insertions(+), 249 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index 76ec0e322..a4ad1a4b8 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -201,7 +201,6 @@ RenderBufferOptions rbOpts; EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_hidpi ) EXTERN_CVAR(Bool, vid_vsync ) -EXTERN_CVAR(Int, vid_adapter ) EXTERN_CVAR(Int, vid_defwidth ) EXTERN_CVAR(Int, vid_defheight) @@ -1957,21 +1956,6 @@ typedef uint32_t Uint32; } SDL_TextureAccess; - - -SDL_bool SDL_PixelFormatEnumToMasks(Uint32 format, int* bpp, Uint32* Rmask, Uint32* Gmask, Uint32* Bmask, Uint32* Amask) -{ - //assert(format == SDL_PIXELFORMAT_ABGR8888); - - *bpp = 32; - *Rmask = 0x000000FF; - *Gmask = 0x0000FF00; - *Bmask = 0x00FF0000; - *Amask = 0xFF000000; - - return SDL_TRUE; -} - struct SDL_Window { Uint32 flags; @@ -2016,27 +2000,6 @@ Uint32 SDL_GetWindowFlags(SDL_Window *window) return window->flags; } -SDL_Surface *SDL_GetWindowSurface(SDL_Window *window) -{ - ZD_UNUSED(window); - return NULL; -} - -void SDL_GetWindowSize(SDL_Window *window, int *w, int *h) -{ - *w = window->w; - *h = window->h; -} - -void SDL_SetWindowSize(SDL_Window *window, int w, int h) -{ - // Currently this is used for handling the fullscreen->windowed transition. - // We can just no-op this for now. - ZD_UNUSED(window); - ZD_UNUSED(w); - ZD_UNUSED(h); -} - int SDL_SetWindowFullscreen(SDL_Window* window, Uint32 flags) { if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == (flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) @@ -2059,48 +2022,6 @@ int SDL_SetWindowFullscreen(SDL_Window* window, Uint32 flags) return 0; } -SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags) -{ - ZD_UNUSED(index); - ZD_UNUSED(flags); - - static SDL_Renderer result; - result.window = window; - - return &result; -} -void SDL_DestroyRenderer(SDL_Renderer *renderer) -{ - ZD_UNUSED(renderer); -} - -SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h) -{ - ZD_UNUSED(format); - ZD_UNUSED(access); - ZD_UNUSED(w); - ZD_UNUSED(h); - - static SDL_Texture result; - result.window = renderer->window; - - return &result; -} -void SDL_DestroyTexture(SDL_Texture *texture) -{ - ZD_UNUSED(texture); -} - -int SDL_QueryTexture(SDL_Texture *texture, Uint32* format, int* access, int* w, int* h) -{ - if(format) *format = 0; //SDL_PIXELFORMAT_ABGR8888; - if(access) *access = SDL_TEXTUREACCESS_STREAMING; - if(w) *w = texture->window->w; - if(h) *h = texture->window->h; - return 0; -} - - int SDL_UpdateWindowSurface(SDL_Window *screen) { assert(NULL != screen); @@ -2176,31 +2097,34 @@ private: size_t m_modeIterator; }; + class CocoaFrameBuffer : public DFrameBuffer { public: CocoaFrameBuffer(int width, int height, bool fullscreen); ~CocoaFrameBuffer(); - bool Lock (bool buffer); - void Unlock (); - bool Relock (); - void ForceBuffering (bool force); - bool IsValid (); - void Update (); - PalEntry *GetPalette (); - void GetFlashedPalette (PalEntry pal[256]); - void UpdatePalette (); - bool SetGamma (float gamma); - bool SetFlash (PalEntry rgb, int amount); - void GetFlash (PalEntry &rgb, int &amount); - void SetFullscreen (bool fullscreen); - int GetPageCount (); - bool IsFullscreen (); + virtual bool IsValid(); - friend class CocoaVideo; + virtual bool Lock(bool buffer); + virtual void Unlock(); + virtual void Update(); + + virtual PalEntry* GetPalette(); + virtual void GetFlashedPalette(PalEntry pal[256]); + virtual void UpdatePalette(); + + virtual bool SetGamma(float gamma); + virtual bool SetFlash(PalEntry rgb, int amount); + virtual void GetFlash(PalEntry &rgb, int &amount); - virtual void SetVSync (bool vsync); + virtual int GetPageCount(); + + virtual bool IsFullscreen(); + + virtual void SetVSync(bool vsync); + + void SetFullscreen(bool fullscreen); private: PalEntry SourcePalette[256]; @@ -2211,102 +2135,96 @@ private: bool UpdatePending; SDL_Window *Screen; - SDL_Renderer *Renderer; - union - { - SDL_Texture *Texture; - SDL_Surface *Surface; - }; bool NeedPalUpdate; bool NeedGammaUpdate; - void UpdateColors (); - void ResetSDLRenderer (); - - CocoaFrameBuffer () {} + void UpdateColors(); }; -struct MiniModeInfo -{ - WORD Width, Height; -}; + +// --------------------------------------------------------------------------- EXTERN_CVAR (Float, Gamma) -CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CVAR (Int, vid_displaybits, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CVAR (Bool, vid_forcesurface, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (screen != NULL) + if (NULL != screen) { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); + screen->SetGamma(Gamma); } } -static MiniModeInfo WinModes[] = +CUSTOM_CVAR (Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - { 320, 200 }, - { 320, 240 }, - { 400, 225 }, // 16:9 - { 400, 300 }, - { 480, 270 }, // 16:9 - { 480, 360 }, - { 512, 288 }, // 16:9 - { 512, 384 }, - { 640, 360 }, // 16:9 - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, // 16:10 - { 720, 540 }, - { 800, 450 }, // 16:9 - { 800, 480 }, - { 800, 500 }, // 16:10 - { 800, 600 }, - { 848, 480 }, // 16:9 - { 960, 600 }, // 16:10 - { 960, 720 }, - { 1024, 576 }, // 16:9 - { 1024, 600 }, // 17:10 - { 1024, 640 }, // 16:10 - { 1024, 768 }, - { 1088, 612 }, // 16:9 - { 1152, 648 }, // 16:9 - { 1152, 720 }, // 16:10 - { 1152, 864 }, - { 1280, 720 }, // 16:9 - { 1280, 854 }, - { 1280, 800 }, // 16:10 - { 1280, 960 }, + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR (Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + + +// --------------------------------------------------------------------------- + + +static const struct MiniModeInfo +{ + uint16_t width; + uint16_t height; +} +VideoModes[] = +{ + { 320, 200 }, + { 320, 240 }, + { 400, 225 }, // 16:9 + { 400, 300 }, + { 480, 270 }, // 16:9 + { 480, 360 }, + { 512, 288 }, // 16:9 + { 512, 384 }, + { 640, 360 }, // 16:9 + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, // 16:10 + { 720, 540 }, + { 800, 450 }, // 16:9 + { 800, 480 }, + { 800, 500 }, // 16:10 + { 800, 600 }, + { 848, 480 }, // 16:9 + { 960, 600 }, // 16:10 + { 960, 720 }, + { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 + { 1024, 640 }, // 16:10 + { 1024, 768 }, + { 1088, 612 }, // 16:9 + { 1152, 648 }, // 16:9 + { 1152, 720 }, // 16:10 + { 1152, 864 }, + { 1280, 720 }, // 16:9 + { 1280, 854 }, + { 1280, 800 }, // 16:10 + { 1280, 960 }, { 1280, 1024 }, // 5:4 - { 1360, 768 }, // 16:9 - { 1366, 768 }, - { 1400, 787 }, // 16:9 - { 1400, 875 }, // 16:10 + { 1360, 768 }, // 16:9 + { 1366, 768 }, + { 1400, 787 }, // 16:9 + { 1400, 875 }, // 16:10 { 1400, 1050 }, - { 1440, 900 }, - { 1440, 960 }, + { 1440, 900 }, + { 1440, 960 }, { 1440, 1080 }, - { 1600, 900 }, // 16:9 + { 1600, 900 }, // 16:9 { 1600, 1000 }, // 16:10 { 1600, 1200 }, { 1920, 1080 }, @@ -2323,10 +2241,14 @@ static MiniModeInfo WinModes[] = { 5120, 2880 } }; + static cycle_t BlitCycles; static cycle_t FlipCycles; +// --------------------------------------------------------------------------- + + CocoaVideo::CocoaVideo(int dummy) : m_modeIterator(0) { @@ -2345,10 +2267,10 @@ bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) { ZD_UNUSED(letterbox); - if (m_modeIterator < sizeof(WinModes) / sizeof(WinModes[0])) + if (m_modeIterator < sizeof(VideoModes) / sizeof(VideoModes[0])) { - *width = WinModes[m_modeIterator].Width; - *height = WinModes[m_modeIterator].Height; + *width = VideoModes[m_modeIterator].width; + *height = VideoModes[m_modeIterator].height; ++m_modeIterator; return true; @@ -2357,7 +2279,7 @@ bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) return false; } -DFrameBuffer *CocoaVideo::CreateFrameBuffer(int width, int height, bool fullscreen, DFrameBuffer* old) +DFrameBuffer* CocoaVideo::CreateFrameBuffer(int width, int height, bool fullscreen, DFrameBuffer* old) { static int retry = 0; static int owidth, oheight; @@ -2368,15 +2290,14 @@ DFrameBuffer *CocoaVideo::CreateFrameBuffer(int width, int height, bool fullscre if (old != NULL) { // Reuse the old framebuffer if its attributes are the same CocoaFrameBuffer *fb = static_cast (old); - if (fb->Width == width && - fb->Height == height) + if (fb->GetWidth() == width && + fb->GetHeight() == height) { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; - - if (fsnow != fullscreen) + if (fb->IsFullscreen() != fullscreen) { fb->SetFullscreen (fullscreen); } + return old; } old->GetFlash (flashColor, flashAmount); @@ -2460,19 +2381,17 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) if (Screen == NULL) return; - Renderer = NULL; - Texture = NULL; - ResetSDLRenderer (); + GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); for (size_t i = 0; i < 256; ++i) { GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; } - memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); - UpdateColors (); + memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + UpdateColors(); - SetVSync (vid_vsync); + SetVSync(vid_vsync); } @@ -2480,13 +2399,6 @@ CocoaFrameBuffer::~CocoaFrameBuffer () { if(Screen) { - if (Renderer) - { - if (Texture) - SDL_DestroyTexture (Texture); - SDL_DestroyRenderer (Renderer); - } - SDL_DestroyWindow (Screen); } } @@ -2506,11 +2418,6 @@ bool CocoaFrameBuffer::Lock (bool buffered) return DSimpleCanvas::Lock (); } -bool CocoaFrameBuffer::Relock () -{ - return DSimpleCanvas::Lock (); -} - void CocoaFrameBuffer::Unlock () { if (UpdatePending && LockCount == 1) @@ -2546,11 +2453,11 @@ void CocoaFrameBuffer::Update () FlipCycles.Reset(); BlitCycles.Clock(); - GPfx.Convert(MemBuffer, Pitch, Texture->window->pixels, Texture->window->pitch, + GPfx.Convert(MemBuffer, Pitch, [appCtrl softwareRenderingBuffer], Width * BYTES_PER_PIXEL, Width, Height, FRACUNIT, FRACUNIT, 0, 0); FlipCycles.Clock(); - SDL_RenderPresent(Renderer); + SDL_UpdateWindowSurface(Screen); FlipCycles.Unclock(); BlitCycles.Unclock(); @@ -2559,20 +2466,20 @@ void CocoaFrameBuffer::Update () { bool Windowed = false; NeedGammaUpdate = false; - CalcGamma ((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); - CalcGamma ((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); - CalcGamma ((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); + CalcGamma((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); + CalcGamma((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); + CalcGamma((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); NeedPalUpdate = true; } if (NeedPalUpdate) { NeedPalUpdate = false; - UpdateColors (); + UpdateColors(); } } -void CocoaFrameBuffer::UpdateColors () +void CocoaFrameBuffer::UpdateColors() { PalEntry palette[256]; @@ -2590,7 +2497,7 @@ void CocoaFrameBuffer::UpdateColors () FlashAmount); } - GPfx.SetPalette (palette); + GPfx.SetPalette(palette); } PalEntry *CocoaFrameBuffer::GetPalette () @@ -2637,13 +2544,6 @@ void CocoaFrameBuffer::GetFlashedPalette (PalEntry pal[256]) void CocoaFrameBuffer::SetFullscreen (bool fullscreen) { SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - if (!fullscreen) - { - // Restore proper window size - SDL_SetWindowSize (Screen, Width, Height); - } - - ResetSDLRenderer (); } bool CocoaFrameBuffer::IsFullscreen () @@ -2651,40 +2551,6 @@ bool CocoaFrameBuffer::IsFullscreen () return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; } -void CocoaFrameBuffer::ResetSDLRenderer () -{ - if (Renderer) - { - if (Texture) - SDL_DestroyTexture (Texture); - SDL_DestroyRenderer (Renderer); - } - - Renderer = SDL_CreateRenderer (Screen, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE| - (vid_vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); - if (!Renderer) - return; - - Uint32 fmt = 0; -// switch(vid_displaybits) -// { -// default: fmt = SDL_PIXELFORMAT_ARGB8888; break; -// case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; -// case 24: fmt = SDL_PIXELFORMAT_RGB888; break; -// case 16: fmt = SDL_PIXELFORMAT_RGB565; break; -// case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; -// } - Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); - - Uint32 format; - SDL_QueryTexture(Texture, &format, NULL, NULL, NULL); - - Uint32 Rmask, Gmask, Bmask, Amask; - int bpp; - SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - GPfx.SetFormat (bpp, Rmask, Gmask, Bmask); -} - void CocoaFrameBuffer::SetVSync (bool vsync) { if (CGLContextObj context = CGLGetCurrentContext()) From ebc0916bc6008856809eab54091308dd8ce53e55 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Dec 2014 17:20:45 +0200 Subject: [PATCH 021/117] Cleanup video part of native OS X backend, phase 3 --- src/posix/cocoa/i_backend_cocoa.mm | 160 ++--------------------------- 1 file changed, 10 insertions(+), 150 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index a4ad1a4b8..b15c4b0ca 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1817,157 +1817,22 @@ bool I_SetCursor(FTexture* cursorpic) extern "C" { - typedef enum - { - SDL_FALSE = 0, - SDL_TRUE = 1 - } SDL_bool; - -typedef int8_t Sint8; -typedef uint8_t Uint8; -typedef int16_t Sint16; -typedef uint16_t Uint16; -typedef int32_t Sint32; -typedef uint32_t Uint32; - - typedef enum - { - SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ - SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ - SDL_WINDOW_SHOWN = 0x00000004, /**< window is visible */ - SDL_WINDOW_HIDDEN = 0x00000008, /**< window is not visible */ - SDL_WINDOW_BORDERLESS = 0x00000010, /**< no window decoration */ - SDL_WINDOW_RESIZABLE = 0x00000020, /**< window can be resized */ - SDL_WINDOW_MINIMIZED = 0x00000040, /**< window is minimized */ - SDL_WINDOW_MAXIMIZED = 0x00000080, /**< window is maximized */ - SDL_WINDOW_INPUT_GRABBED = 0x00000100, /**< window has grabbed input focus */ - SDL_WINDOW_INPUT_FOCUS = 0x00000200, /**< window has input focus */ - SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ - SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), - SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ - SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000 /**< window should be created in high-DPI mode if supported */ - } SDL_WindowFlags; - - - typedef struct SDL_Rect { - Sint16 x, y; - Uint16 w, h; - } SDL_Rect; - - typedef struct SDL_Color { - Uint8 r; - Uint8 g; - Uint8 b; - Uint8 unused; - } SDL_Color; - - - typedef struct SDL_Palette { - int ncolors; - SDL_Color *colors; - } SDL_Palette; - - typedef struct SDL_PixelFormat { - SDL_Palette *palette; - Uint8 BitsPerPixel; - Uint8 BytesPerPixel; - Uint8 Rloss; - Uint8 Gloss; - Uint8 Bloss; - Uint8 Aloss; - Uint8 Rshift; - Uint8 Gshift; - Uint8 Bshift; - Uint8 Ashift; - Uint32 Rmask; - Uint32 Gmask; - Uint32 Bmask; - Uint32 Amask; - - /** RGB color key information */ - Uint32 colorkey; - /** Alpha value information (per-surface alpha) */ - Uint8 alpha; - } SDL_PixelFormat; - - /** This structure should be treated as read-only, except for 'pixels', - * which, if not NULL, contains the raw pixel data for the surface. - */ - typedef struct SDL_Surface { - Uint32 flags; /**< Read-only */ - SDL_PixelFormat *format; /**< Read-only */ - int w, h; /**< Read-only */ - Uint16 pitch; /**< Read-only */ - void *pixels; /**< Read-write */ - int offset; /**< Private */ - - /** Hardware-specific surface info */ - struct private_hwdata *hwdata; - - /** clipping information */ - SDL_Rect clip_rect; /**< Read-only */ - Uint32 unused1; /**< for binary compatibility */ - - /** Allow recursive locks */ - Uint32 locked; /**< Private */ - - /** info for fast blit mapping to other surfaces */ - struct SDL_BlitMap *map; /**< Private */ - - /** format version, bumped at every change to invalidate blit maps */ - unsigned int format_version; /**< Private */ - - /** Reference count -- used when freeing surface */ - int refcount; /**< Read-mostly */ - } SDL_Surface; - - typedef enum - { - SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */ - SDL_RENDERER_ACCELERATED = 0x00000002, /**< The renderer uses hardware - acceleration */ - SDL_RENDERER_PRESENTVSYNC = 0x00000004, /**< Present is synchronized - with the refresh rate */ - SDL_RENDERER_TARGETTEXTURE = 0x00000008 /**< The renderer supports - rendering to texture */ - } SDL_RendererFlags; - - /** - * \brief Information on the capabilities of a render driver or context. - */ - typedef struct SDL_RendererInfo - { - const char *name; /**< The name of the renderer */ - Uint32 flags; /**< Supported ::SDL_RendererFlags */ - Uint32 num_texture_formats; /**< The number of available texture formats */ - Uint32 texture_formats[16]; /**< The available texture formats */ - int max_texture_width; /**< The maximimum texture width */ - int max_texture_height; /**< The maximimum texture height */ - } SDL_RendererInfo; - - /** - * \brief The access pattern allowed for a texture. - */ - typedef enum - { - SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */ - SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */ - SDL_TEXTUREACCESS_TARGET /**< Texture can be used as a render target */ - } SDL_TextureAccess; - +typedef enum +{ + SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ + SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ + SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), +} SDL_WindowFlags; struct SDL_Window { - Uint32 flags; + uint32_t flags; int w, h; int pitch; void *pixels; }; -struct SDL_Renderer { SDL_Window *window; }; -struct SDL_Texture { SDL_Window *window; }; - -SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int width, int height, Uint32 flags) +SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int width, int height, uint32_t flags) { [appCtrl changeVideoResolution:(SDL_WINDOW_FULLSCREEN_DESKTOP & flags) width:width @@ -1995,12 +1860,12 @@ void SDL_DestroyWindow(SDL_Window *window) ZD_UNUSED(window); } -Uint32 SDL_GetWindowFlags(SDL_Window *window) +uint32_t SDL_GetWindowFlags(SDL_Window *window) { return window->flags; } -int SDL_SetWindowFullscreen(SDL_Window* window, Uint32 flags) +int SDL_SetWindowFullscreen(SDL_Window* window, uint32_t flags) { if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == (flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) return 0; @@ -2069,11 +1934,6 @@ int SDL_UpdateWindowSurface(SDL_Window *screen) return 0; } -void SDL_RenderPresent(SDL_Renderer *renderer) -{ - SDL_UpdateWindowSurface(renderer->window); -} - } // extern "C" From e5a41a135821d48e65f460979b6f566c02ccc2fd Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 20 Dec 2014 16:57:00 -0600 Subject: [PATCH 022/117] - Added name filtering to all A_Damage/Kill/Remove functions. - A_DamageChildren(20,"Normal",0,"DoomImp") for example will only target actors of DoomImp specifically. --- src/thingdef/thingdef_codeptr.cpp | 158 +++++++++++++++++++++++------- wadsrc/static/actors/actor.txt | 34 +++---- 2 files changed, 138 insertions(+), 54 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 98451795b..23fe031c7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5068,12 +5068,15 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); - DoDamage(self, self, amount, DamageType, flags); + const PClass *c1 = self->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(self, self, amount, DamageType, flags); } //=========================================================================== @@ -5083,12 +5086,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); - if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); + if (self->target != NULL) + { + const PClass *c1 = self->target->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(self->target, self, amount, DamageType, flags); + } } //=========================================================================== @@ -5098,12 +5107,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); - if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); + if (self->tracer != NULL) + { + const PClass *c1 = self->tracer->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(self->tracer, self, amount, DamageType, flags); + } } //=========================================================================== @@ -5113,12 +5128,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); - if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); + if (self->master != NULL) + { + const PClass *c1 = self->master->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(self->master, self, amount, DamageType, flags); + } } //=========================================================================== @@ -5128,17 +5149,23 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); TThinkerIterator it; AActor * mo; while ( (mo = it.Next()) ) { - if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); + if (mo->master == self) + { + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(mo, self, amount, DamageType, flags); + } } } @@ -5149,10 +5176,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); TThinkerIterator it; AActor * mo; @@ -5161,7 +5189,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) { while ((mo = it.Next())) { - if (mo->master == self->master && mo != self) DoDamage(mo, self, amount, DamageType, flags); + if (mo->master == self->master && mo != self) + { + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoDamage(mo, self, amount, DamageType, flags); + } } } } @@ -5214,11 +5247,17 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); - if (self->target != NULL) DoKill(self->target, self, damagetype, flags); + if (self->target != NULL) + { + const PClass *c1 = self->target->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoKill(self->target, self, damagetype, flags); + } } //=========================================================================== @@ -5228,11 +5267,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); - if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); + if (self->tracer != NULL) + { + const PClass *c1 = self->tracer->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoKill(self->tracer, self, damagetype, flags); + } } //=========================================================================== @@ -5242,11 +5287,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); - if (self->master != NULL) DoKill(self->master, self, damagetype, flags); + if (self->master != NULL) + { + const PClass *c1 = self->master->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoKill(self->master, self, damagetype, flags); + } } //=========================================================================== @@ -5256,16 +5307,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); TThinkerIterator it; AActor *mo; while ( (mo = it.Next()) ) { - if (mo->master == self) DoKill(mo, self, damagetype, flags); + if (mo->master == self) + { + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoKill(mo, self, damagetype, flags); + } } } @@ -5276,9 +5333,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); TThinkerIterator it; AActor *mo; @@ -5287,7 +5345,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { while ( (mo = it.Next()) ) { - if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); + if (mo->master == self->master && mo != self) + { + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoKill(mo, self, damagetype, flags); + } } } } @@ -5333,11 +5396,15 @@ static void DoRemove(AActor *removetarget, int flags) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); - if (self->master != NULL) + ACTION_PARAM_CLASS(filter, 1); + + if (self->target != NULL) { - DoRemove(self->target, flags); + const PClass *c1 = self->target->GetClass(); + if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) + DoRemove(self->target, flags); } } @@ -5348,11 +5415,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); - if (self->master != NULL) + ACTION_PARAM_CLASS(filter, 1); + + if (self->tracer != NULL) { - DoRemove(self->tracer, flags); + const PClass *c1 = self->tracer->GetClass(); + if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) + DoRemove(self->tracer, flags); } } @@ -5363,11 +5434,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + if (self->master != NULL) { - DoRemove(self->master, flags); + const PClass *c1 = self->master->GetClass(); + if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) + DoRemove(self->master, flags); } } @@ -5380,15 +5455,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) { TThinkerIterator it; AActor *mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_BOOL(removeall, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + while ((mo = it.Next()) != NULL) { if (mo->master == self && (mo->health <= 0 || removeall)) { - DoRemove(mo, flags); + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoRemove(mo, flags); } } } @@ -5402,9 +5481,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) { TThinkerIterator it; AActor *mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_BOOL(removeall, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); if (self->master != NULL) { @@ -5412,7 +5492,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) { if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) { - DoRemove(mo, flags); + const PClass *c1 = mo->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoRemove(mo, flags); } } } @@ -5425,15 +5507,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(removee, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); AActor *reference = COPY_AAPTR(self, removee); - if (reference != NULL) { - DoRemove(reference, flags); + const PClass *c1 = reference->GetClass(); + if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) + DoRemove(reference, flags); } } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index cd718b1c0..910bd0778 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -237,12 +237,6 @@ ACTOR Actor native //: Thinker action native A_ChangeFlag(string flagname, bool value); action native A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT); action native A_JumpIf(bool expression, state label); - action native A_RemoveMaster(int flags = 0); - action native A_RemoveChildren(bool removeall = false, int flags = 0); - action native A_RemoveSiblings(bool removeall = false, int flags = 0); - action native A_KillMaster(name damagetype = "none", int flags = 0); - action native A_KillChildren(name damagetype = "none", int flags = 0); - action native A_KillSiblings(name damagetype = "none", int flags = 0); action native A_RaiseMaster(bool copy = 0); action native A_RaiseChildren(bool copy = 0); action native A_RaiseSiblings(bool copy = 0); @@ -278,9 +272,6 @@ ACTOR Actor native //: Thinker action native A_CheckLOF(state jump, int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT); action native A_JumpIfTargetInLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); action native A_JumpIfInTargetLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); - action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0); - action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0); - action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0); action native A_SelectWeapon(class whichweapon); action native A_Punch(); action native A_Feathers(); @@ -307,14 +298,23 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); - action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0); - action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0); - action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0); - action native A_KillTarget(name damagetype = "none", int flags = 0); - action native A_KillTracer(name damagetype = "none", int flags = 0); - action native A_RemoveTarget(int flags = 0); - action native A_RemoveTracer(int flags = 0); - action native A_Remove(int removee, int flags = 0); + action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter); + action native A_KillTarget(name damagetype = "none", int flags = 0, class filter); + action native A_KillMaster(name damagetype = "none", int flags = 0, class filter); + action native A_KillTracer(name damagetype = "none", int flags = 0, class filter); + action native A_KillChildren(name damagetype = "none", int flags = 0, class filter); + action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter); + action native A_RemoveTarget(int flags = 0, class filter); + action native A_RemoveMaster(int flags = 0, class filter); + action native A_RemoveTracer(int flags = 0, class filter); + action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter); + action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter); + action native A_Remove(int removee, int flags = 0, class filter); action native A_GiveToChildren(class itemtype, int amount = 0); action native A_GiveToSiblings(class itemtype, int amount = 0); action native A_TakeFromChildren(class itemtype, int amount = 0); From c168761edacc5aa69bf89f21e3eeb8e769c91c61 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 20 Dec 2014 22:51:43 -0600 Subject: [PATCH 023/117] - Couple additional fixes: - The wiki said the minimum distance to teleport defaults to 0, actor.txt on the other hand said otherwise. I was wondering why it was still broken somewhat... - Prevent stickiness from happening, a.k.a. getting stuck in ceiling or floor and letting the engine unstick the actor. This caused velocity loss. --- src/thingdef/thingdef_codeptr.cpp | 21 ++++++++++++++------- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 23fe031c7..485db3d86 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4199,16 +4199,23 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) fixed_t prevX = self->x; fixed_t prevY = self->y; fixed_t prevZ = self->z; + fixed_t aboveFloor = spot->z - spot->floorz; + fixed_t finalz = spot->floorz + aboveFloor; + + if (spot->z + self->height > spot->ceilingz) + finalz = spot->ceilingz - self->height; + else if (spot->z < spot->floorz) + finalz = spot->floorz; + //Take precedence and cooperate with telefragging first. - bool teleResult = P_TeleportMove(self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG); - - if ((!(teleResult)) && (Flags & TF_FORCED)) - { - //If for some reason the original move didn't work, regardless of telefrag, force it to move. - self->SetOrigin(spot->x, spot->y, spot->z); - teleResult = true; + bool teleResult = P_TeleportMove(self, spot->x, spot->y, finalz, Flags & TF_TELEFRAG); + if (Flags & TF_FORCED) + { + //If for some reason the original move didn't work, regardless of telefrag, force it to move. + self->SetOrigin(spot->x, spot->y, finalz); + teleResult = true; } if (teleResult) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 910bd0778..7bc6b8df4 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -244,7 +244,7 @@ ACTOR Actor native //: Thinker action native A_CheckCeiling(state label); action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); - action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 128, float maxdist = 0); + action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 0); action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = ""); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); From 18c7709007b32bdbc84c98a9f66e3ca24a9626d4 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 21 Dec 2014 12:35:43 +0200 Subject: [PATCH 024/117] Cleanup video part of native OS X backend, phase 4 --- src/posix/cocoa/i_backend_cocoa.mm | 146 ++++++++++++++--------------- 1 file changed, 69 insertions(+), 77 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index b15c4b0ca..b1d737912 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -414,6 +414,8 @@ int OriginalMain(int argc, char** argv) } I_StartupJoysticks(); + atterm(I_ShutdownJoysticks); + C_InitConsole(80 * 8, 25 * 8, false); D_DoomMain(); } @@ -1166,8 +1168,6 @@ namespace - (void)applicationDidFinishLaunching:(NSNotification*)aNotification; -- (void)applicationWillTerminate:(NSNotification*)aNotification; - - (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; - (int)multisample; @@ -1371,17 +1371,6 @@ static bool s_fullscreenNewAPI; } -- (void)applicationWillTerminate:(NSNotification*)aNotification -{ - ZD_UNUSED(aNotification); - - // Hide window as nothing will be rendered at this point - [m_window orderOut:nil]; - - I_ShutdownJoysticks(); -} - - - (int)multisample { return m_multisample; @@ -1975,7 +1964,7 @@ public: virtual void UpdatePalette(); virtual bool SetGamma(float gamma); - virtual bool SetFlash(PalEntry rgb, int amount); + virtual bool SetFlash(PalEntry rgb, int amount); virtual void GetFlash(PalEntry &rgb, int &amount); virtual int GetPageCount(); @@ -1987,18 +1976,20 @@ public: void SetFullscreen(bool fullscreen); private: - PalEntry SourcePalette[256]; - BYTE GammaTable[3][256]; - PalEntry Flash; - int FlashAmount; - float Gamma; - bool UpdatePending; + PalEntry m_palette[256]; + bool m_needPaletteUpdate; + + BYTE m_gammaTable[3][256]; + float m_gamma; + bool m_needGammaUpdate; + + PalEntry m_flashColor; + int m_flashAmount; + + bool m_isUpdatePending; SDL_Window *Screen; - bool NeedPalUpdate; - bool NeedGammaUpdate; - void UpdateColors(); }; @@ -2036,7 +2027,7 @@ CUSTOM_CVAR (Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // --------------------------------------------------------------------------- -static const struct MiniModeInfo +static const struct { uint16_t width; uint16_t height; @@ -2125,6 +2116,8 @@ void CocoaVideo::StartModeIterator(int bits, bool fs) bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) { + assert(NULL != width); + assert(NULL != height); ZD_UNUSED(letterbox); if (m_modeIterator < sizeof(VideoModes) / sizeof(VideoModes[0])) @@ -2226,11 +2219,11 @@ void CocoaVideo::SetWindowedScale (float scale) CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) : DFrameBuffer(width, height) -, FlashAmount(0) -, Gamma(0.0f) -, UpdatePending(false) -, NeedPalUpdate(false) -, NeedGammaUpdate(false) +, m_needPaletteUpdate(false) +, m_gamma(0.0f) +, m_needGammaUpdate(false) +, m_flashAmount(0) +, m_isUpdatePending(false) { FString caption; caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); @@ -2245,10 +2238,10 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) for (size_t i = 0; i < 256; ++i) { - GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; + m_gammaTable[0][i] = m_gammaTable[1][i] = m_gammaTable[2][i] = i; } - memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + memcpy(m_palette, GPalette.BaseColors, sizeof(PalEntry) * 256); UpdateColors(); SetVSync(vid_vsync); @@ -2280,7 +2273,7 @@ bool CocoaFrameBuffer::Lock (bool buffered) void CocoaFrameBuffer::Unlock () { - if (UpdatePending && LockCount == 1) + if (m_isUpdatePending && LockCount == 1) { Update (); } @@ -2297,7 +2290,7 @@ void CocoaFrameBuffer::Update () { if (LockCount > 0) { - UpdatePending = true; + m_isUpdatePending = true; --LockCount; } return; @@ -2307,7 +2300,7 @@ void CocoaFrameBuffer::Update () Buffer = NULL; LockCount = 0; - UpdatePending = false; + m_isUpdatePending = false; BlitCycles.Reset(); FlipCycles.Reset(); @@ -2322,19 +2315,19 @@ void CocoaFrameBuffer::Update () BlitCycles.Unclock(); - if (NeedGammaUpdate) + if (m_needGammaUpdate) { bool Windowed = false; - NeedGammaUpdate = false; - CalcGamma((Windowed || rgamma == 0.f) ? Gamma : (Gamma * rgamma), GammaTable[0]); - CalcGamma((Windowed || ggamma == 0.f) ? Gamma : (Gamma * ggamma), GammaTable[1]); - CalcGamma((Windowed || bgamma == 0.f) ? Gamma : (Gamma * bgamma), GammaTable[2]); - NeedPalUpdate = true; + m_needGammaUpdate = false; + CalcGamma((Windowed || rgamma == 0.f) ? m_gamma : (m_gamma * rgamma), m_gammaTable[0]); + CalcGamma((Windowed || ggamma == 0.f) ? m_gamma : (m_gamma * ggamma), m_gammaTable[1]); + CalcGamma((Windowed || bgamma == 0.f) ? m_gamma : (m_gamma * bgamma), m_gammaTable[2]); + m_needPaletteUpdate = true; } - if (NeedPalUpdate) + if (m_needPaletteUpdate) { - NeedPalUpdate = false; + m_needPaletteUpdate = false; UpdateColors(); } } @@ -2345,16 +2338,16 @@ void CocoaFrameBuffer::UpdateColors() for (size_t i = 0; i < 256; ++i) { - palette[i].r = GammaTable[0][SourcePalette[i].r]; - palette[i].g = GammaTable[1][SourcePalette[i].g]; - palette[i].b = GammaTable[2][SourcePalette[i].b]; + palette[i].r = m_gammaTable[0][m_palette[i].r]; + palette[i].g = m_gammaTable[1][m_palette[i].g]; + palette[i].b = m_gammaTable[2][m_palette[i].b]; } - if (FlashAmount) + if (m_flashAmount) { DoBlending(palette, palette, 256, - GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], - FlashAmount); + m_gammaTable[0][m_flashColor.r], m_gammaTable[1][m_flashColor.g], m_gammaTable[2][m_flashColor.b], + m_flashAmount); } GPfx.SetPalette(palette); @@ -2362,42 +2355,44 @@ void CocoaFrameBuffer::UpdateColors() PalEntry *CocoaFrameBuffer::GetPalette () { - return SourcePalette; + return m_palette; } void CocoaFrameBuffer::UpdatePalette() { - NeedPalUpdate = true; + m_needPaletteUpdate = true; } -bool CocoaFrameBuffer::SetGamma (float gamma) +bool CocoaFrameBuffer::SetGamma(float gamma) { - Gamma = gamma; - NeedGammaUpdate = true; + m_gamma = gamma; + m_needGammaUpdate = true; + return true; } -bool CocoaFrameBuffer::SetFlash (PalEntry rgb, int amount) +bool CocoaFrameBuffer::SetFlash(PalEntry rgb, int amount) { - Flash = rgb; - FlashAmount = amount; - NeedPalUpdate = true; + m_flashColor = rgb; + m_flashAmount = amount; + m_needPaletteUpdate = true; + return true; } -void CocoaFrameBuffer::GetFlash (PalEntry &rgb, int &amount) +void CocoaFrameBuffer::GetFlash(PalEntry &rgb, int &amount) { - rgb = Flash; - amount = FlashAmount; + rgb = m_flashColor; + amount = m_flashAmount; } -// Q: Should I gamma adjust the returned palette? -void CocoaFrameBuffer::GetFlashedPalette (PalEntry pal[256]) +void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) { - memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); - if (FlashAmount) + memcpy(pal, m_palette, sizeof m_palette); + + if (0 != m_flashAmount) { - DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); + DoBlending (pal, pal, 256, m_flashColor.r, m_flashColor.g, m_flashColor.b, m_flashAmount); } } @@ -2413,25 +2408,22 @@ bool CocoaFrameBuffer::IsFullscreen () void CocoaFrameBuffer::SetVSync (bool vsync) { - if (CGLContextObj context = CGLGetCurrentContext()) - { #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - // Inconsistency between 10.4 and 10.5 SDKs: - // third argument of CGLSetParameter() is const long* on 10.4 and const GLint* on 10.5 - // So, GLint typedef'ed to long instead of int to workaround this issue - typedef long GLint; + const long value = vsync ? 1 : 0; +#else // 10.5 or newer + const GLint value = vsync ? 1 : 0; #endif // prior to 10.5 - const GLint value = vsync ? 1 : 0; - CGLSetParameter(context, kCGLCPSwapInterval, &value); - } + [[NSOpenGLContext currentContext] setValues:&value + forParameter:NSOpenGLCPSwapInterval]; } + ADD_STAT(blit) { - FString out; - out.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); - return out; + FString result; + result.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); + return result; } From 5a472e815b9df50079bebbc38c157d3256ace894 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 21 Dec 2014 09:31:24 -0600 Subject: [PATCH 025/117] - Added species checking. - Added two more flags for each of the functions, EXFILTER and EXSPECIES. - Stands for "exclude filter/species" and makes the function not take them into account. - Cleaned up the code and placed all the checking in their own subfunctions. --- src/thingdef/thingdef_codeptr.cpp | 213 ++++++++++++++++------------- wadsrc/static/actors/actor.txt | 34 ++--- wadsrc/static/actors/constants.txt | 6 + 3 files changed, 138 insertions(+), 115 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 485db3d86..e61f33ea4 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5023,6 +5023,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) self->Speed = speed; } +static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) +{ + FName spec = mo->Species; + return (!(species) || !(stricmp(species, "")) || (species && ((exclude) ? (spec != species) : (spec == species)))); +} + +static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) +{ + const PClass *c1 = mo->GetClass(); + return (!(filter) || (filter == NULL) || (filter && ((exclude) ? (c1 != filter) : (c1 == filter)))); +} + //=========================================================================== // // Common A_Damage handler @@ -5040,9 +5052,11 @@ enum DMSS DMSS_NOFACTOR = 8, DMSS_FOILBUDDHA = 16, DMSS_NOPROTECT = 32, + DMSS_EXFILTER = 64, + DMSS_EXSPECIES = 128, }; -static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) { int dmgFlags = 0; if (flags & DMSS_FOILINVUL) @@ -5058,13 +5072,19 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. dmgFlags += DMG_NO_PROTECT; - if (amount > 0) - P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. - - else if (amount < 0) + bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); + + if (filterpass && speciespass) { - amount = -amount; - P_GiveBody(dmgtarget, amount); + if (amount > 0) + P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. + + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } } } @@ -5075,15 +5095,14 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); - const PClass *c1 = self->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(self, self, amount, DamageType, flags); + DoDamage(self, self, amount, DamageType, flags, filter, species); } //=========================================================================== @@ -5093,17 +5112,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); if (self->target != NULL) { - const PClass *c1 = self->target->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(self->target, self, amount, DamageType, flags); + DoDamage(self->target, self, amount, DamageType, flags, filter, species); } } @@ -5114,17 +5132,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); if (self->tracer != NULL) { - const PClass *c1 = self->tracer->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(self->tracer, self, amount, DamageType, flags); + DoDamage(self->tracer, self, amount, DamageType, flags, filter, species); } } @@ -5135,17 +5152,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); if (self->master != NULL) { - const PClass *c1 = self->master->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(self->master, self, amount, DamageType, flags); + DoDamage(self->master, self, amount, DamageType, flags, filter, species); } } @@ -5156,11 +5172,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); TThinkerIterator it; AActor * mo; @@ -5169,9 +5186,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) { if (mo->master == self) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(mo, self, amount, DamageType, flags); + DoDamage(mo, self, amount, DamageType, flags, filter, species); } } } @@ -5183,11 +5198,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); TThinkerIterator it; AActor * mo; @@ -5198,9 +5214,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) { if (mo->master == self->master && mo != self) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoDamage(mo, self, amount, DamageType, flags); + DoDamage(mo, self, amount, DamageType, flags, filter, species); } } } @@ -5218,9 +5232,11 @@ enum KILS KILS_KILLMISSILES = 1 << 1, KILS_NOMONSTERS = 1 << 2, KILS_FOILBUDDHA = 1 << 3, + KILS_EXFILTER = 1 << 4, + KILS_EXSPECIES = 1 << 5, }; -static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) { int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; @@ -5229,20 +5245,25 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags if (KILS_FOILBUDDHA) dmgFlags += DMG_FOILBUDDHA; - if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); + if (filterpass && speciespass) //Check this first. I think it'll save the engine a lot more time this way. { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && - (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) { - P_ExplodeMissile(killtarget, NULL, NULL); + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && + (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(killtarget, NULL, NULL); + } } - } - if (!(flags & KILS_NOMONSTERS)) - { + if (!(flags & KILS_NOMONSTERS)) + { P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); + } } } @@ -5254,16 +5275,15 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); if (self->target != NULL) { - const PClass *c1 = self->target->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoKill(self->target, self, damagetype, flags); + DoKill(self->target, self, damagetype, flags, filter, species); } } @@ -5274,16 +5294,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); if (self->tracer != NULL) { - const PClass *c1 = self->tracer->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoKill(self->tracer, self, damagetype, flags); + DoKill(self->tracer, self, damagetype, flags, filter, species); } } @@ -5294,16 +5313,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); if (self->master != NULL) { - const PClass *c1 = self->master->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoKill(self->master, self, damagetype, flags); + DoKill(self->master, self, damagetype, flags, filter, species); } } @@ -5314,10 +5332,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); TThinkerIterator it; AActor *mo; @@ -5326,9 +5345,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { if (mo->master == self) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoKill(mo, self, damagetype, flags); + DoKill(mo, self, damagetype, flags, filter, species); } } } @@ -5340,10 +5357,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); TThinkerIterator it; AActor *mo; @@ -5354,9 +5372,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { if (mo->master == self->master && mo != self) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoKill(mo, self, damagetype, flags); + DoKill(mo, self, damagetype, flags, filter, species); } } } @@ -5374,25 +5390,32 @@ enum RMVF_flags RMVF_NOMONSTERS = 1 << 1, RMVF_MISC = 1 << 2, RMVF_EVERYTHING = 1 << 3, + RMVF_EXFILTER = 1 << 4, + RMVF_EXSPECIES = 1 << 5, }; -static void DoRemove(AActor *removetarget, int flags) +static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) { - if ((flags & RMVF_EVERYTHING)) + bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); + if (filterpass && speciespass) { - P_RemoveThing(removetarget); - } - if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) - { - P_RemoveThing(removetarget); + if ((flags & RMVF_EVERYTHING)) + { + P_RemoveThing(removetarget); + } + if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) + { + P_RemoveThing(removetarget); + } } } @@ -5406,12 +5429,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); if (self->target != NULL) { - const PClass *c1 = self->target->GetClass(); - if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) - DoRemove(self->target, flags); + DoRemove(self->target, flags, filter, species); } } @@ -5425,12 +5447,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); if (self->tracer != NULL) { - const PClass *c1 = self->tracer->GetClass(); - if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) - DoRemove(self->tracer, flags); + DoRemove(self->tracer, flags, filter, species); } } @@ -5444,12 +5465,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) ACTION_PARAM_START(2); ACTION_PARAM_INT(flags, 0); ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); if (self->master != NULL) { - const PClass *c1 = self->master->GetClass(); - if ((filter && (c1 == filter)) || (filter == NULL) || !(filter)) - DoRemove(self->master, flags); + DoRemove(self->master, flags, filter, species); } } @@ -5462,19 +5482,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) { TThinkerIterator it; AActor *mo; - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_BOOL(removeall, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); while ((mo = it.Next()) != NULL) { if (mo->master == self && (mo->health <= 0 || removeall)) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoRemove(mo, flags); + DoRemove(mo, flags, filter, species); } } } @@ -5488,10 +5507,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) { TThinkerIterator it; AActor *mo; - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_BOOL(removeall, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); if (self->master != NULL) { @@ -5499,9 +5519,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) { if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) { - const PClass *c1 = mo->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoRemove(mo, flags); + DoRemove(mo, flags, filter, species); } } } @@ -5514,17 +5532,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(4); ACTION_PARAM_INT(removee, 0); ACTION_PARAM_INT(flags, 1); ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); AActor *reference = COPY_AAPTR(self, removee); if (reference != NULL) { - const PClass *c1 = reference->GetClass(); - if (!(filter) || (filter == NULL) || (filter && (c1 == filter))) - DoRemove(reference, flags); + DoRemove(reference, flags, filter, species); } } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 7bc6b8df4..19260ae7e 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -298,23 +298,23 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); - action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter); - action native A_KillTarget(name damagetype = "none", int flags = 0, class filter); - action native A_KillMaster(name damagetype = "none", int flags = 0, class filter); - action native A_KillTracer(name damagetype = "none", int flags = 0, class filter); - action native A_KillChildren(name damagetype = "none", int flags = 0, class filter); - action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter); - action native A_RemoveTarget(int flags = 0, class filter); - action native A_RemoveMaster(int flags = 0, class filter); - action native A_RemoveTracer(int flags = 0, class filter); - action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter); - action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter); - action native A_Remove(int removee, int flags = 0, class filter); + action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter, name species); + action native A_KillTarget(name damagetype = "none", int flags = 0, class filter, name species); + action native A_KillMaster(name damagetype = "none", int flags = 0, class filter, name species); + action native A_KillTracer(name damagetype = "none", int flags = 0, class filter, name species); + action native A_KillChildren(name damagetype = "none", int flags = 0, class filter, name species); + action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter, name species); + action native A_RemoveTarget(int flags = 0, class filter, name species); + action native A_RemoveMaster(int flags = 0, class filter, name species); + action native A_RemoveTracer(int flags = 0, class filter, name species); + action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter, name species); + action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter, name species); + action native A_Remove(int removee, int flags = 0, class filter, name species); action native A_GiveToChildren(class itemtype, int amount = 0); action native A_GiveToSiblings(class itemtype, int amount = 0); action native A_TakeFromChildren(class itemtype, int amount = 0); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 756fae981..1d4252188 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -401,6 +401,8 @@ const int KILS_FOILINVUL = 1; const int KILS_KILLMISSILES = 2; const int KILS_NOMONSTERS = 4; const int KILS_FOILBUDDHA = 8; +const int KILS_EXFILTER = 16; +const int KILS_EXSPECIES = 32; // Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series const int DMSS_FOILINVUL = 1; @@ -409,6 +411,8 @@ const int DMSS_KILL = 4; const int DMSS_NOFACTOR = 8; const int DMSS_FOILBUDDHA = 16; const int DMSS_NOPROTECT = 32; +const int DMSS_EXFILTER = 64; +const int DMSS_EXSPECIES = 128; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; @@ -422,6 +426,8 @@ enum RMVF_NOMONSTERS = 1 << 1, RMVF_MISC = 1 << 2, RMVF_EVERYTHING = 1 << 3, + RMVF_EXFILTER = 1 << 4, + RMVF_EXSPECIES = 1 << 5, }; // Flags for A_Fade* From 2c7a3f2ebaa542364a90ed0ad7836e0cbe10a790 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 21 Dec 2014 10:38:51 -0600 Subject: [PATCH 026/117] - Optimized DoDamage and DoKill. - Do a filter and species check first to save time. - Added DMSS/KILS/RMVF_EITHER, which means if the actor is of type or species, it counts. - A_DamageTarget(20,"Normal",DMSS_EITHER,"DoomImp","CyberdemonSpecies") - This affects actor DoomImp, and anything that's of species CyberdemonSpecies. - Added a little more documentation via comments. --- src/thingdef/thingdef_codeptr.cpp | 102 ++++++++++++++++------------- wadsrc/static/actors/constants.txt | 50 ++++++++------ 2 files changed, 85 insertions(+), 67 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index e61f33ea4..1edfc4f32 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5039,44 +5039,51 @@ static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) // // Common A_Damage handler // -// A_Damage* (int amount, str damagetype, int flags) +// A_Damage* (int amount, str damagetype, int flags, str filter, str species) // Damages the specified actor by the specified amount. Negative values heal. +// Flags: See below. +// Filter: Specified actor is the only type allowed to be affected. +// Species: Specified species is the only type allowed to be affected. +// +// Examples: +// A_Damage(20,"Normal",DMSS_FOILINVUL,0,"DemonicSpecies") <--Only actors +// with a species "DemonicSpecies" will be affected. Use 0 to not filter by actor. // //=========================================================================== enum DMSS { - DMSS_FOILINVUL = 1, - DMSS_AFFECTARMOR = 2, - DMSS_KILL = 4, - DMSS_NOFACTOR = 8, - DMSS_FOILBUDDHA = 16, - DMSS_NOPROTECT = 32, - DMSS_EXFILTER = 64, - DMSS_EXSPECIES = 128, + DMSS_FOILINVUL = 1, //Foil invulnerability + DMSS_AFFECTARMOR = 2, //Make it affect armor + DMSS_KILL = 4, //Damages them for their current health + DMSS_NOFACTOR = 8, //Ignore DamageFactors + DMSS_FOILBUDDHA = 16, //Can kill actors with Buddha flag, except the player. + DMSS_NOPROTECT = 32, //Ignores PowerProtection entirely + DMSS_EXFILTER = 64, //Changes filter into a blacklisted class instead of whitelisted. + DMSS_EXSPECIES = 128, // ^ but with species instead. + DMSS_EITHER = 256, //Allow either type or species to be affected. }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) { - int dmgFlags = 0; - if (flags & DMSS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (flags & DMSS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor - dmgFlags += DMG_NO_FACTOR; - if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor - dmgFlags += DMG_NO_ARMOR; - if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. - amount += dmgtarget->health; - if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. - dmgFlags += DMG_NO_PROTECT; - bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); - - if (filterpass && speciespass) + if ((flags & DMSS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) { + int dmgFlags = 0; + if (flags & DMSS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (flags & DMSS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor + dmgFlags += DMG_NO_FACTOR; + if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor + dmgFlags += DMG_NO_ARMOR; + if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. + amount += dmgtarget->health; + if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. + dmgFlags += DMG_NO_PROTECT; + if (amount > 0) P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. @@ -5228,27 +5235,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) //=========================================================================== enum KILS { - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, - KILS_FOILBUDDHA = 1 << 3, - KILS_EXFILTER = 1 << 4, - KILS_EXSPECIES = 1 << 5, + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, + KILS_FOILBUDDHA = 1 << 3, + KILS_EXFILTER = 1 << 4, + KILS_EXSPECIES = 1 << 5, + KILS_EITHER = 1 << 6, }; static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) { - int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; - - if (KILS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (KILS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); - if (filterpass && speciespass) //Check this first. I think it'll save the engine a lot more time this way. + if ((flags & KILS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) //Check this first. I think it'll save the engine a lot more time this way. { + int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; + + if (KILS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (KILS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) { //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! @@ -5386,19 +5395,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) enum RMVF_flags { - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3, - RMVF_EXFILTER = 1 << 4, - RMVF_EXSPECIES = 1 << 5, + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, + RMVF_EXFILTER = 1 << 4, + RMVF_EXSPECIES = 1 << 5, + RMVF_EITHER = 1 << 6, }; static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) { bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); - if (filterpass && speciespass) + if ((flags & RMVF_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) { if ((flags & RMVF_EVERYTHING)) { diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1d4252188..e8ba15d46 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -396,23 +396,30 @@ enum }; // Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series - -const int KILS_FOILINVUL = 1; -const int KILS_KILLMISSILES = 2; -const int KILS_NOMONSTERS = 4; -const int KILS_FOILBUDDHA = 8; -const int KILS_EXFILTER = 16; -const int KILS_EXSPECIES = 32; +enum +{ + KILS_FOILINVUL = 0x00000001, + KILS_KILLMISSILES = 0x00000002, + KILS_NOMONSTERS = 0x00000004, + KILS_FOILBUDDHA = 0x00000008, + KILS_EXFILTER = 0x00000010, + KILS_EXSPECIES = 0x00000020, + KILS_EITHER = 0x00000040, +}; // Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series -const int DMSS_FOILINVUL = 1; -const int DMSS_AFFECTARMOR = 2; -const int DMSS_KILL = 4; -const int DMSS_NOFACTOR = 8; -const int DMSS_FOILBUDDHA = 16; -const int DMSS_NOPROTECT = 32; -const int DMSS_EXFILTER = 64; -const int DMSS_EXSPECIES = 128; +enum +{ + DMSS_FOILINVUL = 0x00000001, + DMSS_AFFECTARMOR = 0x00000002, + DMSS_KILL = 0x00000004, + DMSS_NOFACTOR = 0x00000008, + DMSS_FOILBUDDHA = 0x00000010, + DMSS_NOPROTECT = 0x00000020, + DMSS_EXFILTER = 0x00000040, + DMSS_EXSPECIES = 0x00000080, + DMSS_EITHER = 0x00000100, +}; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; @@ -422,12 +429,13 @@ const int AMF_EMITFROMTARGET = 4; // Flags for A_Remove* enum { - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3, - RMVF_EXFILTER = 1 << 4, - RMVF_EXSPECIES = 1 << 5, + RMVF_MISSILES = 0x00000001, + RMVF_NOMONSTERS = 0x00000002, + RMVF_MISC = 0x00000004, + RMVF_EVERYTHING = 0x00000008, + RMVF_EXFILTER = 0x00000010, + RMVF_EXSPECIES = 0x00000020, + RMVF_EITHER = 0x00000040, }; // Flags for A_Fade* From 91bfe4cceb8648b0c1c84622d212f4e03a4988e1 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 21 Dec 2014 12:29:19 -0600 Subject: [PATCH 027/117] - Added pointers for A_CustomMissile and A_CustomBulletAttack. --- src/p_enemy.h | 2 ++ src/thingdef/thingdef_codeptr.cpp | 25 ++++++++++++++++++------- wadsrc/static/actors/actor.txt | 4 ++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/p_enemy.h b/src/p_enemy.h index ff40c7003..8c5516ead 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -73,6 +73,8 @@ DECLARE_ACTION(A_BossDeath) void A_Chase(AActor *self); void A_FaceTarget (AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); +void A_FaceMaster(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); +void A_FaceTracer(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); bool A_RaiseMobj (AActor *, fixed_t speed); bool A_SinkMobj (AActor *, fixed_t speed); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 98451795b..0f37efa18 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -907,20 +907,23 @@ enum CM_Flags DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) { - ACTION_PARAM_START(6); + ACTION_PARAM_START(7); ACTION_PARAM_CLASS(ti, 0); ACTION_PARAM_FIXED(SpawnHeight, 1); ACTION_PARAM_INT(Spawnofs_XY, 2); ACTION_PARAM_ANGLE(Angle, 3); ACTION_PARAM_INT(flags, 4); ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_INT(ptr, 6); + + AActor *ref = COPY_AAPTR(self, ptr); int aimmode = flags & CMF_AIMMODE; AActor * targ; AActor * missile; - if (self->target != NULL || aimmode==2) + if (ref != NULL || aimmode==2) { if (ti) { @@ -937,14 +940,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) self->x += x; self->y += y; self->z += z; - missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); + missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, ref, ti, false); self->x -= x; self->y -= y; self->z -= z; break; case 1: - missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, self->target, ti, false); + missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, ref, ti, false); break; case 2: @@ -1056,7 +1059,7 @@ enum CBA_Flags DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) { - ACTION_PARAM_START(7); + ACTION_PARAM_START(8); ACTION_PARAM_ANGLE(Spread_XY, 0); ACTION_PARAM_ANGLE(Spread_Z, 1); ACTION_PARAM_INT(NumBullets, 2); @@ -1064,6 +1067,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) ACTION_PARAM_CLASS(pufftype, 4); ACTION_PARAM_FIXED(Range, 5); ACTION_PARAM_INT(Flags, 6); + ACTION_PARAM_INT(ptr, 7); + + AActor *ref = COPY_AAPTR(self, ptr); if(Range==0) Range=MISSILERANGE; @@ -1072,9 +1078,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) int bslope = 0; int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - if (self->target || (Flags & CBAF_AIMFACING)) + if (ref || (Flags & CBAF_AIMFACING)) { - if (!(Flags & CBAF_AIMFACING)) A_FaceTarget (self); + if (!(Flags & CBAF_AIMFACING)) + { + if (ref == self->target) A_FaceTarget(self); + else if (ref == self->master) A_FaceMaster(self); + else if (ref == self->tracer) A_FaceTracer(self); + } bangle = self->angle; if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index cd718b1c0..17b762d68 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -204,8 +204,8 @@ ACTOR Actor native //: Thinker action native A_StopSoundEx(coerce name slot); action native A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10); action native A_Jump(int chance = 256, state label, ...); - action native A_CustomMissile(class missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0); - action native A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0); + action native A_CustomMissile(class missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET); + action native A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET); action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, bool aim = false, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0); action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); action native A_JumpIfCloser(float distance, state label); From aebf0e75262e1f658f582453c2c8be08300a4740 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sun, 21 Dec 2014 19:21:51 +0000 Subject: [PATCH 028/117] - Fixed: Adding multiple bots at the same time could cause the game to crash if there were too few bots defined. - The 'loaded_bots' variable no longer needs to be stored. --- src/b_bot.h | 5 ++-- src/b_game.cpp | 62 +++++++++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/b_bot.h b/src/b_bot.h index 69a2f7774..25689a9be 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -130,7 +130,6 @@ private: protected: bool ctf; - int loaded_bots; int t_join; bool observer; //Consoleplayer is observer. }; @@ -188,12 +187,12 @@ public: fixed_t oldy; private: - //(B_think.cpp) + //(b_think.cpp) void Think (); void ThinkForMove (ticcmd_t *cmd); void Set_enemy (); - //(B_func.cpp) + //(b_func.cpp) bool Reachable (AActor *target); void Dofire (ticcmd_t *cmd); AActor *Choose_Mate (); diff --git a/src/b_game.cpp b/src/b_game.cpp index 44aff03fa..9c222f464 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -216,19 +216,16 @@ bool FCajunMaster::SpawnBot (const char *name, int color) "\\color\\cf df 90" //10 = Bleached Bone }; - botinfo_t *thebot; - int botshift; + botinfo_t *thebot = botinfo; + int botshift = 0; if (name) { - thebot = botinfo; - // Check if exist or already in the game. - botshift = 0; while (thebot && stricmp (name, thebot->name)) { - thebot = thebot->next; botshift++; + thebot = thebot->next; } if (thebot == NULL) @@ -246,27 +243,36 @@ bool FCajunMaster::SpawnBot (const char *name, int color) return false; } } - else if (botnum < loaded_bots) - { - bool vacant = false; //Spawn a random bot from bots.cfg if no name given. - while (!vacant) - { - int rnum = (pr_botspawn() % loaded_bots); - thebot = botinfo; - botshift = 0; - while (rnum) - { - --rnum, thebot = thebot->next; - botshift++; - } - if (thebot->inuse == BOTINUSE_No) - vacant = true; - } - } else { - Printf ("Couldn't spawn bot; no bot left in %s\n", BOTFILENAME); - return false; + //Spawn a random bot from bots.cfg if no name given. + TArray BotInfoAvailable; + + while (thebot) + { + if (thebot->inuse == BOTINUSE_No) + BotInfoAvailable.Push (thebot); + + thebot = thebot->next; + } + + if (BotInfoAvailable.Size () == 0) + { + Printf ("Couldn't spawn bot; no bot left in %s\n", BOTFILENAME); + return false; + } + + thebot = BotInfoAvailable[pr_botspawn() % BotInfoAvailable.Size ()]; + + botinfo_t *thebot2 = botinfo; + while (thebot2) + { + if (thebot == thebot2) + break; + + botshift++; + thebot2 = thebot2->next; + } } thebot->inuse = BOTINUSE_Waiting; @@ -478,7 +484,6 @@ void FCajunMaster::ForgetBots () } botinfo = NULL; - loaded_bots = 0; } bool FCajunMaster::LoadBots () @@ -486,6 +491,7 @@ bool FCajunMaster::LoadBots () FScanner sc; FString tmp; bool gotteam = false; + int loaded_bots = 0; bglobal.ForgetBots (); tmp = M_GetCajunPath(BOTFILENAME); @@ -602,9 +608,9 @@ bool FCajunMaster::LoadBots () newinfo->next = bglobal.botinfo; newinfo->lastteam = TEAM_NONE; bglobal.botinfo = newinfo; - bglobal.loaded_bots++; + loaded_bots++; } - Printf ("%d bots read from %s\n", bglobal.loaded_bots, BOTFILENAME); + Printf ("%d bots read from %s\n", loaded_bots, BOTFILENAME); return true; } From f7405a1d245bf7938704dc73cb17b58accd0fbbe Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Mon, 22 Dec 2014 20:48:27 +0000 Subject: [PATCH 029/117] - Changed two loops from using doomcom.numnodes back to MAXPLAYERS as nodeingame[i] is not necessarily the same as playeringame[playerfornode[i]]. --- src/d_net.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 02ca8c0c4..0e3ae262e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1064,16 +1064,16 @@ void NetUpdate (void) if (consoleplayer == Net_Arbitrator) { - for (j = 0; j < doomcom.numnodes; j++) - { - if (nodeingame[j] && NetMode == NET_PacketServer) - { - count++; - } - } - if (NetMode == NET_PacketServer) { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j] && players[j].Bot == NULL) + { + count++; + } + } + // The loop above added the local player to the count a second time, // and it also added the player being sent the packet to the count. count -= 2; @@ -1203,12 +1203,15 @@ void NetUpdate (void) netbuffer[0] |= NCMD_MULTI; netbuffer[k++] = count; - for (l = 1, j = 0; j < doomcom.numnodes; j++) + if (NetMode == NET_PacketServer) { - if (nodeingame[j] && j != i && j != nodeforplayer[consoleplayer] && NetMode == NET_PacketServer) + for (l = 1, j = 0; j < MAXPLAYERS; j++) { - playerbytes[l++] = playerfornode[j]; - netbuffer[k++] = playerfornode[j]; + if (playeringame[j] && players[j].Bot == NULL && j != playerfornode[i] && j != consoleplayer) + { + playerbytes[l++] = j; + netbuffer[k++] = j; + } } } } From b24f173d03653d449301b6476a77c4ac563677e2 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 23 Dec 2014 22:20:39 +1300 Subject: [PATCH 030/117] Allow players to be popped in packet-server --- src/d_net.cpp | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 02ca8c0c4..4f56a32a9 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -669,25 +669,38 @@ void PlayerIsGone (int netnode, int netconsole) { int i; - if (!nodeingame[netnode]) - return; + if (nodeingame[netnode]) + { + for (i = netnode + 1; i < doomcom.numnodes; ++i) + { + if (nodeingame[i]) + break; + } + if (i == doomcom.numnodes) + { + doomcom.numnodes = netnode; + } - for (i = netnode + 1; i < doomcom.numnodes; ++i) - { - if (nodeingame[i]) - break; + if (playeringame[netconsole]) + { + players[netconsole].playerstate = PST_GONE; + } + nodeingame[netnode] = false; + nodejustleft[netnode] = false; } - if (i == doomcom.numnodes) + else if (nodejustleft[netnode]) // Packet Server { - doomcom.numnodes = netnode; + if (netnode + 1 == doomcom.numnodes) + { + doomcom.numnodes = netnode; + } + if (playeringame[netconsole]) + { + players[netconsole].playerstate = PST_GONE; + } + nodejustleft[netnode] = false; } - - if (playeringame[netconsole]) - { - players[netconsole].playerstate = PST_GONE; - } - nodeingame[netnode] = false; - nodejustleft[netnode] = false; + else return; if (netconsole == Net_Arbitrator) { @@ -790,7 +803,6 @@ void GetPackets (void) else { nodeingame[netnode] = false; - playeringame[netconsole] = false; nodejustleft[netnode] = true; } continue; From dfa6a44402bcd8a4ae47c9e1fa68c3ed68b6a3ef Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 23 Dec 2014 22:38:12 +1300 Subject: [PATCH 031/117] Ignore 0 length chat messages - There is no reason to send empty messages, and they just produced strange output anyway --- src/ct_chat.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index c0b73d3c0..10ff2bffb 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -343,6 +343,9 @@ static void CT_ClearChatMessage () static void ShoveChatStr (const char *str, BYTE who) { + if (strlen(str) < 1) // Don't send empty messages + return; + FString substBuff; if (str[0] == '/' && From d7d022144e10bd0ff447b5d1f7533646c69be9b4 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 23 Dec 2014 21:30:24 -0600 Subject: [PATCH 032/117] Call A_Face() directly from A_CustomBullletAttack --- src/p_enemy.h | 5 ++--- src/thingdef/thingdef_codeptr.cpp | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/p_enemy.h b/src/p_enemy.h index 8c5516ead..f5cc387dd 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -72,9 +72,8 @@ DECLARE_ACTION(A_FreezeDeathChunks) DECLARE_ACTION(A_BossDeath) void A_Chase(AActor *self); -void A_FaceTarget (AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); -void A_FaceMaster(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); -void A_FaceTracer(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); +void A_FaceTarget(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); +void A_Face(AActor *self, AActor *other, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270); bool A_RaiseMobj (AActor *, fixed_t speed); bool A_SinkMobj (AActor *, fixed_t speed); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 0f37efa18..9fdc14a28 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1082,9 +1082,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) { if (!(Flags & CBAF_AIMFACING)) { - if (ref == self->target) A_FaceTarget(self); - else if (ref == self->master) A_FaceMaster(self); - else if (ref == self->tracer) A_FaceTracer(self); + A_Face(self, ref); } bangle = self->angle; From 5caadeba4c349c6c7de82d667eb161007869f558 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 23 Dec 2014 21:33:47 -0600 Subject: [PATCH 033/117] Use a null check instead of strlen in ShoveChatStr --- src/ct_chat.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 10ff2bffb..78efab9d0 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -343,7 +343,8 @@ static void CT_ClearChatMessage () static void ShoveChatStr (const char *str, BYTE who) { - if (strlen(str) < 1) // Don't send empty messages + // Don't send empty messages + if (str == NULL || str[0] == '\0') return; FString substBuff; From 3fb9e754f1e1f0fbcd267712aa44386987487e54 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 23 Dec 2014 21:46:27 -0600 Subject: [PATCH 034/117] Rename 'Pick' to 'RandomPick' ... because 'pick' is way too generic a name to spend a keyword on. --- src/sc_man_scanner.re | 2 +- src/sc_man_tokens.h | 2 +- src/thingdef/thingdef_exp.cpp | 4 ++-- src/thingdef/thingdef_exp.h | 6 +++--- src/thingdef/thingdef_expression.cpp | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 3a7f717a8..52a5d9f61 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -158,7 +158,7 @@ std2: 'random' { RET(TK_Random); } 'random2' { RET(TK_Random2); } 'frandom' { RET(TK_FRandom); } - 'pick' { RET(TK_Pick); } + 'randompick' { RET(TK_RandomPick); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 1c22046c9..22f6e9cd4 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -122,5 +122,5 @@ xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") xx(TK_AlignOf, "'alignof'") -xx(TK_Pick, "'pick'") +xx(TK_RandomPick, "'randompick'") #undef xx diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 2ad741294..7bd10b38d 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -371,7 +371,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) return new FxRandom(rng, min, max, sc); } - else if (sc.CheckToken(TK_Pick)) + else if (sc.CheckToken(TK_RandomPick)) { FRandom *rng; TArray list; @@ -398,7 +398,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) break; sc.MustGetToken(','); } - return new FxPick(rng, list, sc); + return new FxRandomPick(rng, list, sc); } else if (sc.CheckToken(TK_FRandom)) { diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 0e03c661f..807ffcd87 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -559,7 +559,7 @@ public: // //========================================================================== -class FxPick : public FxExpression +class FxRandomPick : public FxExpression { protected: FRandom * rng; @@ -567,8 +567,8 @@ protected: public: - FxPick(FRandom *, TArray mi, const FScriptPosition &pos); - ~FxPick(); + FxRandomPick(FRandom *, TArray mi, const FScriptPosition &pos); + ~FxRandomPick(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression(AActor *self); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 913079bd2..d1c2eba9e 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1696,7 +1696,7 @@ ExpVal FxRandom::EvalExpression (AActor *self) // // //========================================================================== -FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos) +FxRandomPick::FxRandomPick(FRandom * r, TArray mi, const FScriptPosition &pos) : FxExpression(pos) { for (unsigned int index = 0; index < mi.Size(); index++) @@ -1713,7 +1713,7 @@ FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos // //========================================================================== -FxPick::~FxPick() +FxRandomPick::~FxRandomPick() { } @@ -1723,7 +1723,7 @@ FxPick::~FxPick() // //========================================================================== -FxExpression *FxPick::Resolve(FCompileContext &ctx) +FxExpression *FxRandomPick::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); for (unsigned int index = 0; index < min.Size(); index++) @@ -1741,7 +1741,7 @@ FxExpression *FxPick::Resolve(FCompileContext &ctx) // //========================================================================== -ExpVal FxPick::EvalExpression(AActor *self) +ExpVal FxRandomPick::EvalExpression(AActor *self) { ExpVal val; val.Type = VAL_Int; From 62a4945ca4e1264c74670ad00d9f67d76a880953 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 23 Dec 2014 23:30:00 -0600 Subject: [PATCH 035/117] - Fixed: CAUSEPAIN didn't work with A_Explode calls featuring no damage. --- src/p_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 496abbf0d..0b86aa41e 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4765,7 +4765,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo points *= thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT) / (double)FRACUNIT; // points and bombdamage should be the same sign - if ((points * bombdamage) > 0 && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + if (((bombspot->flags7 & MF7_CAUSEPAIN) || (points * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { // OK to damage; target is in direct path double velz; double thrust; From 7d628a8c034ce073ef4797ae59f2a2b9327942ba Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Wed, 24 Dec 2014 19:41:49 +0000 Subject: [PATCH 036/117] - Fixed: FCajunMaster::End() was missing a bot check. --- src/b_game.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/b_game.cpp b/src/b_game.cpp index 9c222f464..e136f3f29 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -178,7 +178,10 @@ void FCajunMaster::End () { for (i = 0; i < MAXPLAYERS; i++) { - getspawned.Push(players[i].userinfo.GetName()); + if (players[i].Bot != NULL) + { + getspawned.Push(players[i].userinfo.GetName()); + } } wanted_botnum = botnum; From d94be8f57bc0d59d0cfb2666438aefa792252e6e Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 24 Dec 2014 17:15:21 -0600 Subject: [PATCH 037/117] - Reverted back and set filter to "None" instead. --- wadsrc/static/actors/actor.txt | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 7faf389e7..fcc8d5ded 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -298,23 +298,23 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); - action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter, name species); - action native A_KillTarget(name damagetype = "none", int flags = 0, class filter, name species); - action native A_KillMaster(name damagetype = "none", int flags = 0, class filter, name species); - action native A_KillTracer(name damagetype = "none", int flags = 0, class filter, name species); - action native A_KillChildren(name damagetype = "none", int flags = 0, class filter, name species); - action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter, name species); - action native A_RemoveTarget(int flags = 0, class filter, name species); - action native A_RemoveMaster(int flags = 0, class filter, name species); - action native A_RemoveTracer(int flags = 0, class filter, name species); - action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter, name species); - action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter, name species); - action native A_Remove(int removee, int flags = 0, class filter, name species); + action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_KillTarget(name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_KillMaster(name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_KillTracer(name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_KillChildren(name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter = "None", name species); + action native A_RemoveTarget(int flags = 0, class filter = "None", name species); + action native A_RemoveMaster(int flags = 0, class filter = "None", name species); + action native A_RemoveTracer(int flags = 0, class filter = "None", name species); + action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter = "None", name species); + action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter = "None", name species); + action native A_Remove(int removee, int flags = 0, class filter = "None", name species); action native A_GiveToChildren(class itemtype, int amount = 0); action native A_GiveToSiblings(class itemtype, int amount = 0); action native A_TakeFromChildren(class itemtype, int amount = 0); From f2551dceda96d4fc3f73b3c353fbbb9baa54264a Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 24 Dec 2014 17:49:58 -0600 Subject: [PATCH 038/117] - Corrected the species checking. --- src/thingdef/thingdef_codeptr.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c5e0b860a..50969ce85 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5034,8 +5034,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) { - FName spec = mo->Species; - return (!(species) || !(stricmp(species, "")) || (species && ((exclude) ? (spec != species) : (spec == species)))); + return (!(species) || mo->Species == NAME_None || (species && ((exclude) ? (mo->Species != species) : (mo->Species == species)))); } static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) From 15cd0debc1a618d4cdfc69c1dea6bc759f04719d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Dec 2014 09:23:59 -0600 Subject: [PATCH 039/117] - Fixed: Species was not defined in actor.txt. --- wadsrc/static/actors/actor.txt | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index fcc8d5ded..73a5e0e4d 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -298,23 +298,23 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); - action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_KillTarget(name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_KillMaster(name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_KillTracer(name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_KillChildren(name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter = "None", name species); - action native A_RemoveTarget(int flags = 0, class filter = "None", name species); - action native A_RemoveMaster(int flags = 0, class filter = "None", name species); - action native A_RemoveTracer(int flags = 0, class filter = "None", name species); - action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter = "None", name species); - action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter = "None", name species); - action native A_Remove(int removee, int flags = 0, class filter = "None", name species); + action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_KillTarget(name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_KillMaster(name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_KillTracer(name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_KillChildren(name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_KillSiblings(name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); + action native A_RemoveTarget(int flags = 0, class filter = "None", name species = "None"); + action native A_RemoveMaster(int flags = 0, class filter = "None", name species = "None"); + action native A_RemoveTracer(int flags = 0, class filter = "None", name species = "None"); + action native A_RemoveChildren(bool removeall = false, int flags = 0, class filter = "None", name species = "None"); + action native A_RemoveSiblings(bool removeall = false, int flags = 0, class filter = "None", name species = "None"); + action native A_Remove(int removee, int flags = 0, class filter = "None", name species = "None"); action native A_GiveToChildren(class itemtype, int amount = 0); action native A_GiveToSiblings(class itemtype, int amount = 0); action native A_TakeFromChildren(class itemtype, int amount = 0); From 6fd70ff32018bdd8c1e1182954390a312f7bf705 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 25 Dec 2014 18:46:50 +0100 Subject: [PATCH 040/117] - fixed: Trying to rotate a polyobject into its actual position during loading of a savegame still inflicted damage on all touching actors - including incomplete player pawns - and also got blocked by them. Similar code already existed for the MovePolyobj function but apparently was overlooked here. --- src/p_saveg.cpp | 2 +- src/po_man.cpp | 30 +++++++++++++++++------------- src/po_man.h | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index a677420f5..67297d800 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -566,7 +566,7 @@ void P_SerializePolyobjs (FArchive &arc) I_Error ("UnarchivePolyobjs: Invalid polyobj tag"); } arc << angle; - po->RotatePolyobj (angle); + po->RotatePolyobj (angle, false); arc << deltaX << deltaY << po->interpolation; deltaX -= po->StartSpot.x; deltaY -= po->StartSpot.y; diff --git a/src/po_man.cpp b/src/po_man.cpp index e19acc53a..c6e273a0f 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -1051,7 +1051,7 @@ static void RotatePt (int an, fixed_t *x, fixed_t *y, fixed_t startSpotX, fixed_ // //========================================================================== -bool FPolyObj::RotatePolyobj (angle_t angle) +bool FPolyObj::RotatePolyobj (angle_t angle, bool fromsave) { int an; bool blocked; @@ -1073,23 +1073,27 @@ bool FPolyObj::RotatePolyobj (angle_t angle) validcount++; UpdateBBox(); - for(unsigned i=0;i < Sidedefs.Size(); i++) + // If we are loading a savegame we do not really want to damage actors and be blocked by them. This can also cause crashes when trying to damage incompletely deserialized player pawns. + if (!fromsave) { - if (CheckMobjBlocking(Sidedefs[i])) + for (unsigned i = 0; i < Sidedefs.Size(); i++) { - blocked = true; + if (CheckMobjBlocking(Sidedefs[i])) + { + blocked = true; + } } - } - if (blocked) - { - for(unsigned i=0;i < Vertices.Size(); i++) + if (blocked) { - Vertices[i]->x = PrevPts[i].x; - Vertices[i]->y = PrevPts[i].y; + for(unsigned i=0;i < Vertices.Size(); i++) + { + Vertices[i]->x = PrevPts[i].x; + Vertices[i]->y = PrevPts[i].y; + } + UpdateBBox(); + LinkPolyobj(); + return false; } - UpdateBBox(); - LinkPolyobj(); - return false; } this->angle += angle; LinkPolyobj(); diff --git a/src/po_man.h b/src/po_man.h index 70ab9d360..9e81cc266 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -74,7 +74,7 @@ struct FPolyObj int GetMirror(); bool MovePolyobj (int x, int y, bool force = false); - bool RotatePolyobj (angle_t angle); + bool RotatePolyobj (angle_t angle, bool fromsave = false); void ClosestPoint(fixed_t fx, fixed_t fy, fixed_t &ox, fixed_t &oy, side_t **side) const; void LinkPolyobj (); void RecalcActorFloorCeil(FBoundingBox bounds) const; From 1a39ac9243b340cdb38a0fb7a0b07a07c8d561d6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 25 Dec 2014 19:56:38 +0100 Subject: [PATCH 041/117] - forgot to save this before committing... --- src/p_saveg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 67297d800..119d92a9c 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -566,7 +566,7 @@ void P_SerializePolyobjs (FArchive &arc) I_Error ("UnarchivePolyobjs: Invalid polyobj tag"); } arc << angle; - po->RotatePolyobj (angle, false); + po->RotatePolyobj (angle, true); arc << deltaX << deltaY << po->interpolation; deltaX -= po->StartSpot.x; deltaY -= po->StartSpot.y; From a5a17e45cf5377c794dc0ad65581cc1d69ee8aca Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 25 Dec 2014 20:43:40 +0100 Subject: [PATCH 042/117] - fixed: Checking the terrain for any texture that was created after initializing the terrain data either returned random garbage or could even create an access violation. Added a range check to the array access function to prevent this. --- src/p_terrain.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_terrain.h b/src/p_terrain.h index d2933b687..9f00d07bc 100644 --- a/src/p_terrain.h +++ b/src/p_terrain.h @@ -49,11 +49,13 @@ public: WORD operator [](FTextureID tex) const { + if ((unsigned)tex.GetIndex() >= Types.Size()) return DefaultTerrainType; WORD type = Types[tex.GetIndex()]; return type == 0xffff? DefaultTerrainType : type; } WORD operator [](int texnum) const { + if ((unsigned)texnum >= Types.Size()) return DefaultTerrainType; WORD type = Types[texnum]; return type == 0xffff? DefaultTerrainType : type; } From 14d7b8b777bae47f81a80f14c2c2646d5c8eeb44 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 25 Dec 2014 21:08:31 +0100 Subject: [PATCH 043/117] - fixed: Since no DrawText calls actually use the non-functional DTA_DestWidth, DTA_DestHeight or DTA_Translation and GCC cannot handle the fudging of the varargs, these will now trigger an assertion. No need to try to make something work that's always a programming error. --- src/v_text.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/v_text.cpp b/src/v_text.cpp index 64b95c041..cad9bb5d2 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -128,7 +128,6 @@ void DCanvas::DrawTextV(FFont *font, int normalcolor, int x, int y, const char * { va_list *more_p; DWORD data; - void *ptrval; switch (tag) { @@ -150,15 +149,9 @@ void DCanvas::DrawTextV(FFont *font, int normalcolor, int x, int y, const char * // We don't handle these. :( case DTA_DestWidth: case DTA_DestHeight: - *(DWORD *)tags = TAG_IGNORE; - data = va_arg (tags, DWORD); - break; - - // Translation is specified explicitly by the text. case DTA_Translation: - *(DWORD *)tags = TAG_IGNORE; - ptrval = va_arg (tags, void*); - break; + assert("Bad parameter for DrawText" && false); + return; case DTA_CleanNoMove_1: boolval = va_arg (tags, INTBOOL); From 4f383e5aa711fd6c3076b971150a8f508821d09a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 26 Dec 2014 13:43:49 +0100 Subject: [PATCH 044/117] - Nash's force fake contrast submission. --- src/g_level.cpp | 2 ++ src/g_level.h | 6 ++++++ src/g_mapinfo.cpp | 18 ++++++++++++++++++ src/p_sectors.cpp | 2 +- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 761ea4f48..713847b05 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1287,6 +1287,7 @@ void G_InitLevelLocals () level.teamdamage = teamdamage; level.flags = 0; level.flags2 = 0; + level.flags3 = 0; info = FindLevelInfo (level.MapName); @@ -1340,6 +1341,7 @@ void G_InitLevelLocals () level.clusterflags = clus ? clus->flags : 0; level.flags |= info->flags; level.flags2 |= info->flags2; + level.flags3 |= info->flags3; level.levelnum = info->levelnum; level.Music = info->Music; level.musicorder = info->musicorder; diff --git a/src/g_level.h b/src/g_level.h index 8e78361d2..6e07b0c74 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -216,6 +216,9 @@ enum ELevelFlags LEVEL2_ENDGAME = 0x20000000, // This is an epilogue level that cannot be quit. LEVEL2_NOAUTOSAVEHINT = 0x40000000, // tell the game that an autosave for this level does not need to be kept LEVEL2_FORGETSTATE = 0x80000000, // forget this map's state in a hub + + // More flags! + LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled }; @@ -285,6 +288,8 @@ struct level_info_t int sucktime; DWORD flags; DWORD flags2; + DWORD flags3; + FString Music; FString LevelName; SBYTE WallVertLight, WallHorizLight; @@ -398,6 +403,7 @@ struct FLevelLocals DWORD flags; DWORD flags2; + DWORD flags3; DWORD fadeto; // The color the palette fades to (usually black) DWORD outsidefog; // The fog for sectors with sky ceilings diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index b68408237..c44f81ab2 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1190,6 +1190,9 @@ enum EMIType MITYPE_SETFLAG2, MITYPE_CLRFLAG2, MITYPE_SCFLAGS2, + MITYPE_SETFLAG3, + MITYPE_CLRFLAG3, + MITYPE_SCFLAGS3, MITYPE_COMPATFLAG, }; @@ -1275,6 +1278,7 @@ MapFlagHandlers[] = { "rememberstate", MITYPE_CLRFLAG2, LEVEL2_FORGETSTATE, 0 }, { "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 }, { "spawnwithweaponraised", MITYPE_SETFLAG2, LEVEL2_PRERAISEWEAPON, 0 }, + { "forcefakecontrast", MITYPE_SETFLAG3, LEVEL3_FORCEFAKECONTRAST, 0 }, { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes { "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX, 0 }, { "compat_stairs", MITYPE_COMPATFLAG, COMPATF_STAIRINDEX, 0 }, @@ -1372,6 +1376,20 @@ void FMapInfoParser::ParseMapDefinition(level_info_t &info) info.flags2 = (info.flags2 & handler->data2) | handler->data1; break; + case MITYPE_SETFLAG3: + info.flags3 |= handler->data1; + info.flags3 |= handler->data2; + break; + + case MITYPE_CLRFLAG3: + info.flags3 &= ~handler->data1; + info.flags3 |= handler->data2; + break; + + case MITYPE_SCFLAGS3: + info.flags3 = (info.flags3 & handler->data2) | handler->data1; + break; + case MITYPE_COMPATFLAG: { int set = 1; diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 699350839..904c76c1b 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -965,7 +965,7 @@ int side_t::GetLightLevel (bool foggy, int baselight, bool noabsolute, int *pfak *pfakecontrast = 0; } - if (!foggy) // Don't do relative lighting in foggy sectors + if (!foggy || level.flags3 & LEVEL3_FORCEFAKECONTRAST) // Don't do relative lighting in foggy sectors { if (!(Flags & WALLF_NOFAKECONTRAST) && r_fakecontrast != 0) { From 0d1c954bc08207489e1e4c78e03717abe20d68d8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 26 Dec 2014 22:11:03 +0200 Subject: [PATCH 045/117] Fixed crash in game controller support code Joystick's startup and shutdown (of Native OS X backend) can be called more than once --- src/posix/cocoa/i_joystick.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index a3bc1c0fa..29f98fcd1 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -739,6 +739,11 @@ IOKitJoystickManager* s_joystickManager; void I_StartupJoysticks() { + if (NULL != s_joystickManager) + { + return; + } + // HID Manager API is available on 10.5 (Darwin 9.x) or newer if (darwinVersion.major >= 9) @@ -750,6 +755,7 @@ void I_StartupJoysticks() void I_ShutdownJoysticks() { delete s_joystickManager; + s_joystickManager = NULL; } void I_GetJoysticks( TArray< IJoystickConfig* >& sticks ) From ec98937c88cca3cfa5c2762f042775c0480aecf2 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 14:41:01 -0600 Subject: [PATCH 046/117] Read script number as signed when parsing SARY chunks - Fixed: Script arrays didn't work in named scripts because the loader read the script number as an unsigned word, hence it would never find named scripts, since they are stored with negative numbers. --- src/p_acs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e005c3c67..2929eb32a 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2393,7 +2393,7 @@ void FBehavior::LoadScriptsDirectory () int size = LittleLong(scripts.dw[1]); if (size >= 6) { - int script_num = LittleShort(scripts.w[4]); + int script_num = LittleShort(scripts.sw[4]); ScriptPtr *ptr = const_cast(FindScript(script_num)); if (ptr != NULL) { From e0667544d2d7068e941a31c3d8aa2768534b688b Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 15:15:42 -0600 Subject: [PATCH 047/117] Clamp chase_dist and chase_height because integer overflow is a thing. --- src/p_map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 0b86aa41e..456db2d0f 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4279,7 +4279,7 @@ CVAR(Float, chase_dist, 90.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &CameraZ, sector_t *&CameraSector) { - fixed_t distance = (fixed_t)(chase_dist * FRACUNIT); + fixed_t distance = (fixed_t)(clamp(chase_dist, 0, 30000) * FRACUNIT); angle_t angle = (t1->angle - ANG180) >> ANGLETOFINESHIFT; angle_t pitch = (angle_t)(t1->pitch) >> ANGLETOFINESHIFT; FTraceResults trace; @@ -4289,7 +4289,7 @@ void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &Camera vy = FixedMul(finecosine[pitch], finesine[angle]); vz = finesine[pitch]; - sz = t1->z - t1->floorclip + t1->height + (fixed_t)(chase_height * FRACUNIT); + sz = t1->z - t1->floorclip + t1->height + (fixed_t)(clamp(chase_height, -1000, 1000) * FRACUNIT); if (Trace(t1->x, t1->y, sz, t1->Sector, vx, vy, vz, distance, 0, 0, NULL, trace) && From f00c8e194323e6939d642e37ce831cb6a135ee27 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 16:34:38 -0600 Subject: [PATCH 048/117] Revert "Move C_ExecCmdLineParams() call slightly later in the startup process." This reverts commit 3c376aa342994971e0e3f578476e8085d346cdf1. - I was wrong. It breaks pullin and complete logging (at the very least). --- src/d_main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 2ceec3926..11a8c4ff0 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2273,6 +2273,8 @@ void D_DoomMain (void) execFiles = Args->GatherFiles ("-exec"); D_MultiExec (execFiles, true); + C_ExecCmdLineParams (); // [RH] do all +set commands on the command line + CopyFiles(allwads, pwads); // Since this function will never leave we must delete this array here manually. @@ -2288,8 +2290,6 @@ void D_DoomMain (void) // Now that wads are loaded, define mod-specific cvars. ParseCVarInfo(); - C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - // [RH] Initialize localizable strings. GStrings.LoadStrings (false); From 4017a6d864b6fa4a97c4c6e8013c99169ccc4d3b Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 17:21:57 -0600 Subject: [PATCH 049/117] Retry setting unknown cvars after CVARINFOs are processed. - Fixed: Using +set cvarname and +cvarname on the command line would not work if cvarname was defined in CVARINFO. This should be the proper way to fix it. Rather than move all command line execution after loading CVARINFO, keep command line execution before wads are loaded. If an attempt is made to set an unknown cvar or to run an unknown command (which could potentially be shorthand for setting an unknown cvar), save it and try running it again after all CVARINFOs have been handled. --- src/c_dispatch.cpp | 50 ++++++++++++++++++++++++++++++++++++++++------ src/c_dispatch.h | 1 + src/d_main.cpp | 3 +++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 65e017bdc..0719f5a90 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -185,6 +185,9 @@ static const char *KeyConfCommands[] = "clearplayerclasses" }; +static TArray StoredStartupSets; +static bool RunningStoredStartups; + // CODE -------------------------------------------------------------------- IMPLEMENT_CLASS (DWaitingCommand) @@ -537,6 +540,18 @@ void ResetButtonStates () } } +void C_ExecStoredSets() +{ + assert(!RunningStoredStartups); + RunningStoredStartups = true; + for (unsigned i = 0; i < StoredStartupSets.Size(); ++i) + { + C_DoCommand(StoredStartupSets[i]); + } + StoredStartupSets.Clear(); + RunningStoredStartups = false; +} + void C_DoCommand (const char *cmd, int keynum) { FConsoleCommand *com; @@ -612,7 +627,22 @@ void C_DoCommand (const char *cmd, int keynum) if ( (com = FindNameInHashTable (Commands, beg, len)) ) { - if (gamestate != GS_STARTUP || ParsingKeyConf || + if (gamestate == GS_STARTUP && !RunningStoredStartups && + len == 3 && strnicmp(beg, "set", 3) == 0) + { + // Save setting of unknown cvars for later, in case a loaded wad has a + // CVARINFO that defines it. + FCommandLine args(beg); + if (args.argc() > 1 && FindCVar(args[1], NULL) == NULL) + { + StoredStartupSets.Push(beg); + } + else + { + com->Run(args, players[consoleplayer].mo, keynum); + } + } + else if (gamestate != GS_STARTUP || ParsingKeyConf || (len == 3 && strnicmp (beg, "set", 3) == 0) || (len == 7 && strnicmp (beg, "logfile", 7) == 0) || (len == 9 && strnicmp (beg, "unbindall", 9) == 0) || @@ -657,12 +687,20 @@ void C_DoCommand (const char *cmd, int keynum) } else { // We don't know how to handle this command - char cmdname[64]; - size_t minlen = MIN (len, 63); + if (gamestate == GS_STARTUP && !RunningStoredStartups) + { + // Save it for later, in case a CVARINFO defines it. + StoredStartupSets.Push(beg); + } + else + { + char cmdname[64]; + size_t minlen = MIN (len, 63); - memcpy (cmdname, beg, minlen); - cmdname[len] = 0; - Printf ("Unknown command \"%s\"\n", cmdname); + memcpy (cmdname, beg, minlen); + cmdname[len] = 0; + Printf ("Unknown command \"%s\"\n", cmdname); + } } } } diff --git a/src/c_dispatch.h b/src/c_dispatch.h index f4518608d..b494005c7 100644 --- a/src/c_dispatch.h +++ b/src/c_dispatch.h @@ -42,6 +42,7 @@ class APlayerPawn; extern bool CheckCheatmode (bool printmsg = true); void C_ExecCmdLineParams (); +void C_ExecStoredSets(); // Add commands to the console as if they were typed in. Can handle wait // and semicolon-separated commands. This function may modify the source diff --git a/src/d_main.cpp b/src/d_main.cpp index 11a8c4ff0..817d3b9ec 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2290,6 +2290,9 @@ void D_DoomMain (void) // Now that wads are loaded, define mod-specific cvars. ParseCVarInfo(); + // Try setting previously unknown cvars again, as a CVARINFO may have made them known. + C_ExecStoredSets(); + // [RH] Initialize localizable strings. GStrings.LoadStrings (false); From 798267d223f0c5b0d39e5b9674557e986d75169d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 17:29:13 -0600 Subject: [PATCH 050/117] Remove memcpy from "Unknown command" error printing --- src/c_dispatch.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 0719f5a90..7a6748fdc 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -694,12 +694,7 @@ void C_DoCommand (const char *cmd, int keynum) } else { - char cmdname[64]; - size_t minlen = MIN (len, 63); - - memcpy (cmdname, beg, minlen); - cmdname[len] = 0; - Printf ("Unknown command \"%s\"\n", cmdname); + Printf ("Unknown command \"%.*s\"\n", len, beg); } } } From bce7d12379ce1c892c0478d03978fb1083089c2c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 18:40:15 -0600 Subject: [PATCH 051/117] Change clipping in DSBarInfo::DrawGraphic() - Fixed: When DSBarInfo::DrawGraphic() is used scaled, without fullscreen offset, if one of the top and left clip locations was 0 and the other was non-0, the 0 one would be clipped to the edge of a 4:3 box centered on the screen instead of the edge of the screen. --- src/g_shared/sbarinfo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index a738a5aad..7a11a2865 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1215,7 +1215,11 @@ public: if(Scaled) { if(cx != 0 || cy != 0) + { screen->VirtualToRealCoords(dcx, dcy, tmp, tmp, script->resW, script->resH, true); + if (cx == 0) dcx = 0; + if (cy == 0) dcy = 0; + } if(cr != 0 || cb != 0 || clearDontDraw) screen->VirtualToRealCoords(dcr, dcb, tmp, tmp, script->resW, script->resH, true); screen->VirtualToRealCoords(dx, dy, w, h, script->resW, script->resH, true); From f76d137d33b32208cc7809221c0879889f2ab596 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 18:58:59 -0600 Subject: [PATCH 052/117] Be less ugly when hiding warnings for using the %B formatter - Take advantage of the new _Pragma operator to hide the printf warning suppression inside of macros instead of needing to litter the code around Printfs with a bunch of junk. --- src/p_3dfloors.cpp | 2 ++ src/p_acs.cpp | 17 ++--------------- src/zstring.h | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index d13e46204..fc35409dc 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -968,10 +968,12 @@ CCMD (dump3df) fixed_t height=ffloors[i]->top.plane->ZatPoint(CenterSpot(sector)); fixed_t bheight=ffloors[i]->bottom.plane->ZatPoint(CenterSpot(sector)); + IGNORE_FORMAT_PRE Printf("FFloor %d @ top = %f (model = %d), bottom = %f (model = %d), flags = %B, alpha = %d %s %s\n", i, height / 65536., ffloors[i]->top.model->sectornum, bheight / 65536., ffloors[i]->bottom.model->sectornum, ffloors[i]->flags, ffloors[i]->alpha, (ffloors[i]->flags&FF_EXISTS)? "Exists":"", (ffloors[i]->flags&FF_DYNAMIC)? "Dynamic":""); + IGNORE_FORMAT_POST } } } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2929eb32a..2ea64f26e 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -7496,22 +7496,9 @@ scriptwait: break; case PCD_PRINTBINARY: -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)))) || defined(__clang__) -#define HAS_DIAGNOSTIC_PRAGMA -#endif -#ifdef HAS_DIAGNOSTIC_PRAGMA -#pragma GCC diagnostic push -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wformat-invalid-specifier" -#else -#pragma GCC diagnostic ignored "-Wformat=" -#endif -#pragma GCC diagnostic ignored "-Wformat-extra-args" -#endif + IGNORE_FORMAT_PRE work.AppendFormat ("%B", STACK(1)); -#ifdef HAS_DIAGNOSTIC_PRAGMA -#pragma GCC diagnostic pop -#endif + IGNORE_FORMAT_POST --sp; break; diff --git a/src/zstring.h b/src/zstring.h index 1736f3421..fe7ee72cf 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -47,6 +47,24 @@ #define PRINTFISH(x) #endif +#ifdef __clang__ +#define IGNORE_FORMAT_PRE \ + _Pragma("GCC diagnostic push" \ + _Pragma("GCC diagnostic ignored \"-Wformat-invalid-specifier\"") \ + _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\"") +#define IGNORE_FORMAT_POST _Pragma("GCC diagnostic pop") +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))) +#define IGNORE_FORMAT_PRE \ + _Pragma("GCC diagnostic push" \ + _Pragma("GCC diagnostic ignored \"-Wformat=\"") \ + _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\"") +#define IGNORE_FORMAT_POST _Pragma("GCC diagnostic pop") +#else +#define IGNORE_FORMAT_PRE +#define IGNORE_FORMAT_POST +#endif + + struct FStringData { unsigned int Len; // Length of string, excluding terminating null From 84afd2252f02f444cd1a22aeee70641a5268b306 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 26 Dec 2014 20:10:41 -0500 Subject: [PATCH 053/117] - Fixed missing right paren on the first _Pragma from last commit. --- src/zstring.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zstring.h b/src/zstring.h index fe7ee72cf..0104020fa 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -49,13 +49,13 @@ #ifdef __clang__ #define IGNORE_FORMAT_PRE \ - _Pragma("GCC diagnostic push" \ + _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wformat-invalid-specifier\"") \ _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\"") #define IGNORE_FORMAT_POST _Pragma("GCC diagnostic pop") #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))) #define IGNORE_FORMAT_PRE \ - _Pragma("GCC diagnostic push" \ + _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wformat=\"") \ _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\"") #define IGNORE_FORMAT_POST _Pragma("GCC diagnostic pop") From 1aa00f1b0eb574f8174e4821c3aa9f0a5ec08746 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 26 Dec 2014 19:22:09 -0600 Subject: [PATCH 054/117] Draw player setup cursor equivalently to options menu cursors - Explicitly size and position the text cursor in FListMenuItem::DrawSelector() the same way that the options menu does it using M_DrawConText(): By sizing it to a scaled 8x8 cell. --- src/menu/listmenu.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 53b4fee22..402c1d5e9 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -300,7 +300,12 @@ void FListMenuItem::DrawSelector(int xofs, int yofs, FTextureID tex) if ((DMenu::MenuTime%8) < 6) { screen->DrawText(ConFont, OptionSettings.mFontColorSelection, - mXpos + xofs, mYpos + yofs, "\xd", DTA_Clean, true, TAG_DONE); + (mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2, + (mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2, + "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); } } else From 8b6966dd36c33305de39bc38ad209979c51052ba Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 27 Dec 2014 15:42:56 +0200 Subject: [PATCH 055/117] Got rid of SDL emulation's last remnants in native OS X backend --- src/posix/cocoa/i_backend_cocoa.mm | 330 +++++++++-------------------- 1 file changed, 95 insertions(+), 235 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index b1d737912..a5a8d3520 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1176,6 +1176,8 @@ namespace - (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI; - (void)useHiDPI:(bool)hiDPI; +- (bool)fullscreen; + - (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height; - (void*)softwareRenderingBuffer; @@ -1570,6 +1572,12 @@ static bool s_fullscreenNewAPI; } +- (bool)fullscreen +{ + return m_fullscreen; +} + + - (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height { if (0 == m_softwareRenderingTexture) @@ -1803,132 +1811,6 @@ bool I_SetCursor(FTexture* cursorpic) // --------------------------------------------------------------------------- -extern "C" -{ - -typedef enum -{ - SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ - SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ - SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), -} SDL_WindowFlags; - -struct SDL_Window -{ - uint32_t flags; - int w, h; - int pitch; - void *pixels; -}; - -SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int width, int height, uint32_t flags) -{ - [appCtrl changeVideoResolution:(SDL_WINDOW_FULLSCREEN_DESKTOP & flags) - width:width - height:height - useHiDPI:vid_hidpi]; - - static SDL_Window result; - - if (!(SDL_WINDOW_OPENGL & flags)) - { - [appCtrl setupSoftwareRenderingWithWidth:width - height:height]; - } - - result.flags = flags; - result.w = width; - result.h = height; - result.pitch = width * BYTES_PER_PIXEL; - result.pixels = [appCtrl softwareRenderingBuffer]; - - return &result; -} -void SDL_DestroyWindow(SDL_Window *window) -{ - ZD_UNUSED(window); -} - -uint32_t SDL_GetWindowFlags(SDL_Window *window) -{ - return window->flags; -} - -int SDL_SetWindowFullscreen(SDL_Window* window, uint32_t flags) -{ - if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == (flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) - return 0; - - if (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) - { - window->flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP; - } - else - { - window->flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - - [appCtrl changeVideoResolution:(SDL_WINDOW_FULLSCREEN_DESKTOP & flags) - width:window->w - height:window->h - useHiDPI:vid_hidpi]; - - return 0; -} - -int SDL_UpdateWindowSurface(SDL_Window *screen) -{ - assert(NULL != screen); - - if (rbOpts.dirty) - { - glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); - - // TODO: Figure out why the following glClear() call is needed - // to avoid drawing of garbage in fullscreen mode when - // in-game's aspect ratio is different from display one - glClear(GL_COLOR_BUFFER_BIT); - - rbOpts.dirty = false; - } - - const int width = screen->w; - const int height = screen->h; - -#ifdef __LITTLE_ENDIAN__ - static const GLenum format = GL_RGBA; -#else // __BIG_ENDIAN__ - static const GLenum format = GL_ABGR_EXT; -#endif // __LITTLE_ENDIAN__ - - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - width, height, 0, format, GL_UNSIGNED_BYTE, screen->pixels); - - glBegin(GL_QUADS); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glTexCoord2f(0.0f, 0.0f); - glVertex2f(0.0f, 0.0f); - glTexCoord2f(width, 0.0f); - glVertex2f(width, 0.0f); - glTexCoord2f(width, height); - glVertex2f(width, height); - glTexCoord2f(0.0f, height); - glVertex2f(0.0f, height); - glEnd(); - - glFlush(); - - [[NSOpenGLContext currentContext] flushBuffer]; - - return 0; -} - -} // extern "C" - - -// --------------------------------------------------------------------------- - - class CocoaVideo : public IVideo { public: @@ -1953,8 +1835,6 @@ public: CocoaFrameBuffer(int width, int height, bool fullscreen); ~CocoaFrameBuffer(); - virtual bool IsValid(); - virtual bool Lock(bool buffer); virtual void Unlock(); virtual void Update(); @@ -1973,8 +1853,6 @@ public: virtual void SetVSync(bool vsync); - void SetFullscreen(bool fullscreen); - private: PalEntry m_palette[256]; bool m_needPaletteUpdate; @@ -1988,7 +1866,7 @@ private: bool m_isUpdatePending; - SDL_Window *Screen; + void Flip(); void UpdateColors(); }; @@ -2132,87 +2010,42 @@ bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) return false; } -DFrameBuffer* CocoaVideo::CreateFrameBuffer(int width, int height, bool fullscreen, DFrameBuffer* old) +DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool fullscreen, DFrameBuffer* const old) { - static int retry = 0; - static int owidth, oheight; - - PalEntry flashColor; - int flashAmount; + PalEntry flashColor = 0; + int flashAmount = 0; if (old != NULL) - { // Reuse the old framebuffer if its attributes are the same - CocoaFrameBuffer *fb = static_cast (old); - if (fb->GetWidth() == width && - fb->GetHeight() == height) + { + if ( width == old->GetWidth() + && height == old->GetHeight()) { - if (fb->IsFullscreen() != fullscreen) - { - fb->SetFullscreen (fullscreen); - } + [appCtrl changeVideoResolution:fullscreen + width:width + height:height + useHiDPI:vid_hidpi]; return old; } - old->GetFlash (flashColor, flashAmount); + + old->GetFlash(flashColor, flashAmount); old->ObjectFlags |= OF_YesReallyDelete; - if (screen == old) screen = NULL; + + if (old == screen) + { + screen = NULL; + } + delete old; } - else - { - flashColor = 0; - flashAmount = 0; - } - CocoaFrameBuffer *fb = new CocoaFrameBuffer (width, height, fullscreen); - retry = 0; - - // If we could not create the framebuffer, try again with slightly - // different parameters in this order: - // 1. Try with the closest size - // 2. Try in the opposite screen mode with the original size - // 3. Try in the opposite screen mode with the closest size - // This is a somewhat confusing mass of recursion here. - - while (fb == NULL || !fb->IsValid ()) - { - if (fb != NULL) - { - delete fb; - } - - switch (retry) - { - case 0: - owidth = width; - oheight = height; - case 2: - // Try a different resolution. Hopefully that will work. - I_ClosestResolution (&width, &height, 8); - break; - - case 1: - // Try changing fullscreen mode. Maybe that will work. - width = owidth; - height = oheight; - fullscreen = !fullscreen; - break; - - default: - // I give up! - I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); - } - - ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); - } - - fb->SetFlash (flashColor, flashAmount); + CocoaFrameBuffer* fb = new CocoaFrameBuffer(width, height, fullscreen); + fb->SetFlash(flashColor, flashAmount); return fb; } -void CocoaVideo::SetWindowedScale (float scale) +void CocoaVideo::SetWindowedScale(float scale) { } @@ -2225,14 +2058,13 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) , m_flashAmount(0) , m_isUpdatePending(false) { - FString caption; - caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); + [appCtrl changeVideoResolution:fullscreen + width:width + height:height + useHiDPI:vid_hidpi]; - Screen = SDL_CreateWindow (caption, 0, 0, - width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); - - if (Screen == NULL) - return; + [appCtrl setupSoftwareRenderingWithWidth:width + height:height]; GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); @@ -2248,30 +2080,21 @@ CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) } -CocoaFrameBuffer::~CocoaFrameBuffer () +CocoaFrameBuffer::~CocoaFrameBuffer() { - if(Screen) - { - SDL_DestroyWindow (Screen); - } } -bool CocoaFrameBuffer::IsValid () -{ - return DFrameBuffer::IsValid() && Screen != NULL; -} - -int CocoaFrameBuffer::GetPageCount () +int CocoaFrameBuffer::GetPageCount() { return 1; } -bool CocoaFrameBuffer::Lock (bool buffered) +bool CocoaFrameBuffer::Lock(bool buffered) { - return DSimpleCanvas::Lock (); + return DSimpleCanvas::Lock(buffered); } -void CocoaFrameBuffer::Unlock () +void CocoaFrameBuffer::Unlock() { if (m_isUpdatePending && LockCount == 1) { @@ -2284,7 +2107,7 @@ void CocoaFrameBuffer::Unlock () } } -void CocoaFrameBuffer::Update () +void CocoaFrameBuffer::Update() { if (LockCount != 1) { @@ -2296,7 +2119,7 @@ void CocoaFrameBuffer::Update () return; } - DrawRateStuff (); + DrawRateStuff(); Buffer = NULL; LockCount = 0; @@ -2310,18 +2133,18 @@ void CocoaFrameBuffer::Update () Width, Height, FRACUNIT, FRACUNIT, 0, 0); FlipCycles.Clock(); - SDL_UpdateWindowSurface(Screen); + Flip(); FlipCycles.Unclock(); BlitCycles.Unclock(); if (m_needGammaUpdate) { - bool Windowed = false; - m_needGammaUpdate = false; - CalcGamma((Windowed || rgamma == 0.f) ? m_gamma : (m_gamma * rgamma), m_gammaTable[0]); - CalcGamma((Windowed || ggamma == 0.f) ? m_gamma : (m_gamma * ggamma), m_gammaTable[1]); - CalcGamma((Windowed || bgamma == 0.f) ? m_gamma : (m_gamma * bgamma), m_gammaTable[2]); + CalcGamma(rgamma == 0.0f ? m_gamma : m_gamma * rgamma, m_gammaTable[0]); + CalcGamma(ggamma == 0.0f ? m_gamma : m_gamma * ggamma, m_gammaTable[1]); + CalcGamma(bgamma == 0.0f ? m_gamma : m_gamma * bgamma, m_gammaTable[2]); + + m_needGammaUpdate = false; m_needPaletteUpdate = true; } @@ -2353,7 +2176,7 @@ void CocoaFrameBuffer::UpdateColors() GPfx.SetPalette(palette); } -PalEntry *CocoaFrameBuffer::GetPalette () +PalEntry *CocoaFrameBuffer::GetPalette() { return m_palette; } @@ -2396,17 +2219,12 @@ void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) } } -void CocoaFrameBuffer::SetFullscreen (bool fullscreen) +bool CocoaFrameBuffer::IsFullscreen() { - SDL_SetWindowFullscreen (Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + return [appCtrl fullscreen]; } -bool CocoaFrameBuffer::IsFullscreen () -{ - return (SDL_GetWindowFlags (Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; -} - -void CocoaFrameBuffer::SetVSync (bool vsync) +void CocoaFrameBuffer::SetVSync(bool vsync) { #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 const long value = vsync ? 1 : 0; @@ -2418,6 +2236,48 @@ void CocoaFrameBuffer::SetVSync (bool vsync) forParameter:NSOpenGLCPSwapInterval]; } +void CocoaFrameBuffer::Flip() +{ + assert(NULL != screen); + + if (rbOpts.dirty) + { + glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); + + // TODO: Figure out why the following glClear() call is needed + // to avoid drawing of garbage in fullscreen mode when + // in-game's aspect ratio is different from display one + glClear(GL_COLOR_BUFFER_BIT); + + rbOpts.dirty = false; + } + +#ifdef __LITTLE_ENDIAN__ + static const GLenum format = GL_RGBA; +#else // __BIG_ENDIAN__ + static const GLenum format = GL_ABGR_EXT; +#endif // __LITTLE_ENDIAN__ + + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + Width, Height, 0, format, GL_UNSIGNED_BYTE, [appCtrl softwareRenderingBuffer]); + + glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, 0.0f); + glTexCoord2f(Width, 0.0f); + glVertex2f(Width, 0.0f); + glTexCoord2f(Width, Height); + glVertex2f(Width, Height); + glTexCoord2f(0.0f, Height); + glVertex2f(0.0f, Height); + glEnd(); + + glFlush(); + + [[NSOpenGLContext currentContext] flushBuffer]; +} + ADD_STAT(blit) { From 16965eb78ae72e6113e7046d97afdd2f4267b4c6 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 27 Dec 2014 17:30:50 +0200 Subject: [PATCH 056/117] Removed vid_listmodes console command from native OS X backend There is a hard-coded set of resolutions independent from the hardware --- src/posix/cocoa/i_backend_cocoa.mm | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index a5a8d3520..fa1b1d83e 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -2428,34 +2428,7 @@ CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } -CCMD (vid_listmodes) -{ - static const char *ratios[5] = { "", " - 16:9", " - 16:10", "", " - 5:4" }; - int width, height, bits; - bool letterbox; - - if (Video == NULL) - { - return; - } - for (bits = 1; bits <= 32; bits++) - { - Video->StartModeIterator (bits, screen->IsFullscreen()); - while (Video->NextMode (&width, &height, &letterbox)) - { - bool thisMode = (width == DisplayWidth && height == DisplayHeight && bits == DisplayBits); - int ratio = CheckRatio (width, height); - Printf (thisMode ? PRINT_BOLD : PRINT_HIGH, - "%s%4d x%5d x%3d%s%s\n", - thisMode || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, - width, height, bits, - ratios[ratio], - thisMode || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); - } - } -} - -CCMD (vid_currentmode) +CCMD(vid_currentmode) { Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); } From 613c3293ce9f0f3546610033a97cfe76458f0854 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 27 Dec 2014 17:47:21 +0200 Subject: [PATCH 057/117] Further cleanup of native OS X backend video part --- src/posix/cocoa/i_backend_cocoa.mm | 86 ++++++++++++++---------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index fa1b1d83e..41b4fe76b 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1814,7 +1814,7 @@ bool I_SetCursor(FTexture* cursorpic) class CocoaVideo : public IVideo { public: - explicit CocoaVideo(int dummy); + CocoaVideo(); virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } virtual void SetWindowedScale(float scale); @@ -1978,10 +1978,9 @@ static cycle_t FlipCycles; // --------------------------------------------------------------------------- -CocoaVideo::CocoaVideo(int dummy) +CocoaVideo::CocoaVideo() : m_modeIterator(0) { - ZD_UNUSED(dummy); } void CocoaVideo::StartModeIterator(int bits, bool fs) @@ -2176,7 +2175,7 @@ void CocoaFrameBuffer::UpdateColors() GPfx.SetPalette(palette); } -PalEntry *CocoaFrameBuffer::GetPalette() +PalEntry* CocoaFrameBuffer::GetPalette() { return m_palette; } @@ -2290,41 +2289,40 @@ ADD_STAT(blit) IVideo *Video; -void I_ShutdownGraphics () +void I_ShutdownGraphics() { - if (screen) + if (NULL != screen) { - DFrameBuffer *s = screen; + screen->ObjectFlags |= OF_YesReallyDelete; + delete screen; screen = NULL; - s->ObjectFlags |= OF_YesReallyDelete; - delete s; } - if (Video) - delete Video, Video = NULL; + + delete Video; + Video = NULL; } -void I_InitGraphics () +void I_InitGraphics() { UCVarValue val; - val.Bool = !!Args->CheckParm ("-devparm"); - ticker.SetGenericRepDefault (val, CVAR_Bool); - - Video = new CocoaVideo (0); - if (Video == NULL) - I_FatalError ("Failed to initialize display"); + val.Bool = !!Args->CheckParm("-devparm"); + ticker.SetGenericRepDefault(val, CVAR_Bool); + Video = new CocoaVideo; atterm (I_ShutdownGraphics); } + static void I_DeleteRenderer() { - if (Renderer != NULL) delete Renderer; + delete Renderer; + Renderer = NULL; } void I_CreateRenderer() { - if (Renderer == NULL) + if (NULL == Renderer) { Renderer = new FSoftwareRenderer; atterm(I_DeleteRenderer); @@ -2332,58 +2330,53 @@ void I_CreateRenderer() } -DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) +DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) { - bool fs = false; - switch (Video->GetDisplayType ()) - { - case DISPLAY_WindowOnly: - fs = false; - break; - case DISPLAY_FullscreenOnly: - fs = true; - break; - case DISPLAY_Both: - fs = fullscreen; - break; - } - - return Video->CreateFrameBuffer (width, height, fs, old); + return Video->CreateFrameBuffer(width, height, fullscreen, old); } -bool I_CheckResolution (int width, int height, int bits) +bool I_CheckResolution(const int width, const int height, const int bits) { int twidth, theight; - Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); - while (Video->NextMode (&twidth, &theight, NULL)) + Video->StartModeIterator(bits, fullscreen); + + while (Video->NextMode(&twidth, &theight, NULL)) { if (width == twidth && height == theight) + { return true; + } } + return false; } -void I_ClosestResolution (int *width, int *height, int bits) +void I_ClosestResolution(int *width, int *height, int bits) { int twidth, theight; int cwidth = 0, cheight = 0; int iteration; - DWORD closest = 4294967295u; + DWORD closest = DWORD(-1); - for (iteration = 0; iteration < 2; iteration++) + for (iteration = 0; iteration < 2; ++iteration) { - Video->StartModeIterator (bits, screen ? screen->IsFullscreen() : fullscreen); + Video->StartModeIterator (bits, fullscreen); + while (Video->NextMode (&twidth, &theight, NULL)) { if (twidth == *width && theight == *height) + { return; + } if (iteration == 0 && (twidth < *width || theight < *height)) + { continue; + } - DWORD dist = (twidth - *width) * (twidth - *width) - + (theight - *height) * (theight - *height); + const DWORD dist = (twidth - *width) * (twidth - *width) + + (theight - *height) * (theight - *height); if (dist < closest) { @@ -2392,7 +2385,8 @@ void I_ClosestResolution (int *width, int *height, int bits) cheight = theight; } } - if (closest != 4294967295u) + + if (closest != DWORD(-1)) { *width = cwidth; *height = cheight; From d43b201de9980e8903755f8a1428d16c93abe663 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 27 Dec 2014 18:13:47 +0200 Subject: [PATCH 058/117] Yet another code beautification --- src/posix/cocoa/i_backend_cocoa.mm | 36 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index 41b4fe76b..e43adb651 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1875,9 +1875,9 @@ private: // --------------------------------------------------------------------------- -EXTERN_CVAR (Float, Gamma) +EXTERN_CVAR(Float, Gamma) -CUSTOM_CVAR (Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (NULL != screen) { @@ -1885,7 +1885,7 @@ CUSTOM_CVAR (Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } -CUSTOM_CVAR (Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (NULL != screen) { @@ -1893,7 +1893,7 @@ CUSTOM_CVAR (Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } -CUSTOM_CVAR (Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (NULL != screen) { @@ -2014,7 +2014,7 @@ DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, c PalEntry flashColor = 0; int flashAmount = 0; - if (old != NULL) + if (NULL != old) { if ( width == old->GetWidth() && height == old->GetHeight()) @@ -2049,7 +2049,7 @@ void CocoaVideo::SetWindowedScale(float scale) } -CocoaFrameBuffer::CocoaFrameBuffer (int width, int height, bool fullscreen) +CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) : DFrameBuffer(width, height) , m_needPaletteUpdate(false) , m_gamma(0.0f) @@ -2097,7 +2097,7 @@ void CocoaFrameBuffer::Unlock() { if (m_isUpdatePending && LockCount == 1) { - Update (); + Update(); } else if (--LockCount <= 0) { @@ -2165,10 +2165,12 @@ void CocoaFrameBuffer::UpdateColors() palette[i].b = m_gammaTable[2][m_palette[i].b]; } - if (m_flashAmount) + if (0 != m_flashAmount) { DoBlending(palette, palette, 256, - m_gammaTable[0][m_flashColor.r], m_gammaTable[1][m_flashColor.g], m_gammaTable[2][m_flashColor.b], + m_gammaTable[0][m_flashColor.r], + m_gammaTable[1][m_flashColor.g], + m_gammaTable[2][m_flashColor.b], m_flashAmount); } @@ -2214,7 +2216,9 @@ void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) if (0 != m_flashAmount) { - DoBlending (pal, pal, 256, m_flashColor.r, m_flashColor.g, m_flashColor.b, m_flashAmount); + DoBlending(pal, pal, 256, + m_flashColor.r, m_flashColor.g, m_flashColor.b, + m_flashAmount); } } @@ -2310,7 +2314,7 @@ void I_InitGraphics() ticker.SetGenericRepDefault(val, CVAR_Bool); Video = new CocoaVideo; - atterm (I_ShutdownGraphics); + atterm(I_ShutdownGraphics); } @@ -2361,9 +2365,9 @@ void I_ClosestResolution(int *width, int *height, int bits) for (iteration = 0; iteration < 2; ++iteration) { - Video->StartModeIterator (bits, fullscreen); + Video->StartModeIterator(bits, fullscreen); - while (Video->NextMode (&twidth, &theight, NULL)) + while (Video->NextMode(&twidth, &theight, NULL)) { if (twidth == *width && theight == *height) { @@ -2385,7 +2389,7 @@ void I_ClosestResolution(int *width, int *height, int bits) cheight = theight; } } - + if (closest != DWORD(-1)) { *width = cwidth; @@ -2405,7 +2409,7 @@ void I_SetFPSLimit(int limit) { } -CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (vid_maxfps < TICRATE && vid_maxfps != 0) { @@ -2424,7 +2428,7 @@ CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CCMD(vid_currentmode) { - Printf ("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); + Printf("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); } From cef8ae56325dd3ffddf8c755c0d59d9a652b062c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Dec 2014 19:19:15 +0100 Subject: [PATCH 059/117] - fixed: The A_BossDeath code in P_MorphedDeath was missing a NULL pointer check. --- src/g_shared/a_morph.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 6014d6fee..e3433f12e 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -529,24 +529,26 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor { AMorphedMonster *fakeme = static_cast(actor); AActor *realme = fakeme->UnmorphedMe; - if ((fakeme->UnmorphTime) && - (fakeme->MorphStyle & MORPH_UNDOBYDEATH) && - (realme)) + if (realme != NULL) { - int realstyle = fakeme->MorphStyle; - int realhealth = fakeme->health; - if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + if ((fakeme->UnmorphTime) && + (fakeme->MorphStyle & MORPH_UNDOBYDEATH)) { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; + int realstyle = fakeme->MorphStyle; + int realhealth = fakeme->health; + if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + { + *morphed = realme; + *morphedstyle = realstyle; + *morphedhealth = realhealth; + return true; + } + } + if (realme->flags4 & MF4_BOSSDEATH) + { + realme->health = 0; // make sure that A_BossDeath considers it dead. + CALL_ACTION(A_BossDeath, realme); } - } - if (realme->flags4 & MF4_BOSSDEATH) - { - realme->health = 0; // make sure that A_BossDeath considers it dead. - CALL_ACTION(A_BossDeath, realme); } fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() return false; From fdf2d6c493ea898e13e1b92b145566949f1ac38b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Dec 2014 12:47:48 -0600 Subject: [PATCH 060/117] - Cleaned up some of the reflective code. - Added null checks to AIMREFLECT. - The missile being reflected now corrects the z velocity to perfectly reflect towards the actor's middle. --- src/p_mobj.cpp | 79 ++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8c76d793e..a88ec6515 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1961,48 +1961,50 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) // Don't change the angle if there's THRUREFLECT on the monster. if (!(BlockingMobj->flags7 & MF7_THRUREFLECT)) { - int dir; - angle_t delta; - - if (BlockingMobj->flags7 & MF7_MIRRORREFLECT) - angle = mo->angle + ANG180; - else - angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); - + //int dir; + //angle_t delta; + bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle)); // Change angle for deflection/reflection - // AIMREFLECT calls precedence so make sure not to bother with adjusting here if declared. - if (!(BlockingMobj->flags7 & MF7_AIMREFLECT) && (mo->AdjustReflectionAngle(BlockingMobj, angle))) - { - goto explode; - } - // Reflect the missile along angle - if (BlockingMobj->flags7 & MF7_AIMREFLECT) + if (!dontReflect) { - dir = P_FaceMobj(mo, mo->target, &delta); - if (dir) - { // Turn clockwise - mo->angle += delta; + bool tg = (mo->target != NULL); + bool blockingtg = (BlockingMobj->target != NULL); + if (BlockingMobj->flags7 & MF7_AIMREFLECT && (tg || blockingtg)) + { + AActor *origin; + if (tg) + origin = mo->target; + else if (blockingtg) + origin = BlockingMobj->target; + + float speed = (float)(mo->Speed); + //dest->x - source->x + FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z); + velocity.Resize(speed); + angle = mo->angle >> ANGLETOFINESHIFT; + mo->velx = (fixed_t)(velocity.X); + mo->vely = (fixed_t)(velocity.Y); + mo->velz = (fixed_t)(velocity.Z); + /* + mo->velx = FixedMul(mo->Speed, finecosine[angle]); + mo->vely = FixedMul(mo->Speed, finesine[angle]); + mo->velz = -mo->velz; + */ } else - { // Turn counter clockwise - mo->angle -= delta; + { + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); + mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]); + mo->velz = -mo->velz / 2; } - angle = mo->angle >> ANGLETOFINESHIFT; - mo->velx = FixedMul(mo->Speed, finecosine[angle]); - mo->vely = FixedMul(mo->Speed, finesine[angle]); - mo->velz = -mo->velz; } else { - mo->angle = angle; - angle >>= ANGLETOFINESHIFT; - mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); - mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]); - mo->velz = -mo->velz / 2; - } - - + goto explode; + } } if (mo->flags2 & MF2_SEEKERMISSILE) { @@ -2928,8 +2930,10 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) if (flags2 & MF2_DONTREFLECT) return true; if (thing->flags7 & MF7_THRUREFLECT) return false; + if (thing->flags7 & MF7_MIRRORREFLECT) + angle += ANGLE_180; // Change angle for reflection - if (thing->flags4&MF4_SHIELDREFLECT) + else if (thing->flags4&MF4_SHIELDREFLECT) { // Shield reflection (from the Centaur if (abs (angle - thing->angle)>>24 > 45) @@ -2952,6 +2956,13 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) else angle -= ANG45; } + else if (thing->flags7 & MF7_AIMREFLECT) + { + if (this->target != NULL) + A_Face(this, this->target); + else if (thing->target != NULL) + A_Face(this, thing->target); + } else angle += ANGLE_1 * ((pr_reflect()%16)-8); return false; From 519ff8b7d114b6d38ba0813b8d7f509ef6e6329c Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Dec 2014 14:15:14 -0600 Subject: [PATCH 061/117] - HITTARGET/MASTER/TRACER now set the puff's pointer(s) within P_SpawnPuff. - PUFFGETSOWNER, for the sake of compatibility, maintains override for target. --- src/p_local.h | 2 +- src/p_map.cpp | 18 +++--------------- src/p_mobj.cpp | 10 +++++++++- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index c0a754e25..fe7958807 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -133,7 +133,7 @@ enum EPuffFlags PF_NORANDOMZ = 16 }; -AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0); +AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL); void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator); void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator); void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator); diff --git a/src/p_map.cpp b/src/p_map.cpp index 456db2d0f..aecead6ed 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3761,14 +3761,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, puffFlags |= PF_HITTHINGBLEED; // We must pass the unreplaced puff type here - puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING); - } - - if (puffDefaults != NULL && trace.Actor != NULL && puff != NULL) - { - if (puffDefaults->flags7 && MF7_HITTARGET) puff->target = trace.Actor; - if (puffDefaults->flags7 && MF7_HITMASTER) puff->master = trace.Actor; - if (puffDefaults->flags7 && MF7_HITTRACER) puff->tracer = trace.Actor; + puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING, trace.Actor); } // Allow puffs to inflict poison damage, so that hitscans can poison, too. @@ -4211,14 +4204,9 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i } if (spawnpuff) { - P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags); - } - if (hitactor != NULL && puffDefaults != NULL && thepuff != NULL) - { - if (puffDefaults->flags7 & MF7_HITTARGET) thepuff->target = hitactor; - if (puffDefaults->flags7 & MF7_HITMASTER) thepuff->master = hitactor; - if (puffDefaults->flags7 & MF7_HITTRACER) thepuff->tracer = hitactor; + P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags, hitactor); } + if (puffDefaults && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN) { P_PoisonMobj(hitactor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod, puffDefaults->PoisonDamageType); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a88ec6515..dbce9e2ff 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4930,7 +4930,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // P_SpawnPuff // -AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags) +AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict) { AActor *puff; @@ -4940,9 +4940,17 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE); if (puff == NULL) return NULL; + //Moved puff creation and target/master/tracer setting to here. + if (puff && vict) + { + if (puff->flags7 & MF7_HITTARGET) puff->target = vict; + if (puff->flags7 & MF7_HITMASTER) puff->master = vict; + if (puff->flags7 & MF7_HITTRACER) puff->tracer = vict; + } // [BB] If the puff came from a player, set the target of the puff to this player. if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER)) puff->target = source; + if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y); From b66c712446d6e534748dafdf8c179150b7bc4d62 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 28 Dec 2014 11:34:55 +0200 Subject: [PATCH 062/117] Moved out video handling from application controller --- src/posix/cocoa/i_backend_cocoa.mm | 952 +++++++++++++---------------- 1 file changed, 431 insertions(+), 521 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index e43adb651..6d93b7306 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1095,15 +1095,21 @@ namespace // --------------------------------------------------------------------------- -@interface FullscreenWindow : NSWindow +@interface CocoaWindow : NSWindow { - } -- (bool)canBecomeKeyWindow; +- (BOOL)canBecomeKeyWindow; -- (void)setLevel:(NSInteger)level; -- (void)setStyleMask:(NSUInteger)styleMask; +@end + + +@implementation CocoaWindow + +- (BOOL)canBecomeKeyWindow +{ + return true; +} @end @@ -1111,9 +1117,8 @@ namespace // --------------------------------------------------------------------------- -@interface FullscreenView : NSOpenGLView +@interface CocoaView : NSOpenGLView { - } - (void)resetCursorRects; @@ -1121,7 +1126,7 @@ namespace @end -@implementation FullscreenView +@implementation CocoaView - (void)resetCursorRects { @@ -1141,25 +1146,8 @@ namespace #endif { -@private - FullscreenWindow* m_window; - - uint8_t* m_softwareRenderingBuffer; - GLuint m_softwareRenderingTexture; - - int m_multisample; - - int m_width; - int m_height; - bool m_fullscreen; - bool m_hiDPI; - - bool m_openGLInitialized; } -- (id)init; -- (void)dealloc; - - (void)keyDown:(NSEvent*)theEvent; - (void)keyUp:(NSEvent*)theEvent; @@ -1170,123 +1158,16 @@ namespace - (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; -- (int)multisample; -- (void)setMultisample:(int)multisample; - -- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI; -- (void)useHiDPI:(bool)hiDPI; - -- (bool)fullscreen; - -- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height; -- (void*)softwareRenderingBuffer; - - (void)processEvents:(NSTimer*)timer; -- (void)invalidateCursorRects; - -- (void)setMainWindowVisible:(bool)visible; - -- (void)setWindowStyleMask:(NSUInteger)styleMask; - @end static ApplicationController* appCtrl; -// --------------------------------------------------------------------------- - - -@implementation FullscreenWindow - -static bool s_fullscreenNewAPI; - -+ (void)initialize -{ - // The following value shoud be equal to NSAppKitVersionNumber10_6 - // and it's hard-coded in order to build on earlier SDKs - s_fullscreenNewAPI = NSAppKitVersionNumber >= 1038; -} - -- (bool)canBecomeKeyWindow -{ - return true; -} - -- (void)setLevel:(NSInteger)level -{ - if (s_fullscreenNewAPI) - { - [super setLevel:level]; - } - else - { - // Old Carbon-based way to make fullscreen window above dock and menu - // It's supported on 64-bit, but on 10.6 and later the following is preferred: - // [NSWindow setLevel:NSMainMenuWindowLevel + 1] - - const SystemUIMode mode = LEVEL_FULLSCREEN == level - ? kUIModeAllHidden - : kUIModeNormal; - SetSystemUIMode(mode, 0); - } -} - -- (void)setStyleMask:(NSUInteger)styleMask -{ - if (s_fullscreenNewAPI) - { - [super setStyleMask:styleMask]; - } - else - { - [appCtrl setWindowStyleMask:styleMask]; - } -} - -@end - - -// --------------------------------------------------------------------------- - - @implementation ApplicationController -- (id)init -{ - self = [super init]; - - m_window = nil; - - m_softwareRenderingBuffer = NULL; - m_softwareRenderingTexture = 0; - - m_multisample = 0; - - m_width = -1; - m_height = -1; - m_fullscreen = false; - m_hiDPI = false; - - m_openGLInitialized = false; - - return self; -} - -- (void)dealloc -{ - delete[] m_softwareRenderingBuffer; - - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &m_softwareRenderingTexture); - - [m_window release]; - - [super dealloc]; -} - - - (void)keyDown:(NSEvent*)theEvent { // Empty but present to avoid playing of 'beep' alert sound @@ -1373,244 +1254,6 @@ static bool s_fullscreenNewAPI; } -- (int)multisample -{ - return m_multisample; -} - -- (void)setMultisample:(int)multisample -{ - m_multisample = multisample; -} - - -- (FullscreenWindow*)createWindow:(NSUInteger)styleMask -{ - FullscreenWindow* window = [[FullscreenWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; - [window setOpaque:YES]; - [window makeFirstResponder:self]; - [window setAcceptsMouseMovedEvents:YES]; - - return window; -} - -- (void)initializeOpenGL -{ - if (m_openGLInitialized) - { - return; - } - - m_window = [self createWindow:STYLE_MASK_WINDOWED]; - - // Create OpenGL context and view - - NSOpenGLPixelFormatAttribute attributes[16]; - size_t i = 0; - - attributes[i++] = NSOpenGLPFADoubleBuffer; - attributes[i++] = NSOpenGLPFAColorSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(32); - attributes[i++] = NSOpenGLPFADepthSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(24); - attributes[i++] = NSOpenGLPFAStencilSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(8); - - if (m_multisample) - { - attributes[i++] = NSOpenGLPFAMultisample; - attributes[i++] = NSOpenGLPFASampleBuffers; - attributes[i++] = NSOpenGLPixelFormatAttribute(1); - attributes[i++] = NSOpenGLPFASamples; - attributes[i++] = NSOpenGLPixelFormatAttribute(m_multisample); - } - - attributes[i] = NSOpenGLPixelFormatAttribute(0); - - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - - const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; - NSOpenGLView* glView = [[FullscreenView alloc] initWithFrame:contentRect - pixelFormat:pixelFormat]; - [[glView openGLContext] makeCurrentContext]; - - [m_window setContentView:glView]; - - m_openGLInitialized = true; -} - -- (void)setFullscreenModeWidth:(int)width height:(int)height -{ - NSScreen* screen = [m_window screen]; - - const NSRect screenFrame = [screen frame]; - const NSRect displayRect = vid_hidpi - ? [screen convertRectToBacking:screenFrame] - : screenFrame; - - const float displayWidth = displayRect.size.width; - const float displayHeight = displayRect.size.height; - - const float pixelScaleFactorX = displayWidth / static_cast(width ); - const float pixelScaleFactorY = displayHeight / static_cast(height); - - rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - - rbOpts.width = width * rbOpts.pixelScale; - rbOpts.height = height * rbOpts.pixelScale; - - rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; - rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; - - if (!m_fullscreen) - { - [m_window setLevel:LEVEL_FULLSCREEN]; - [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; - [m_window setHidesOnDeactivate:YES]; - } - - [m_window setFrame:displayRect display:YES]; - [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; -} - -- (void)setWindowedModeWidth:(int)width height:(int)height -{ - rbOpts.pixelScale = 1.0f; - - rbOpts.width = static_cast(width ); - rbOpts.height = static_cast(height); - - rbOpts.shiftX = 0.0f; - rbOpts.shiftY = 0.0f; - - const NSSize windowPixelSize = NSMakeSize(width, height); - const NSSize windowSize = vid_hidpi - ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] - : windowPixelSize; - - if (m_fullscreen) - { - [m_window setLevel:LEVEL_WINDOWED]; - [m_window setStyleMask:STYLE_MASK_WINDOWED]; - [m_window setHidesOnDeactivate:NO]; - } - - [m_window setContentSize:windowSize]; - [m_window center]; - - NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; - [closeButton setAction:@selector(terminate:)]; - [closeButton setTarget:NSApp]; -} - -- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI -{ - if (fullscreen == m_fullscreen - && width == m_width - && height == m_height - && hiDPI == m_hiDPI) - { - return; - } - - [self initializeOpenGL]; - - if (IsHiDPISupported()) - { - NSOpenGLView* const glView = [m_window contentView]; - [glView setWantsBestResolutionOpenGLSurface:hiDPI]; - } - - if (fullscreen) - { - [self setFullscreenModeWidth:width height:height]; - } - else - { - [self setWindowedModeWidth:width height:height]; - } - - rbOpts.dirty = true; - - const NSSize viewSize = GetRealContentViewSize(m_window); - - glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - CGLFlushDrawable(CGLGetCurrentContext()); - - static NSString* const TITLE_STRING = - [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; - [m_window setTitle:TITLE_STRING]; - - if (![m_window isKeyWindow]) - { - [m_window makeKeyAndOrderFront:nil]; - } - - m_fullscreen = fullscreen; - m_width = width; - m_height = height; - m_hiDPI = hiDPI; -} - -- (void)useHiDPI:(bool)hiDPI -{ - if (!m_openGLInitialized) - { - return; - } - - [self changeVideoResolution:m_fullscreen - width:m_width - height:m_height - useHiDPI:hiDPI]; -} - - -- (bool)fullscreen -{ - return m_fullscreen; -} - - -- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height -{ - if (0 == m_softwareRenderingTexture) - { - glEnable(GL_TEXTURE_RECTANGLE_ARB); - - glGenTextures(1, &m_softwareRenderingTexture); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_softwareRenderingTexture); - glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); - - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - delete[] m_softwareRenderingBuffer; - m_softwareRenderingBuffer = new uint8_t[width * height * BYTES_PER_PIXEL]; - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, width, height, 0.0, -1.0, 1.0); -} - -- (void*)softwareRenderingBuffer -{ - return m_softwareRenderingBuffer; -} - - - (void)processEvents:(NSTimer*)timer { ZD_UNUSED(timer); @@ -1677,144 +1320,17 @@ static bool s_fullscreenNewAPI; [pool release]; } - -- (void)invalidateCursorRects -{ - [m_window invalidateCursorRectsForView:[m_window contentView]]; -} - - -- (void)setMainWindowVisible:(bool)visible -{ - if (visible) - { - [m_window orderFront:nil]; - } - else - { - [m_window orderOut:nil]; - } -} - - -- (void)setWindowStyleMask:(NSUInteger)styleMask -{ - // Before 10.6 it's impossible to change window's style mask - // To workaround this new window should be created with required style mask - // This method should not be called when building for Snow Leopard or newer - - FullscreenWindow* tempWindow = [self createWindow:styleMask]; - [tempWindow setContentView:[m_window contentView]]; - - [m_window close]; - m_window = tempWindow; -} - @end // --------------------------------------------------------------------------- -CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (IsHiDPISupported()) - { - [appCtrl useHiDPI:self]; - } - else if (0 != self) - { - self = 0; - } -} - - -// --------------------------------------------------------------------------- - - -void I_SetMainWindowVisible(bool visible) -{ - [appCtrl setMainWindowVisible:visible]; - - SetNativeMouse(!visible); -} - - -// --------------------------------------------------------------------------- - - -bool I_SetCursor(FTexture* cursorpic) -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) - { - s_cursor = [NSCursor arrowCursor]; - } - else - { - // Create bitmap image representation - - const NSInteger imageWidth = cursorpic->GetWidth(); - const NSInteger imageHeight = cursorpic->GetHeight(); - const NSInteger imagePitch = imageWidth * 4; - - NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; - [bitmapImageRep initWithBitmapDataPlanes:NULL - pixelsWide:imageWidth - pixelsHigh:imageHeight - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:imagePitch - bitsPerPixel:0]; - - // Load bitmap data to representation - - BYTE* buffer = [bitmapImageRep bitmapData]; - memset(buffer, 0, imagePitch * imageHeight); - - FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); - cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); - - // Swap red and blue components in each pixel - - for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) - { - const size_t offset = i * 4; - - const BYTE temp = buffer[offset ]; - buffer[offset ] = buffer[offset + 2]; - buffer[offset + 2] = temp; - } - - // Create image from representation and set it as cursor - - NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType - properties:nil]; - NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; - - s_cursor = [[NSCursor alloc] initWithImage:cursorImage - hotSpot:NSMakePoint(0.0f, 0.0f)]; - } - - [appCtrl invalidateCursorRects]; - - [pool release]; - - return true; -} - - -// --------------------------------------------------------------------------- - - class CocoaVideo : public IVideo { public: - CocoaVideo(); + explicit CocoaVideo(int multisample); + ~CocoaVideo(); virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } virtual void SetWindowedScale(float scale); @@ -1824,8 +1340,27 @@ public: virtual void StartModeIterator(int bits, bool fs); virtual bool NextMode(int* width, int* height, bool* letterbox); + static bool IsFullscreen(); + static void UseHiDPI(bool hiDPI); + static void InvalidateCursorRects(); + static void SetWindowVisible(bool visible); + private: size_t m_modeIterator; + + CocoaWindow* m_window; + + int m_width; + int m_height; + bool m_fullscreen; + bool m_hiDPI; + + void SetStyleMask(NSUInteger styleMask); + void SetFullscreenMode(int width, int height); + void SetWindowedMode(int width, int height); + void SetMode(int width, int height, bool fullscreen, bool hiDPI); + + static CocoaVideo* GetInstance(); }; @@ -1866,6 +1401,9 @@ private: bool m_isUpdatePending; + uint8_t* m_pixelBuffer; + GLuint m_texture; + void Flip(); void UpdateColors(); @@ -1905,7 +1443,10 @@ CUSTOM_CVAR(Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // --------------------------------------------------------------------------- -static const struct +namespace +{ + +const struct { uint16_t width; uint16_t height; @@ -1971,16 +1512,77 @@ VideoModes[] = }; -static cycle_t BlitCycles; -static cycle_t FlipCycles; +cycle_t BlitCycles; +cycle_t FlipCycles; + + +CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) +{ + CocoaWindow* window = [[CocoaWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [window setOpaque:YES]; + [window makeFirstResponder:appCtrl]; + [window setAcceptsMouseMovedEvents:YES]; + + return window; +} + +} // unnamed namespace // --------------------------------------------------------------------------- -CocoaVideo::CocoaVideo() +CocoaVideo::CocoaVideo(const int multisample) : m_modeIterator(0) +, m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +//, m_multisample(0) +, m_width(-1) +, m_height(-1) +, m_fullscreen(false) +, m_hiDPI(false) { + // Set attributes for OpenGL context + + NSOpenGLPixelFormatAttribute attributes[16]; + size_t i = 0; + + attributes[i++] = NSOpenGLPFADoubleBuffer; + attributes[i++] = NSOpenGLPFAColorSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(32); + attributes[i++] = NSOpenGLPFADepthSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(24); + attributes[i++] = NSOpenGLPFAStencilSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(8); + + if (multisample) + { + attributes[i++] = NSOpenGLPFAMultisample; + attributes[i++] = NSOpenGLPFASampleBuffers; + attributes[i++] = NSOpenGLPixelFormatAttribute(1); + attributes[i++] = NSOpenGLPFASamples; + attributes[i++] = NSOpenGLPixelFormatAttribute(multisample); + } + + attributes[i] = NSOpenGLPixelFormatAttribute(0); + + // Create OpenGL context and view + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + + const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; + NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect + pixelFormat:pixelFormat]; + [[glView openGLContext] makeCurrentContext]; + + [m_window setContentView:glView]; +} + +CocoaVideo::~CocoaVideo() +{ + [m_window release]; } void CocoaVideo::StartModeIterator(int bits, bool fs) @@ -2016,14 +1618,9 @@ DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, c if (NULL != old) { - if ( width == old->GetWidth() - && height == old->GetHeight()) + if (width == m_width && height == m_height) { - [appCtrl changeVideoResolution:fullscreen - width:width - height:height - useHiDPI:vid_hidpi]; - + SetMode(width, height, fullscreen, vid_hidpi); return old; } @@ -2041,6 +1638,8 @@ DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, c CocoaFrameBuffer* fb = new CocoaFrameBuffer(width, height, fullscreen); fb->SetFlash(flashColor, flashAmount); + SetMode(width, height, fullscreen, vid_hidpi); + return fb; } @@ -2049,6 +1648,212 @@ void CocoaVideo::SetWindowedScale(float scale) } +bool CocoaVideo::IsFullscreen() +{ + CocoaVideo* const video = GetInstance(); + return NULL == video + ? false + : video->m_fullscreen; +} + +void CocoaVideo::UseHiDPI(const bool hiDPI) +{ + if (CocoaVideo* const video = GetInstance()) + { + video->SetMode(video->m_width, video->m_height, video->m_fullscreen, hiDPI); + } +} + +void CocoaVideo::InvalidateCursorRects() +{ + if (CocoaVideo* const video = GetInstance()) + { + [video->m_window invalidateCursorRectsForView:[video->m_window contentView]]; + } +} + +void CocoaVideo::SetWindowVisible(bool visible) +{ + if (CocoaVideo* const video = GetInstance()) + { + if (visible) + { + [video->m_window orderFront:nil]; + } + else + { + [video->m_window orderOut:nil]; + } + } +} + + +static bool HasModernFullscreenAPI() +{ + // The following value shoud be equal to NSAppKitVersionNumber10_6 + // and it's hard-coded in order to build on earlier SDKs + + return NSAppKitVersionNumber >= 1038; +} + +void CocoaVideo::SetStyleMask(const NSUInteger styleMask) +{ + // Before 10.6 it's impossible to change window's style mask + // To workaround this new window should be created with required style mask + // This method should not be called when running on Snow Leopard or newer + + assert(!HasModernFullscreenAPI()); + + CocoaWindow* tempWindow = CreateCocoaWindow(styleMask); + [tempWindow setContentView:[m_window contentView]]; + + [m_window close]; + m_window = tempWindow; +} + +void CocoaVideo::SetFullscreenMode(const int width, const int height) +{ + NSScreen* screen = [m_window screen]; + + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = vid_hidpi + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast(width ); + const float pixelScaleFactorY = displayHeight / static_cast(height); + + rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); + + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; + + rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; + rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; + + if (!m_fullscreen) + { + if (HasModernFullscreenAPI()) + { + [m_window setLevel:LEVEL_FULLSCREEN]; + [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; + } + else + { + // Old Carbon-based way to make fullscreen window above dock and menu + // It's supported on 64-bit, but on 10.6 and later the following is preferred: + // [NSWindow setLevel:NSMainMenuWindowLevel + 1] + + SetSystemUIMode(kUIModeAllHidden, 0); + SetStyleMask(STYLE_MASK_FULLSCREEN); + } + + [m_window setHidesOnDeactivate:YES]; + } + + [m_window setFrame:displayRect display:YES]; + [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; +} + +void CocoaVideo::SetWindowedMode(const int width, const int height) +{ + rbOpts.pixelScale = 1.0f; + + rbOpts.width = static_cast(width ); + rbOpts.height = static_cast(height); + + rbOpts.shiftX = 0.0f; + rbOpts.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = vid_hidpi + ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + if (m_fullscreen) + { + if (HasModernFullscreenAPI()) + { + [m_window setLevel:LEVEL_WINDOWED]; + [m_window setStyleMask:STYLE_MASK_WINDOWED]; + } + else + { + SetSystemUIMode(kUIModeNormal, 0); + SetStyleMask(STYLE_MASK_WINDOWED); + } + + [m_window setHidesOnDeactivate:NO]; + } + + [m_window setContentSize:windowSize]; + [m_window center]; + + NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(terminate:)]; + [closeButton setTarget:NSApp]; +} + +void CocoaVideo::SetMode(const int width, const int height, const bool fullscreen, const bool hiDPI) +{ + if (fullscreen == m_fullscreen + && width == m_width + && height == m_height + && hiDPI == m_hiDPI) + { + return; + } + + if (IsHiDPISupported()) + { + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; + } + + if (fullscreen) + { + SetFullscreenMode(width, height); + } + else + { + SetWindowedMode(width, height); + } + + rbOpts.dirty = true; + + const NSSize viewSize = GetRealContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + CGLFlushDrawable(CGLGetCurrentContext()); + + static NSString* const TITLE_STRING = + [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + [m_window setTitle:TITLE_STRING]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; +} + + +CocoaVideo* CocoaVideo::GetInstance() +{ + return static_cast(Video); +} + + CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) : DFrameBuffer(width, height) , m_needPaletteUpdate(false) @@ -2056,14 +1861,26 @@ CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) , m_needGammaUpdate(false) , m_flashAmount(0) , m_isUpdatePending(false) +, m_pixelBuffer(new uint8_t[width * height * BYTES_PER_PIXEL]) +, m_texture(0) { - [appCtrl changeVideoResolution:fullscreen - width:width - height:height - useHiDPI:vid_hidpi]; + glEnable(GL_TEXTURE_RECTANGLE_ARB); - [appCtrl setupSoftwareRenderingWithWidth:width - height:height]; + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_texture); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, width, height, 0.0, -1.0, 1.0); GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); @@ -2081,6 +1898,10 @@ CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) CocoaFrameBuffer::~CocoaFrameBuffer() { + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_texture); + + delete[] m_pixelBuffer; } int CocoaFrameBuffer::GetPageCount() @@ -2128,7 +1949,7 @@ void CocoaFrameBuffer::Update() FlipCycles.Reset(); BlitCycles.Clock(); - GPfx.Convert(MemBuffer, Pitch, [appCtrl softwareRenderingBuffer], Width * BYTES_PER_PIXEL, + GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, Width, Height, FRACUNIT, FRACUNIT, 0, 0); FlipCycles.Clock(); @@ -2224,7 +2045,7 @@ void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) bool CocoaFrameBuffer::IsFullscreen() { - return [appCtrl fullscreen]; + return CocoaVideo::IsFullscreen(); } void CocoaFrameBuffer::SetVSync(bool vsync) @@ -2262,7 +2083,7 @@ void CocoaFrameBuffer::Flip() #endif // __LITTLE_ENDIAN__ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - Width, Height, 0, format, GL_UNSIGNED_BYTE, [appCtrl softwareRenderingBuffer]); + Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -2290,7 +2111,7 @@ ADD_STAT(blit) } -IVideo *Video; +IVideo* Video; void I_ShutdownGraphics() @@ -2313,7 +2134,7 @@ void I_InitGraphics() val.Bool = !!Args->CheckParm("-devparm"); ticker.SetGenericRepDefault(val, CVAR_Bool); - Video = new CocoaVideo; + Video = new CocoaVideo(0); atterm(I_ShutdownGraphics); } @@ -2425,6 +2246,17 @@ CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (IsHiDPISupported()) + { + CocoaVideo::UseHiDPI(self); + } + else if (0 != self) + { + self = 0; + } +} CCMD(vid_currentmode) { @@ -2432,6 +2264,84 @@ CCMD(vid_currentmode) } +// --------------------------------------------------------------------------- + + +bool I_SetCursor(FTexture* cursorpic) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) + { + s_cursor = [NSCursor arrowCursor]; + } + else + { + // Create bitmap image representation + + const NSInteger imageWidth = cursorpic->GetWidth(); + const NSInteger imageHeight = cursorpic->GetHeight(); + const NSInteger imagePitch = imageWidth * 4; + + NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; + [bitmapImageRep initWithBitmapDataPlanes:NULL + pixelsWide:imageWidth + pixelsHigh:imageHeight + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:imagePitch + bitsPerPixel:0]; + + // Load bitmap data to representation + + BYTE* buffer = [bitmapImageRep bitmapData]; + memset(buffer, 0, imagePitch * imageHeight); + + FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); + cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); + + // Swap red and blue components in each pixel + + for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) + { + const size_t offset = i * 4; + + const BYTE temp = buffer[offset ]; + buffer[offset ] = buffer[offset + 2]; + buffer[offset + 2] = temp; + } + + // Create image from representation and set it as cursor + + NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType + properties:nil]; + NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; + + s_cursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSMakePoint(0.0f, 0.0f)]; + } + + CocoaVideo::InvalidateCursorRects(); + + [pool release]; + + return true; +} + + +void I_SetMainWindowVisible(bool visible) +{ + CocoaVideo::SetWindowVisible(visible); + SetNativeMouse(!visible); +} + + +// --------------------------------------------------------------------------- + + namespace { From 40d4dc502e261a90ceace828134ab95efc99e95b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 28 Dec 2014 12:48:08 +0200 Subject: [PATCH 063/117] Added comprehensive video modes iteration Added letterboxing status for fullscreen modes Brought back vid_listmodes console command --- src/posix/cocoa/i_backend_cocoa.mm | 82 ++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm index 6d93b7306..0636789ca 100644 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ b/src/posix/cocoa/i_backend_cocoa.mm @@ -1337,7 +1337,7 @@ public: virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); - virtual void StartModeIterator(int bits, bool fs); + virtual void StartModeIterator(int bits, bool fullscreen); virtual bool NextMode(int* width, int* height, bool* letterbox); static bool IsFullscreen(); @@ -1346,7 +1346,14 @@ public: static void SetWindowVisible(bool visible); private: - size_t m_modeIterator; + struct ModeIterator + { + size_t index; + int bits; + bool fullscreen; + }; + + ModeIterator m_modeIterator; CocoaWindow* m_window; @@ -1536,14 +1543,14 @@ CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) CocoaVideo::CocoaVideo(const int multisample) -: m_modeIterator(0) -, m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) -//, m_multisample(0) +: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) , m_width(-1) , m_height(-1) , m_fullscreen(false) , m_hiDPI(false) { + memset(&m_modeIterator, 0, sizeof m_modeIterator); + // Set attributes for OpenGL context NSOpenGLPixelFormatAttribute attributes[16]; @@ -1585,26 +1592,43 @@ CocoaVideo::~CocoaVideo() [m_window release]; } -void CocoaVideo::StartModeIterator(int bits, bool fs) +void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) { - ZD_UNUSED(bits); - ZD_UNUSED(fs); - - m_modeIterator = 0; + m_modeIterator.index = 0; + m_modeIterator.bits = bits; + m_modeIterator.fullscreen = fullscreen; } -bool CocoaVideo::NextMode(int* width, int* height, bool* letterbox) +bool CocoaVideo::NextMode(int* const width, int* const height, bool* const letterbox) { assert(NULL != width); assert(NULL != height); - ZD_UNUSED(letterbox); - if (m_modeIterator < sizeof(VideoModes) / sizeof(VideoModes[0])) + const int bits = m_modeIterator.bits; + + if (8 != bits && 16 != bits && 24 != bits && 32 != bits) { - *width = VideoModes[m_modeIterator].width; - *height = VideoModes[m_modeIterator].height; + return false; + } + + size_t& index = m_modeIterator.index; + + if (index < sizeof(VideoModes) / sizeof(VideoModes[0])) + { + *width = VideoModes[index].width; + *height = VideoModes[index].height; + + if (m_modeIterator.fullscreen && NULL != letterbox) + { + const NSSize screenSize = [[m_window screen] frame].size; + const float screenRatio = screenSize.width / screenSize.height; + const float modeRatio = float(*width) / *height; + + *letterbox = fabs(screenRatio - modeRatio) > 0.001f; + } + + ++index; - ++m_modeIterator; return true; } @@ -2258,6 +2282,32 @@ CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } + +CCMD(vid_listmodes) +{ + if (Video == NULL) + { + return; + } + + static const char* const ratios[5] = { "", " - 16:9", " - 16:10", " - 17:10", " - 5:4" }; + int width, height; + bool letterbox; + + Video->StartModeIterator(32, screen->IsFullscreen()); + + while (Video->NextMode(&width, &height, &letterbox)) + { + const bool current = width == DisplayWidth && height == DisplayHeight; + const int ratio = CheckRatio(width, height); + + Printf(current ? PRINT_BOLD : PRINT_HIGH, "%s%4d x%5d x%3d%s%s\n", + current || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, + width, height, 32, ratios[ratio], + current || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); + } +} + CCMD(vid_currentmode) { Printf("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); From a67dc4148b014c707c095295244cc836589ceea9 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 28 Dec 2014 16:35:00 +0200 Subject: [PATCH 064/117] Split implementation of native OS X backend into several files --- src/CMakeLists.txt | 6 +- src/posix/cocoa/i_backend_cocoa.mm | 2580 ---------------------------- src/posix/cocoa/i_common.h | 170 ++ src/posix/cocoa/i_input.mm | 692 ++++++++ src/posix/cocoa/i_main.mm | 626 +++++++ src/posix/cocoa/i_video.mm | 1226 +++++++++++++ 6 files changed, 2718 insertions(+), 2582 deletions(-) delete mode 100644 src/posix/cocoa/i_backend_cocoa.mm create mode 100644 src/posix/cocoa/i_common.h create mode 100644 src/posix/cocoa/i_input.mm create mode 100644 src/posix/cocoa/i_main.mm create mode 100644 src/posix/cocoa/i_video.mm diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd2568f7d..066a92c36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -575,9 +575,11 @@ set( PLAT_COCOA_SOURCES posix/cocoa/hid/IOHIDElement_.c posix/cocoa/hid/ImmrHIDUtilAddOn.c posix/cocoa/critsec.cpp - posix/cocoa/i_backend_cocoa.mm + posix/cocoa/i_input.mm posix/cocoa/i_joystick.cpp - posix/cocoa/i_timer.cpp ) + posix/cocoa/i_main.mm + posix/cocoa/i_timer.cpp + posix/cocoa/i_video.mm ) if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) diff --git a/src/posix/cocoa/i_backend_cocoa.mm b/src/posix/cocoa/i_backend_cocoa.mm deleted file mode 100644 index 0636789ca..000000000 --- a/src/posix/cocoa/i_backend_cocoa.mm +++ /dev/null @@ -1,2580 +0,0 @@ -/* - ** i_backend_cocoa.mm - ** - **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -// Avoid collision between DObject class and Objective-C -#define Class ObjectClass - -#include "bitmap.h" -#include "c_console.h" -#include "c_dispatch.h" -#include "cmdlib.h" -#include "d_event.h" -#include "d_gui.h" -#include "d_main.h" -#include "dikeys.h" -#include "doomdef.h" -#include "doomerrors.h" -#include "doomstat.h" -#include "hardware.h" -#include "m_argv.h" -#include "r_renderer.h" -#include "r_swrenderer.h" -#include "s_sound.h" -#include "stats.h" -#include "textures.h" -#include "v_palette.h" -#include "v_pfx.h" -#include "v_text.h" -#include "v_video.h" -#include "version.h" -#include "i_rbopts.h" -#include "i_osversion.h" -#include "i_system.h" - -#undef Class - - -#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) - - -// --------------------------------------------------------------------------- - - -// The following definitions are required to build with older OS X SDKs - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - -typedef unsigned int NSUInteger; -typedef int NSInteger; - -typedef float CGFloat; - -// From HIToolbox/Events.h -enum -{ - kVK_Return = 0x24, - kVK_Tab = 0x30, - kVK_Space = 0x31, - kVK_Delete = 0x33, - kVK_Escape = 0x35, - kVK_Command = 0x37, - kVK_Shift = 0x38, - kVK_CapsLock = 0x39, - kVK_Option = 0x3A, - kVK_Control = 0x3B, - kVK_RightShift = 0x3C, - kVK_RightOption = 0x3D, - kVK_RightControl = 0x3E, - kVK_Function = 0x3F, - kVK_F17 = 0x40, - kVK_VolumeUp = 0x48, - kVK_VolumeDown = 0x49, - kVK_Mute = 0x4A, - kVK_F18 = 0x4F, - kVK_F19 = 0x50, - kVK_F20 = 0x5A, - kVK_F5 = 0x60, - kVK_F6 = 0x61, - kVK_F7 = 0x62, - kVK_F3 = 0x63, - kVK_F8 = 0x64, - kVK_F9 = 0x65, - kVK_F11 = 0x67, - kVK_F13 = 0x69, - kVK_F16 = 0x6A, - kVK_F14 = 0x6B, - kVK_F10 = 0x6D, - kVK_F12 = 0x6F, - kVK_F15 = 0x71, - kVK_Help = 0x72, - kVK_Home = 0x73, - kVK_PageUp = 0x74, - kVK_ForwardDelete = 0x75, - kVK_F4 = 0x76, - kVK_End = 0x77, - kVK_F2 = 0x78, - kVK_PageDown = 0x79, - kVK_F1 = 0x7A, - kVK_LeftArrow = 0x7B, - kVK_RightArrow = 0x7C, - kVK_DownArrow = 0x7D, - kVK_UpArrow = 0x7E -}; - -@interface NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint; -@end - -@implementation NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint -{ - return [self convertPoint:aPoint fromView:nil]; -} -@end - -#endif // prior to 10.5 - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 - -enum -{ - NSApplicationActivationPolicyRegular -}; - -typedef NSInteger NSApplicationActivationPolicy; - -@interface NSApplication(ActivationPolicy) -- (BOOL)setActivationPolicy:(NSApplicationActivationPolicy)activationPolicy; -@end - -@interface NSWindow(SetStyleMask) -- (void)setStyleMask:(NSUInteger)styleMask; -@end - -#endif // prior to 10.6 - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 - -@interface NSView(HiDPIStubs) -- (NSPoint)convertPointToBacking:(NSPoint)aPoint; -- (NSSize)convertSizeToBacking:(NSSize)aSize; -- (NSSize)convertSizeFromBacking:(NSSize)aSize; - -- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag; -@end - -@interface NSScreen(HiDPIStubs) -- (NSRect)convertRectToBacking:(NSRect)aRect; -@end - -#endif // prior to 10.7 - - -// --------------------------------------------------------------------------- - - -RenderBufferOptions rbOpts; - -EXTERN_CVAR(Bool, ticker ) -EXTERN_CVAR(Bool, vid_hidpi ) -EXTERN_CVAR(Bool, vid_vsync ) -EXTERN_CVAR(Int, vid_defwidth ) -EXTERN_CVAR(Int, vid_defheight) - -CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) -{ - if (self < 0) - { - self = 0; - } - else if (self > 2) - { - self = 2; - } -} - -bool GUICapture; - - -extern int paused, chatmodeon; -extern constate_e ConsoleState; - -EXTERN_CVAR(Int, m_use_mouse); - -void I_StartupJoysticks(); -void I_ShutdownJoysticks(); - - -// --------------------------------------------------------------------------- - - -extern int NewWidth, NewHeight, NewBits, DisplayBits; - - -CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - NewWidth = screen->GetWidth(); - NewHeight = screen->GetHeight(); - NewBits = DisplayBits; - setmodeneeded = true; -} - - -// --------------------------------------------------------------------------- - - -DArgs *Args; // command line arguments - -namespace -{ - -// The maximum number of functions that can be registered with atterm. -static const size_t MAX_TERMS = 64; - -static void (*TermFuncs[MAX_TERMS])(); -static const char *TermNames[MAX_TERMS]; -static size_t NumTerms; - -void call_terms() -{ - while (NumTerms > 0) - { - TermFuncs[--NumTerms](); - } -} - -} // unnamed namespace - - -void addterm(void (*func)(), const char *name) -{ - // Make sure this function wasn't already registered. - - for (size_t i = 0; i < NumTerms; ++i) - { - if (TermFuncs[i] == func) - { - return; - } - } - - if (NumTerms == MAX_TERMS) - { - func(); - I_FatalError("Too many exit functions registered."); - } - - TermNames[NumTerms] = name; - TermFuncs[NumTerms] = func; - - ++NumTerms; -} - -void popterm() -{ - if (NumTerms) - { - --NumTerms; - } -} - - -void I_SetMainWindowVisible(bool); - -void Mac_I_FatalError(const char* const message) -{ - I_SetMainWindowVisible(false); - - const CFStringRef errorString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, - message, kCFStringEncodingASCII, kCFAllocatorNull); - - if (NULL != errorString) - { - CFOptionFlags dummy; - - CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, - CFSTR("Fatal Error"), errorString, CFSTR("Exit"), NULL, NULL, &dummy); - - CFRelease(errorString); - } -} - - -namespace -{ - -const int ARGC_MAX = 64; - -int s_argc; -char* s_argv[ARGC_MAX]; - -TArray s_argvStorage; - -bool s_restartedFromWADPicker; - - -bool s_nativeMouse = true; - -// TODO: remove this magic! -size_t s_skipMouseMoves; - -NSCursor* s_cursor; - - -void NewFailure() -{ - I_FatalError("Failed to allocate memory from system heap"); -} - - -int OriginalMain(int argc, char** argv) -{ - printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n", - GetVersionString(), GetGitTime(), __DATE__); - - seteuid(getuid()); - std::set_new_handler(NewFailure); - - // Set LC_NUMERIC environment variable in case some library decides to - // clear the setlocale call at least this will be correct. - // Note that the LANG environment variable is overridden by LC_* - setenv("LC_NUMERIC", "C", 1); - setlocale(LC_ALL, "C"); - - // Set reasonable default values for video settings - const NSSize screenSize = [[NSScreen mainScreen] frame].size; - vid_defwidth = static_cast(screenSize.width); - vid_defheight = static_cast(screenSize.height); - vid_vsync = true; - fullscreen = true; - - try - { - Args = new DArgs(argc, argv); - - /* - killough 1/98: - - This fixes some problems with exit handling - during abnormal situations. - - The old code called I_Quit() to end program, - while now I_Quit() is installed as an exit - handler and exit() is called to exit, either - normally or abnormally. Seg faults are caught - and the error handler is used, to prevent - being left in graphics mode or having very - loud SFX noise because the sound card is - left in an unstable state. - */ - - atexit (call_terms); - atterm (I_Quit); - - // Should we even be doing anything with progdir on Unix systems? - char program[PATH_MAX]; - if (realpath (argv[0], program) == NULL) - strcpy (program, argv[0]); - char *slash = strrchr (program, '/'); - if (slash != NULL) - { - *(slash + 1) = '\0'; - progdir = program; - } - else - { - progdir = "./"; - } - - I_StartupJoysticks(); - atterm(I_ShutdownJoysticks); - - C_InitConsole(80 * 8, 25 * 8, false); - D_DoomMain(); - } - catch(const CDoomError& error) - { - const char* const message = error.GetMessage(); - - if (NULL != message) - { - fprintf(stderr, "%s\n", message); - Mac_I_FatalError(message); - } - - exit(-1); - } - catch(...) - { - call_terms(); - throw; - } - - return 0; -} - - -// --------------------------------------------------------------------------- - - -void CheckGUICapture() -{ - const bool wantCapture = (MENU_Off == menuactive) - ? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon) - : (menuactive == MENU_On || menuactive == MENU_OnNoPause); - - if (wantCapture != GUICapture) - { - GUICapture = wantCapture; - - ResetButtonStates(); - } -} - -void CenterCursor() -{ - NSWindow* window = [NSApp keyWindow]; - if (nil == window) - { - return; - } - - const NSRect displayRect = [[window screen] frame]; - const NSRect windowRect = [window frame]; - const CGPoint centerPoint = CGPointMake(NSMidX(windowRect), displayRect.size.height - NSMidY(windowRect)); - - CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); - - if (NULL != eventSource) - { - CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource, - kCGEventMouseMoved, centerPoint, kCGMouseButtonLeft); - - if (NULL != mouseMoveEvent) - { - CGEventPost(kCGHIDEventTap, mouseMoveEvent); - CFRelease(mouseMoveEvent); - } - - CFRelease(eventSource); - } - - // TODO: remove this magic! - s_skipMouseMoves = 2; -} - - -bool IsInGame() -{ - switch (mouse_capturemode) - { - default: - case 0: - return gamestate == GS_LEVEL; - - case 1: - return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; - - case 2: - return true; - } -} - -void SetNativeMouse(bool wantNative) -{ - if (wantNative != s_nativeMouse) - { - s_nativeMouse = wantNative; - - if (!wantNative) - { - CenterCursor(); - } - - CGAssociateMouseAndMouseCursorPosition(wantNative); - - if (wantNative) - { - [NSCursor unhide]; - } - else - { - [NSCursor hide]; - } - } -} - -void CheckNativeMouse() -{ - bool windowed = (NULL == screen) || !screen->IsFullscreen(); - bool wantNative; - - if (windowed) - { - if (![NSApp isActive] || !use_mouse) - { - wantNative = true; - } - else if (MENU_WaitKey == menuactive) - { - wantNative = false; - } - else - { - wantNative = (!m_use_mouse || MENU_WaitKey != menuactive) - && (!IsInGame() || GUICapture || paused || demoplayback); - } - } - else - { - // ungrab mouse when in the menu with mouse control on. - wantNative = m_use_mouse - && (MENU_On == menuactive || MENU_OnNoPause == menuactive); - } - - SetNativeMouse(wantNative); -} - -} // unnamed namespace - - -// see cocoa/i_joystick.cpp -void I_ProcessJoysticks(); - - -void I_GetEvent() -{ - [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; -} - -void I_StartTic() -{ - CheckGUICapture(); - CheckNativeMouse(); - - I_ProcessJoysticks(); - I_GetEvent(); -} - -void I_StartFrame() -{ - -} - - -void I_SetMouseCapture() -{ - -} - -void I_ReleaseMouseCapture() -{ - -} - - -// --------------------------------------------------------------------------- - - -namespace -{ - -const size_t KEY_COUNT = 128; - - -// See Carbon -> HIToolbox -> Events.h for kVK_ constants - -const uint8_t KEYCODE_TO_DIK[KEY_COUNT] = -{ - DIK_A, DIK_S, DIK_D, DIK_F, DIK_H, DIK_G, DIK_Z, DIK_X, // 0x00 - 0x07 - DIK_C, DIK_V, 0, DIK_B, DIK_Q, DIK_W, DIK_E, DIK_R, // 0x08 - 0x0F - DIK_Y, DIK_T, DIK_1, DIK_2, DIK_3, DIK_4, DIK_6, DIK_5, // 0x10 - 0x17 - DIK_EQUALS, DIK_9, DIK_7, DIK_MINUS, DIK_8, DIK_0, DIK_RBRACKET, DIK_O, // 0x18 - 0x1F - DIK_U, DIK_LBRACKET, DIK_I, DIK_P, DIK_RETURN, DIK_L, DIK_J, DIK_APOSTROPHE, // 0x20 - 0x27 - DIK_K, DIK_SEMICOLON, DIK_BACKSLASH, DIK_COMMA, DIK_SLASH, DIK_N, DIK_M, DIK_PERIOD, // 0x28 - 0x2F - DIK_TAB, DIK_SPACE, DIK_GRAVE, DIK_BACK, 0, DIK_ESCAPE, 0, DIK_LWIN, // 0x30 - 0x37 - DIK_LSHIFT, DIK_CAPITAL, DIK_LMENU, DIK_LCONTROL, DIK_RSHIFT, DIK_RMENU, DIK_RCONTROL, 0, // 0x38 - 0x3F - 0, DIK_DECIMAL, 0, DIK_MULTIPLY, 0, DIK_ADD, 0, 0, // 0x40 - 0x47 - DIK_VOLUMEUP, DIK_VOLUMEDOWN, DIK_MUTE, DIK_SLASH, DIK_NUMPADENTER, 0, DIK_SUBTRACT, 0, // 0x48 - 0x4F - 0, DIK_NUMPAD_EQUALS, DIK_NUMPAD0, DIK_NUMPAD1, DIK_NUMPAD2, DIK_NUMPAD3, DIK_NUMPAD4, DIK_NUMPAD5, // 0x50 - 0x57 - DIK_NUMPAD6, DIK_NUMPAD7, 0, DIK_NUMPAD8, DIK_NUMPAD9, 0, 0, 0, // 0x58 - 0x5F - DIK_F5, DIK_F6, DIK_F7, DIK_F3, DIK_F8, DIK_F9, 0, DIK_F11, // 0x60 - 0x67 - 0, DIK_F13, 0, DIK_F14, 0, DIK_F10, 0, DIK_F12, // 0x68 - 0x6F - 0, DIK_F15, 0, DIK_HOME, 0, DIK_DELETE, DIK_F4, DIK_END, // 0x70 - 0x77 - DIK_F2, 0, DIK_F1, DIK_LEFT, DIK_RIGHT, DIK_DOWN, DIK_UP, 0, // 0x78 - 0x7F -}; - -const uint8_t KEYCODE_TO_ASCII[KEY_COUNT] = -{ - 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', // 0x00 - 0x07 - 'c', 'v', 0, 'b', 'q', 'w', 'e', 'r', // 0x08 - 0x0F - 'y', 't', '1', '2', '3', '4', '6', '5', // 0x10 - 0x17 - '=', '9', '7', '-', '8', '0', ']', 'o', // 0x18 - 0x1F - 'u', '[', 'i', 'p', 13, 'l', 'j', '\'', // 0x20 - 0x27 - 'k', ';', '\\', ',', '/', 'n', 'm', '.', // 0x28 - 0x2F - 9, ' ', '`', 12, 0, 27, 0, 0, // 0x30 - 0x37 - 0, 0, 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F - 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x47 - 0, 0, 0, 0, 0, 0, 0, 0, // 0x48 - 0x4F - 0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x57 - 0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F - 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67 - 0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F - 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77 - 0, 0, 0, 0, 0, 0, 0, 0, // 0x78 - 0x7F -}; - - -uint8_t ModifierToDIK(const uint32_t modifier) -{ - switch (modifier) - { - case NSAlphaShiftKeyMask: return DIK_CAPITAL; - case NSShiftKeyMask: return DIK_LSHIFT; - case NSControlKeyMask: return DIK_LCONTROL; - case NSAlternateKeyMask: return DIK_LMENU; - case NSCommandKeyMask: return DIK_LWIN; - } - - return 0; -} - -SWORD ModifierFlagsToGUIKeyModifiers(NSEvent* theEvent) -{ - const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); - return ((modifiers & NSShiftKeyMask ) ? GKM_SHIFT : 0) - | ((modifiers & NSControlKeyMask ) ? GKM_CTRL : 0) - | ((modifiers & NSAlternateKeyMask) ? GKM_ALT : 0) - | ((modifiers & NSCommandKeyMask ) ? GKM_META : 0); -} - -bool ShouldGenerateGUICharEvent(NSEvent* theEvent) -{ - const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); - return !(modifiers & NSControlKeyMask) - && !(modifiers & NSAlternateKeyMask) - && !(modifiers & NSCommandKeyMask) - && !(modifiers & NSFunctionKeyMask); -} - -void ProcessKeyboardFlagsEvent(NSEvent* theEvent) -{ - static const uint32_t FLAGS_MASK = - NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask; - - const uint32_t modifiers = [theEvent modifierFlags] & FLAGS_MASK; - static uint32_t oldModifiers = 0; - const uint32_t deltaModifiers = modifiers ^ oldModifiers; - - if (0 == deltaModifiers) - { - return; - } - - event_t event = {}; - - event.type = modifiers > oldModifiers ? EV_KeyDown : EV_KeyUp; - event.data1 = ModifierToDIK(deltaModifiers); - - oldModifiers = modifiers; - - // Caps Lock is a modifier key which generates one event per state change - // but not per actual key press or release. So treat any event as key down - // Also its event should be not be posted in menu and console - - if (DIK_CAPITAL == event.data1) - { - if (GUICapture) - { - return; - } - - event.type = EV_KeyDown; - } - - D_PostEvent(&event); -} - -NSStringEncoding GetEncodingForUnicodeCharacter(const unichar character) -{ - if (character >= L'\u0100' && character <= L'\u024F') - { - return NSWindowsCP1250StringEncoding; // Central and Eastern Europe - } - else if (character >= L'\u0370' && character <= L'\u03FF') - { - return NSWindowsCP1253StringEncoding; // Greek - } - else if (character >= L'\u0400' && character <= L'\u04FF') - { - return NSWindowsCP1251StringEncoding; // Cyrillic - } - - // TODO: add handling for other characters - // TODO: Turkish should use NSWindowsCP1254StringEncoding - - return NSWindowsCP1252StringEncoding; -} - -unsigned char GetCharacterFromNSEvent(NSEvent* theEvent) -{ - const NSString* unicodeCharacters = [theEvent characters]; - - if (0 == [unicodeCharacters length]) - { - return '\0'; - } - - const unichar unicodeCharacter = [unicodeCharacters characterAtIndex:0]; - const NSStringEncoding encoding = GetEncodingForUnicodeCharacter(unicodeCharacter); - - unsigned char character = '\0'; - - if (NSWindowsCP1252StringEncoding == encoding) - { - // TODO: make sure that the following is always correct - character = unicodeCharacter & 0xFF; - } - else - { - const NSData* const characters = - [[theEvent characters] dataUsingEncoding:encoding]; - - character = [characters length] > 0 - ? *static_cast([characters bytes]) - : '\0'; - } - - return character; -} - -void ProcessKeyboardEventInMenu(NSEvent* theEvent) -{ - event_t event = {}; - - event.type = EV_GUI_Event; - event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp; - event.data2 = GetCharacterFromNSEvent(theEvent); - event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); - - if (EV_GUI_KeyDown == event.subtype && [theEvent isARepeat]) - { - event.subtype = EV_GUI_KeyRepeat; - } - - const unsigned short keyCode = [theEvent keyCode]; - - switch (keyCode) - { - case kVK_Return: event.data1 = GK_RETURN; break; - case kVK_PageUp: event.data1 = GK_PGUP; break; - case kVK_PageDown: event.data1 = GK_PGDN; break; - case kVK_End: event.data1 = GK_END; break; - case kVK_Home: event.data1 = GK_HOME; break; - case kVK_LeftArrow: event.data1 = GK_LEFT; break; - case kVK_RightArrow: event.data1 = GK_RIGHT; break; - case kVK_UpArrow: event.data1 = GK_UP; break; - case kVK_DownArrow: event.data1 = GK_DOWN; break; - case kVK_Delete: event.data1 = GK_BACKSPACE; break; - case kVK_ForwardDelete: event.data1 = GK_DEL; break; - case kVK_Escape: event.data1 = GK_ESCAPE; break; - case kVK_F1: event.data1 = GK_F1; break; - case kVK_F2: event.data1 = GK_F2; break; - case kVK_F3: event.data1 = GK_F3; break; - case kVK_F4: event.data1 = GK_F4; break; - case kVK_F5: event.data1 = GK_F5; break; - case kVK_F6: event.data1 = GK_F6; break; - case kVK_F7: event.data1 = GK_F7; break; - case kVK_F8: event.data1 = GK_F8; break; - case kVK_F9: event.data1 = GK_F9; break; - case kVK_F10: event.data1 = GK_F10; break; - case kVK_F11: event.data1 = GK_F11; break; - case kVK_F12: event.data1 = GK_F12; break; - default: - event.data1 = KEYCODE_TO_ASCII[keyCode]; - break; - } - - if (event.data1 < 128) - { - event.data1 = toupper(event.data1); - - D_PostEvent(&event); - } - - if (!iscntrl(event.data2) - && EV_GUI_KeyUp != event.subtype - && ShouldGenerateGUICharEvent(theEvent)) - { - event.subtype = EV_GUI_Char; - event.data1 = event.data2; - event.data2 = event.data3 & GKM_ALT; - - D_PostEvent(&event); - } -} - -void ProcessKeyboardEvent(NSEvent* theEvent) -{ - const unsigned short keyCode = [theEvent keyCode]; - if (keyCode >= KEY_COUNT) - { - assert(!"Unknown keycode"); - return; - } - - if (GUICapture) - { - ProcessKeyboardEventInMenu(theEvent); - } - else - { - event_t event = {}; - - event.type = NSKeyDown == [theEvent type] ? EV_KeyDown : EV_KeyUp; - event.data1 = KEYCODE_TO_DIK[ keyCode ]; - - if (0 != event.data1) - { - event.data2 = KEYCODE_TO_ASCII[ keyCode ]; - - D_PostEvent(&event); - } - } -} - - -bool IsHiDPISupported() -{ - // The following value shoud be equal to NSAppKitVersionNumber10_7 - // and it's hard-coded in order to build on earlier SDKs - return NSAppKitVersionNumber >= 1138; -} - -NSSize GetRealContentViewSize(const NSWindow* const window) -{ - const NSView* view = [window contentView]; - const NSSize frameSize = [view frame].size; - - // TODO: figure out why [NSView frame] returns different values in "fullscreen" and in window - // In "fullscreen" the result is multiplied by [NSScreen backingScaleFactor], but not in window - - return (vid_hidpi && !fullscreen) - ? [view convertSizeToBacking:frameSize] - : frameSize; -} - - -void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) -{ - const NSWindow* window = [inEvent window]; - const NSView* view = [window contentView]; - - const NSPoint screenPos = [NSEvent mouseLocation]; - const NSPoint windowPos = [window convertScreenToBase:screenPos]; - - const NSPoint viewPos = IsHiDPISupported() - ? [view convertPointToBacking:windowPos] - : [view convertPoint:windowPos fromView:nil]; - - const CGFloat frameHeight = GetRealContentViewSize(window).height; - - const CGFloat posX = ( viewPos.x - rbOpts.shiftX) / rbOpts.pixelScale; - const CGFloat posY = (frameHeight - viewPos.y - rbOpts.shiftY) / rbOpts.pixelScale; - - outEvent->data1 = static_cast< int >(posX); - outEvent->data2 = static_cast< int >(posY); -} - -void ProcessMouseButtonEvent(NSEvent* theEvent) -{ - event_t event = {}; - - const NSEventType cocoaEventType = [theEvent type]; - - if (GUICapture) - { - event.type = EV_GUI_Event; - - switch (cocoaEventType) - { - case NSLeftMouseDown: event.subtype = EV_GUI_LButtonDown; break; - case NSRightMouseDown: event.subtype = EV_GUI_RButtonDown; break; - case NSOtherMouseDown: event.subtype = EV_GUI_MButtonDown; break; - case NSLeftMouseUp: event.subtype = EV_GUI_LButtonUp; break; - case NSRightMouseUp: event.subtype = EV_GUI_RButtonUp; break; - case NSOtherMouseUp: event.subtype = EV_GUI_MButtonUp; break; - default: break; - } - - NSEventToGameMousePosition(theEvent, &event); - - D_PostEvent(&event); - } - else - { - switch (cocoaEventType) - { - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: - event.type = EV_KeyDown; - break; - - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: - event.type = EV_KeyUp; - break; - - default: - break; - } - - event.data1 = std::min(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8)); - - D_PostEvent(&event); - } -} - - -void ProcessMouseMoveInMenu(NSEvent* theEvent) -{ - event_t event = {}; - - event.type = EV_GUI_Event; - event.subtype = EV_GUI_MouseMove; - - NSEventToGameMousePosition(theEvent, &event); - - D_PostEvent(&event); -} - -void ProcessMouseMoveInGame(NSEvent* theEvent) -{ - if (!use_mouse) - { - return; - } - - // TODO: remove this magic! - - if (s_skipMouseMoves > 0) - { - --s_skipMouseMoves; - return; - } - - int x([theEvent deltaX]); - int y(-[theEvent deltaY]); - - if (0 == x && 0 == y) - { - return; - } - - if (!m_noprescale) - { - x *= 3; - y *= 2; - } - - event_t event = {}; - - static int lastX = 0, lastY = 0; - - if (m_filter) - { - event.x = (x + lastX) / 2; - event.y = (y + lastY) / 2; - } - else - { - event.x = x; - event.y = y; - } - - lastX = x; - lastY = y; - - if (0 != event.x | 0 != event.y) - { - event.type = EV_Mouse; - - D_PostEvent(&event); - } -} - -void ProcessMouseMoveEvent(NSEvent* theEvent) -{ - if (GUICapture) - { - ProcessMouseMoveInMenu(theEvent); - } - else - { - ProcessMouseMoveInGame(theEvent); - } -} - - -void ProcessMouseWheelEvent(NSEvent* theEvent) -{ - const CGFloat delta = [theEvent deltaY]; - const bool isZeroDelta = fabs(delta) < 1.0E-5; - - if (isZeroDelta && GUICapture) - { - return; - } - - event_t event = {}; - - if (GUICapture) - { - event.type = EV_GUI_Event; - event.subtype = delta > 0.0f ? EV_GUI_WheelUp : EV_GUI_WheelDown; - event.data3 = delta; - event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); - } - else - { - event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown; - event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN; - } - - D_PostEvent(&event); -} - - -const size_t BYTES_PER_PIXEL = 4; - -} // unnamed namespace - - -// --------------------------------------------------------------------------- - - -namespace -{ - const NSInteger LEVEL_FULLSCREEN = NSMainMenuWindowLevel + 1; - const NSInteger LEVEL_WINDOWED = NSNormalWindowLevel; - - const NSUInteger STYLE_MASK_FULLSCREEN = NSBorderlessWindowMask; - const NSUInteger STYLE_MASK_WINDOWED = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; -} - - -// --------------------------------------------------------------------------- - - -@interface CocoaWindow : NSWindow -{ -} - -- (BOOL)canBecomeKeyWindow; - -@end - - -@implementation CocoaWindow - -- (BOOL)canBecomeKeyWindow -{ - return true; -} - -@end - - -// --------------------------------------------------------------------------- - - -@interface CocoaView : NSOpenGLView -{ -} - -- (void)resetCursorRects; - -@end - - -@implementation CocoaView - -- (void)resetCursorRects -{ - [super resetCursorRects]; - [self addCursorRect: [self bounds] - cursor: s_cursor]; -} - -@end - - -// --------------------------------------------------------------------------- - - -@interface ApplicationController : NSResponder -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - -#endif -{ -} - -- (void)keyDown:(NSEvent*)theEvent; -- (void)keyUp:(NSEvent*)theEvent; - -- (void)applicationDidBecomeActive:(NSNotification*)aNotification; -- (void)applicationWillResignActive:(NSNotification*)aNotification; - -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification; - -- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; - -- (void)processEvents:(NSTimer*)timer; - -@end - - -static ApplicationController* appCtrl; - - -@implementation ApplicationController - -- (void)keyDown:(NSEvent*)theEvent -{ - // Empty but present to avoid playing of 'beep' alert sound - - ZD_UNUSED(theEvent); -} - -- (void)keyUp:(NSEvent*)theEvent -{ - // Empty but present to avoid playing of 'beep' alert sound - - ZD_UNUSED(theEvent); -} - - -- (void)applicationDidBecomeActive:(NSNotification*)aNotification -{ - ZD_UNUSED(aNotification); - - S_SetSoundPaused(1); -} - -- (void)applicationWillResignActive:(NSNotification*)aNotification -{ - ZD_UNUSED(aNotification); - - S_SetSoundPaused(0); -} - - -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification -{ - // When starting from command line with real executable path, e.g. ZDoom.app/Contents/MacOS/ZDoom - // application remains deactivated for an unknown reason. - // The following call resolves this issue - [NSApp activateIgnoringOtherApps:YES]; - - // Setup timer for custom event loop - - NSTimer* timer = [NSTimer timerWithTimeInterval:0 - target:self - selector:@selector(processEvents:) - userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:timer - forMode:NSDefaultRunLoopMode]; - - exit(OriginalMain(s_argc, s_argv)); -} - - -- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename -{ - ZD_UNUSED(theApplication); - - if (s_restartedFromWADPicker - || 0 == [filename length] - || s_argc + 2 >= ARGC_MAX) - { - return FALSE; - } - - // Some parameters from command line are passed to this function - // These parameters need to be skipped to avoid duplication - // Note: SDL has different approach to fix this issue, see the same method in SDLMain.m - - const char* const charFileName = [filename UTF8String]; - - for (int i = 0; i < s_argc; ++i) - { - if (0 == strcmp(s_argv[i], charFileName)) - { - return FALSE; - } - } - - s_argvStorage.Push("-file"); - s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); - - s_argvStorage.Push([filename UTF8String]); - s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); - - return TRUE; -} - - -- (void)processEvents:(NSTimer*)timer -{ - ZD_UNUSED(timer); - - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - while (true) - { - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (nil == event) - { - break; - } - - const NSEventType eventType = [event type]; - - switch (eventType) - { - case NSMouseMoved: - ProcessMouseMoveEvent(event); - break; - - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: - case NSOtherMouseDown: - case NSOtherMouseUp: - ProcessMouseButtonEvent(event); - break; - - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - ProcessMouseButtonEvent(event); - ProcessMouseMoveEvent(event); - break; - - case NSScrollWheel: - ProcessMouseWheelEvent(event); - break; - - case NSKeyDown: - case NSKeyUp: - ProcessKeyboardEvent(event); - break; - - case NSFlagsChanged: - ProcessKeyboardFlagsEvent(event); - break; - - default: - break; - } - - [NSApp sendEvent:event]; - } - - [NSApp updateWindows]; - - [pool release]; -} - -@end - - -// --------------------------------------------------------------------------- - - -class CocoaVideo : public IVideo -{ -public: - explicit CocoaVideo(int multisample); - ~CocoaVideo(); - - virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } - virtual void SetWindowedScale(float scale); - - virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); - - virtual void StartModeIterator(int bits, bool fullscreen); - virtual bool NextMode(int* width, int* height, bool* letterbox); - - static bool IsFullscreen(); - static void UseHiDPI(bool hiDPI); - static void InvalidateCursorRects(); - static void SetWindowVisible(bool visible); - -private: - struct ModeIterator - { - size_t index; - int bits; - bool fullscreen; - }; - - ModeIterator m_modeIterator; - - CocoaWindow* m_window; - - int m_width; - int m_height; - bool m_fullscreen; - bool m_hiDPI; - - void SetStyleMask(NSUInteger styleMask); - void SetFullscreenMode(int width, int height); - void SetWindowedMode(int width, int height); - void SetMode(int width, int height, bool fullscreen, bool hiDPI); - - static CocoaVideo* GetInstance(); -}; - - -class CocoaFrameBuffer : public DFrameBuffer -{ -public: - CocoaFrameBuffer(int width, int height, bool fullscreen); - ~CocoaFrameBuffer(); - - virtual bool Lock(bool buffer); - virtual void Unlock(); - virtual void Update(); - - virtual PalEntry* GetPalette(); - virtual void GetFlashedPalette(PalEntry pal[256]); - virtual void UpdatePalette(); - - virtual bool SetGamma(float gamma); - virtual bool SetFlash(PalEntry rgb, int amount); - virtual void GetFlash(PalEntry &rgb, int &amount); - - virtual int GetPageCount(); - - virtual bool IsFullscreen(); - - virtual void SetVSync(bool vsync); - -private: - PalEntry m_palette[256]; - bool m_needPaletteUpdate; - - BYTE m_gammaTable[3][256]; - float m_gamma; - bool m_needGammaUpdate; - - PalEntry m_flashColor; - int m_flashAmount; - - bool m_isUpdatePending; - - uint8_t* m_pixelBuffer; - GLuint m_texture; - - void Flip(); - - void UpdateColors(); -}; - - -// --------------------------------------------------------------------------- - - -EXTERN_CVAR(Float, Gamma) - -CUSTOM_CVAR(Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (NULL != screen) - { - screen->SetGamma(Gamma); - } -} - -CUSTOM_CVAR(Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (NULL != screen) - { - screen->SetGamma(Gamma); - } -} - -CUSTOM_CVAR(Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (NULL != screen) - { - screen->SetGamma(Gamma); - } -} - - -// --------------------------------------------------------------------------- - - -namespace -{ - -const struct -{ - uint16_t width; - uint16_t height; -} -VideoModes[] = -{ - { 320, 200 }, - { 320, 240 }, - { 400, 225 }, // 16:9 - { 400, 300 }, - { 480, 270 }, // 16:9 - { 480, 360 }, - { 512, 288 }, // 16:9 - { 512, 384 }, - { 640, 360 }, // 16:9 - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, // 16:10 - { 720, 540 }, - { 800, 450 }, // 16:9 - { 800, 480 }, - { 800, 500 }, // 16:10 - { 800, 600 }, - { 848, 480 }, // 16:9 - { 960, 600 }, // 16:10 - { 960, 720 }, - { 1024, 576 }, // 16:9 - { 1024, 600 }, // 17:10 - { 1024, 640 }, // 16:10 - { 1024, 768 }, - { 1088, 612 }, // 16:9 - { 1152, 648 }, // 16:9 - { 1152, 720 }, // 16:10 - { 1152, 864 }, - { 1280, 720 }, // 16:9 - { 1280, 854 }, - { 1280, 800 }, // 16:10 - { 1280, 960 }, - { 1280, 1024 }, // 5:4 - { 1360, 768 }, // 16:9 - { 1366, 768 }, - { 1400, 787 }, // 16:9 - { 1400, 875 }, // 16:10 - { 1400, 1050 }, - { 1440, 900 }, - { 1440, 960 }, - { 1440, 1080 }, - { 1600, 900 }, // 16:9 - { 1600, 1000 }, // 16:10 - { 1600, 1200 }, - { 1920, 1080 }, - { 1920, 1200 }, - { 2048, 1536 }, - { 2560, 1440 }, - { 2560, 1600 }, - { 2560, 2048 }, - { 2880, 1800 }, - { 3200, 1800 }, - { 3840, 2160 }, - { 3840, 2400 }, - { 4096, 2160 }, - { 5120, 2880 } -}; - - -cycle_t BlitCycles; -cycle_t FlipCycles; - - -CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) -{ - CocoaWindow* window = [[CocoaWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; - [window setOpaque:YES]; - [window makeFirstResponder:appCtrl]; - [window setAcceptsMouseMovedEvents:YES]; - - return window; -} - -} // unnamed namespace - - -// --------------------------------------------------------------------------- - - -CocoaVideo::CocoaVideo(const int multisample) -: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) -, m_width(-1) -, m_height(-1) -, m_fullscreen(false) -, m_hiDPI(false) -{ - memset(&m_modeIterator, 0, sizeof m_modeIterator); - - // Set attributes for OpenGL context - - NSOpenGLPixelFormatAttribute attributes[16]; - size_t i = 0; - - attributes[i++] = NSOpenGLPFADoubleBuffer; - attributes[i++] = NSOpenGLPFAColorSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(32); - attributes[i++] = NSOpenGLPFADepthSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(24); - attributes[i++] = NSOpenGLPFAStencilSize; - attributes[i++] = NSOpenGLPixelFormatAttribute(8); - - if (multisample) - { - attributes[i++] = NSOpenGLPFAMultisample; - attributes[i++] = NSOpenGLPFASampleBuffers; - attributes[i++] = NSOpenGLPixelFormatAttribute(1); - attributes[i++] = NSOpenGLPFASamples; - attributes[i++] = NSOpenGLPixelFormatAttribute(multisample); - } - - attributes[i] = NSOpenGLPixelFormatAttribute(0); - - // Create OpenGL context and view - - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - - const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; - NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect - pixelFormat:pixelFormat]; - [[glView openGLContext] makeCurrentContext]; - - [m_window setContentView:glView]; -} - -CocoaVideo::~CocoaVideo() -{ - [m_window release]; -} - -void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) -{ - m_modeIterator.index = 0; - m_modeIterator.bits = bits; - m_modeIterator.fullscreen = fullscreen; -} - -bool CocoaVideo::NextMode(int* const width, int* const height, bool* const letterbox) -{ - assert(NULL != width); - assert(NULL != height); - - const int bits = m_modeIterator.bits; - - if (8 != bits && 16 != bits && 24 != bits && 32 != bits) - { - return false; - } - - size_t& index = m_modeIterator.index; - - if (index < sizeof(VideoModes) / sizeof(VideoModes[0])) - { - *width = VideoModes[index].width; - *height = VideoModes[index].height; - - if (m_modeIterator.fullscreen && NULL != letterbox) - { - const NSSize screenSize = [[m_window screen] frame].size; - const float screenRatio = screenSize.width / screenSize.height; - const float modeRatio = float(*width) / *height; - - *letterbox = fabs(screenRatio - modeRatio) > 0.001f; - } - - ++index; - - return true; - } - - return false; -} - -DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool fullscreen, DFrameBuffer* const old) -{ - PalEntry flashColor = 0; - int flashAmount = 0; - - if (NULL != old) - { - if (width == m_width && height == m_height) - { - SetMode(width, height, fullscreen, vid_hidpi); - return old; - } - - old->GetFlash(flashColor, flashAmount); - old->ObjectFlags |= OF_YesReallyDelete; - - if (old == screen) - { - screen = NULL; - } - - delete old; - } - - CocoaFrameBuffer* fb = new CocoaFrameBuffer(width, height, fullscreen); - fb->SetFlash(flashColor, flashAmount); - - SetMode(width, height, fullscreen, vid_hidpi); - - return fb; -} - -void CocoaVideo::SetWindowedScale(float scale) -{ -} - - -bool CocoaVideo::IsFullscreen() -{ - CocoaVideo* const video = GetInstance(); - return NULL == video - ? false - : video->m_fullscreen; -} - -void CocoaVideo::UseHiDPI(const bool hiDPI) -{ - if (CocoaVideo* const video = GetInstance()) - { - video->SetMode(video->m_width, video->m_height, video->m_fullscreen, hiDPI); - } -} - -void CocoaVideo::InvalidateCursorRects() -{ - if (CocoaVideo* const video = GetInstance()) - { - [video->m_window invalidateCursorRectsForView:[video->m_window contentView]]; - } -} - -void CocoaVideo::SetWindowVisible(bool visible) -{ - if (CocoaVideo* const video = GetInstance()) - { - if (visible) - { - [video->m_window orderFront:nil]; - } - else - { - [video->m_window orderOut:nil]; - } - } -} - - -static bool HasModernFullscreenAPI() -{ - // The following value shoud be equal to NSAppKitVersionNumber10_6 - // and it's hard-coded in order to build on earlier SDKs - - return NSAppKitVersionNumber >= 1038; -} - -void CocoaVideo::SetStyleMask(const NSUInteger styleMask) -{ - // Before 10.6 it's impossible to change window's style mask - // To workaround this new window should be created with required style mask - // This method should not be called when running on Snow Leopard or newer - - assert(!HasModernFullscreenAPI()); - - CocoaWindow* tempWindow = CreateCocoaWindow(styleMask); - [tempWindow setContentView:[m_window contentView]]; - - [m_window close]; - m_window = tempWindow; -} - -void CocoaVideo::SetFullscreenMode(const int width, const int height) -{ - NSScreen* screen = [m_window screen]; - - const NSRect screenFrame = [screen frame]; - const NSRect displayRect = vid_hidpi - ? [screen convertRectToBacking:screenFrame] - : screenFrame; - - const float displayWidth = displayRect.size.width; - const float displayHeight = displayRect.size.height; - - const float pixelScaleFactorX = displayWidth / static_cast(width ); - const float pixelScaleFactorY = displayHeight / static_cast(height); - - rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - - rbOpts.width = width * rbOpts.pixelScale; - rbOpts.height = height * rbOpts.pixelScale; - - rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; - rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; - - if (!m_fullscreen) - { - if (HasModernFullscreenAPI()) - { - [m_window setLevel:LEVEL_FULLSCREEN]; - [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; - } - else - { - // Old Carbon-based way to make fullscreen window above dock and menu - // It's supported on 64-bit, but on 10.6 and later the following is preferred: - // [NSWindow setLevel:NSMainMenuWindowLevel + 1] - - SetSystemUIMode(kUIModeAllHidden, 0); - SetStyleMask(STYLE_MASK_FULLSCREEN); - } - - [m_window setHidesOnDeactivate:YES]; - } - - [m_window setFrame:displayRect display:YES]; - [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; -} - -void CocoaVideo::SetWindowedMode(const int width, const int height) -{ - rbOpts.pixelScale = 1.0f; - - rbOpts.width = static_cast(width ); - rbOpts.height = static_cast(height); - - rbOpts.shiftX = 0.0f; - rbOpts.shiftY = 0.0f; - - const NSSize windowPixelSize = NSMakeSize(width, height); - const NSSize windowSize = vid_hidpi - ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] - : windowPixelSize; - - if (m_fullscreen) - { - if (HasModernFullscreenAPI()) - { - [m_window setLevel:LEVEL_WINDOWED]; - [m_window setStyleMask:STYLE_MASK_WINDOWED]; - } - else - { - SetSystemUIMode(kUIModeNormal, 0); - SetStyleMask(STYLE_MASK_WINDOWED); - } - - [m_window setHidesOnDeactivate:NO]; - } - - [m_window setContentSize:windowSize]; - [m_window center]; - - NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; - [closeButton setAction:@selector(terminate:)]; - [closeButton setTarget:NSApp]; -} - -void CocoaVideo::SetMode(const int width, const int height, const bool fullscreen, const bool hiDPI) -{ - if (fullscreen == m_fullscreen - && width == m_width - && height == m_height - && hiDPI == m_hiDPI) - { - return; - } - - if (IsHiDPISupported()) - { - NSOpenGLView* const glView = [m_window contentView]; - [glView setWantsBestResolutionOpenGLSurface:hiDPI]; - } - - if (fullscreen) - { - SetFullscreenMode(width, height); - } - else - { - SetWindowedMode(width, height); - } - - rbOpts.dirty = true; - - const NSSize viewSize = GetRealContentViewSize(m_window); - - glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - CGLFlushDrawable(CGLGetCurrentContext()); - - static NSString* const TITLE_STRING = - [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; - [m_window setTitle:TITLE_STRING]; - - if (![m_window isKeyWindow]) - { - [m_window makeKeyAndOrderFront:nil]; - } - - m_fullscreen = fullscreen; - m_width = width; - m_height = height; - m_hiDPI = hiDPI; -} - - -CocoaVideo* CocoaVideo::GetInstance() -{ - return static_cast(Video); -} - - -CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) -: DFrameBuffer(width, height) -, m_needPaletteUpdate(false) -, m_gamma(0.0f) -, m_needGammaUpdate(false) -, m_flashAmount(0) -, m_isUpdatePending(false) -, m_pixelBuffer(new uint8_t[width * height * BYTES_PER_PIXEL]) -, m_texture(0) -{ - glEnable(GL_TEXTURE_RECTANGLE_ARB); - - glGenTextures(1, &m_texture); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_texture); - glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); - - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, width, height, 0.0, -1.0, 1.0); - - GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); - - for (size_t i = 0; i < 256; ++i) - { - m_gammaTable[0][i] = m_gammaTable[1][i] = m_gammaTable[2][i] = i; - } - - memcpy(m_palette, GPalette.BaseColors, sizeof(PalEntry) * 256); - UpdateColors(); - - SetVSync(vid_vsync); -} - - -CocoaFrameBuffer::~CocoaFrameBuffer() -{ - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &m_texture); - - delete[] m_pixelBuffer; -} - -int CocoaFrameBuffer::GetPageCount() -{ - return 1; -} - -bool CocoaFrameBuffer::Lock(bool buffered) -{ - return DSimpleCanvas::Lock(buffered); -} - -void CocoaFrameBuffer::Unlock() -{ - if (m_isUpdatePending && LockCount == 1) - { - Update(); - } - else if (--LockCount <= 0) - { - Buffer = NULL; - LockCount = 0; - } -} - -void CocoaFrameBuffer::Update() -{ - if (LockCount != 1) - { - if (LockCount > 0) - { - m_isUpdatePending = true; - --LockCount; - } - return; - } - - DrawRateStuff(); - - Buffer = NULL; - LockCount = 0; - m_isUpdatePending = false; - - BlitCycles.Reset(); - FlipCycles.Reset(); - BlitCycles.Clock(); - - GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, - Width, Height, FRACUNIT, FRACUNIT, 0, 0); - - FlipCycles.Clock(); - Flip(); - FlipCycles.Unclock(); - - BlitCycles.Unclock(); - - if (m_needGammaUpdate) - { - CalcGamma(rgamma == 0.0f ? m_gamma : m_gamma * rgamma, m_gammaTable[0]); - CalcGamma(ggamma == 0.0f ? m_gamma : m_gamma * ggamma, m_gammaTable[1]); - CalcGamma(bgamma == 0.0f ? m_gamma : m_gamma * bgamma, m_gammaTable[2]); - - m_needGammaUpdate = false; - m_needPaletteUpdate = true; - } - - if (m_needPaletteUpdate) - { - m_needPaletteUpdate = false; - UpdateColors(); - } -} - -void CocoaFrameBuffer::UpdateColors() -{ - PalEntry palette[256]; - - for (size_t i = 0; i < 256; ++i) - { - palette[i].r = m_gammaTable[0][m_palette[i].r]; - palette[i].g = m_gammaTable[1][m_palette[i].g]; - palette[i].b = m_gammaTable[2][m_palette[i].b]; - } - - if (0 != m_flashAmount) - { - DoBlending(palette, palette, 256, - m_gammaTable[0][m_flashColor.r], - m_gammaTable[1][m_flashColor.g], - m_gammaTable[2][m_flashColor.b], - m_flashAmount); - } - - GPfx.SetPalette(palette); -} - -PalEntry* CocoaFrameBuffer::GetPalette() -{ - return m_palette; -} - -void CocoaFrameBuffer::UpdatePalette() -{ - m_needPaletteUpdate = true; -} - -bool CocoaFrameBuffer::SetGamma(float gamma) -{ - m_gamma = gamma; - m_needGammaUpdate = true; - - return true; -} - -bool CocoaFrameBuffer::SetFlash(PalEntry rgb, int amount) -{ - m_flashColor = rgb; - m_flashAmount = amount; - m_needPaletteUpdate = true; - - return true; -} - -void CocoaFrameBuffer::GetFlash(PalEntry &rgb, int &amount) -{ - rgb = m_flashColor; - amount = m_flashAmount; -} - -void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) -{ - memcpy(pal, m_palette, sizeof m_palette); - - if (0 != m_flashAmount) - { - DoBlending(pal, pal, 256, - m_flashColor.r, m_flashColor.g, m_flashColor.b, - m_flashAmount); - } -} - -bool CocoaFrameBuffer::IsFullscreen() -{ - return CocoaVideo::IsFullscreen(); -} - -void CocoaFrameBuffer::SetVSync(bool vsync) -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - const long value = vsync ? 1 : 0; -#else // 10.5 or newer - const GLint value = vsync ? 1 : 0; -#endif // prior to 10.5 - - [[NSOpenGLContext currentContext] setValues:&value - forParameter:NSOpenGLCPSwapInterval]; -} - -void CocoaFrameBuffer::Flip() -{ - assert(NULL != screen); - - if (rbOpts.dirty) - { - glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); - - // TODO: Figure out why the following glClear() call is needed - // to avoid drawing of garbage in fullscreen mode when - // in-game's aspect ratio is different from display one - glClear(GL_COLOR_BUFFER_BIT); - - rbOpts.dirty = false; - } - -#ifdef __LITTLE_ENDIAN__ - static const GLenum format = GL_RGBA; -#else // __BIG_ENDIAN__ - static const GLenum format = GL_ABGR_EXT; -#endif // __LITTLE_ENDIAN__ - - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); - - glBegin(GL_QUADS); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glTexCoord2f(0.0f, 0.0f); - glVertex2f(0.0f, 0.0f); - glTexCoord2f(Width, 0.0f); - glVertex2f(Width, 0.0f); - glTexCoord2f(Width, Height); - glVertex2f(Width, Height); - glTexCoord2f(0.0f, Height); - glVertex2f(0.0f, Height); - glEnd(); - - glFlush(); - - [[NSOpenGLContext currentContext] flushBuffer]; -} - - -ADD_STAT(blit) -{ - FString result; - result.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); - return result; -} - - -IVideo* Video; - - -void I_ShutdownGraphics() -{ - if (NULL != screen) - { - screen->ObjectFlags |= OF_YesReallyDelete; - delete screen; - screen = NULL; - } - - delete Video; - Video = NULL; -} - -void I_InitGraphics() -{ - UCVarValue val; - - val.Bool = !!Args->CheckParm("-devparm"); - ticker.SetGenericRepDefault(val, CVAR_Bool); - - Video = new CocoaVideo(0); - atterm(I_ShutdownGraphics); -} - - -static void I_DeleteRenderer() -{ - delete Renderer; - Renderer = NULL; -} - -void I_CreateRenderer() -{ - if (NULL == Renderer) - { - Renderer = new FSoftwareRenderer; - atterm(I_DeleteRenderer); - } -} - - -DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) -{ - return Video->CreateFrameBuffer(width, height, fullscreen, old); -} - -bool I_CheckResolution(const int width, const int height, const int bits) -{ - int twidth, theight; - - Video->StartModeIterator(bits, fullscreen); - - while (Video->NextMode(&twidth, &theight, NULL)) - { - if (width == twidth && height == theight) - { - return true; - } - } - - return false; -} - -void I_ClosestResolution(int *width, int *height, int bits) -{ - int twidth, theight; - int cwidth = 0, cheight = 0; - int iteration; - DWORD closest = DWORD(-1); - - for (iteration = 0; iteration < 2; ++iteration) - { - Video->StartModeIterator(bits, fullscreen); - - while (Video->NextMode(&twidth, &theight, NULL)) - { - if (twidth == *width && theight == *height) - { - return; - } - - if (iteration == 0 && (twidth < *width || theight < *height)) - { - continue; - } - - const DWORD dist = (twidth - *width) * (twidth - *width) - + (theight - *height) * (theight - *height); - - if (dist < closest) - { - closest = dist; - cwidth = twidth; - cheight = theight; - } - } - - if (closest != DWORD(-1)) - { - *width = cwidth; - *height = cheight; - return; - } - } -} - - -EXTERN_CVAR(Int, vid_maxfps); -EXTERN_CVAR(Bool, cl_capfps); - -// So Apple doesn't support POSIX timers and I can't find a good substitute short of -// having Objective-C Cocoa events or something like that. -void I_SetFPSLimit(int limit) -{ -} - -CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (vid_maxfps < TICRATE && vid_maxfps != 0) - { - vid_maxfps = TICRATE; - } - else if (vid_maxfps > 1000) - { - vid_maxfps = 1000; - } - else if (cl_capfps == 0) - { - I_SetFPSLimit(vid_maxfps); - } -} - -CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (IsHiDPISupported()) - { - CocoaVideo::UseHiDPI(self); - } - else if (0 != self) - { - self = 0; - } -} - - -CCMD(vid_listmodes) -{ - if (Video == NULL) - { - return; - } - - static const char* const ratios[5] = { "", " - 16:9", " - 16:10", " - 17:10", " - 5:4" }; - int width, height; - bool letterbox; - - Video->StartModeIterator(32, screen->IsFullscreen()); - - while (Video->NextMode(&width, &height, &letterbox)) - { - const bool current = width == DisplayWidth && height == DisplayHeight; - const int ratio = CheckRatio(width, height); - - Printf(current ? PRINT_BOLD : PRINT_HIGH, "%s%4d x%5d x%3d%s%s\n", - current || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, - width, height, 32, ratios[ratio], - current || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); - } -} - -CCMD(vid_currentmode) -{ - Printf("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); -} - - -// --------------------------------------------------------------------------- - - -bool I_SetCursor(FTexture* cursorpic) -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) - { - s_cursor = [NSCursor arrowCursor]; - } - else - { - // Create bitmap image representation - - const NSInteger imageWidth = cursorpic->GetWidth(); - const NSInteger imageHeight = cursorpic->GetHeight(); - const NSInteger imagePitch = imageWidth * 4; - - NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; - [bitmapImageRep initWithBitmapDataPlanes:NULL - pixelsWide:imageWidth - pixelsHigh:imageHeight - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:imagePitch - bitsPerPixel:0]; - - // Load bitmap data to representation - - BYTE* buffer = [bitmapImageRep bitmapData]; - memset(buffer, 0, imagePitch * imageHeight); - - FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); - cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); - - // Swap red and blue components in each pixel - - for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) - { - const size_t offset = i * 4; - - const BYTE temp = buffer[offset ]; - buffer[offset ] = buffer[offset + 2]; - buffer[offset + 2] = temp; - } - - // Create image from representation and set it as cursor - - NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType - properties:nil]; - NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; - - s_cursor = [[NSCursor alloc] initWithImage:cursorImage - hotSpot:NSMakePoint(0.0f, 0.0f)]; - } - - CocoaVideo::InvalidateCursorRects(); - - [pool release]; - - return true; -} - - -void I_SetMainWindowVisible(bool visible) -{ - CocoaVideo::SetWindowVisible(visible); - SetNativeMouse(!visible); -} - - -// --------------------------------------------------------------------------- - - -namespace -{ - -NSMenuItem* CreateApplicationMenu() -{ - NSMenu* menu = [NSMenu new]; - - [menu addItemWithTitle:[@"About " stringByAppendingString:@GAMENAME] - action:@selector(orderFrontStandardAboutPanel:) - keyEquivalent:@""]; - [menu addItem:[NSMenuItem separatorItem]]; - [menu addItemWithTitle:[@"Hide " stringByAppendingString:@GAMENAME] - action:@selector(hide:) - keyEquivalent:@"h"]; - [[menu addItemWithTitle:@"Hide Others" - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"] - setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; - [menu addItemWithTitle:@"Show All" - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - [menu addItem:[NSMenuItem separatorItem]]; - [menu addItemWithTitle:[@"Quit " stringByAppendingString:@GAMENAME] - action:@selector(terminate:) - keyEquivalent:@"q"]; - - NSMenuItem* menuItem = [NSMenuItem new]; - [menuItem setSubmenu:menu]; - - if ([NSApp respondsToSelector:@selector(setAppleMenu:)]) - { - [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; - } - - return menuItem; -} - -NSMenuItem* CreateEditMenu() -{ - NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Edit"]; - - [menu addItemWithTitle:@"Undo" - action:@selector(undo:) - keyEquivalent:@"z"]; - [menu addItemWithTitle:@"Redo" - action:@selector(redo:) - keyEquivalent:@"Z"]; - [menu addItem:[NSMenuItem separatorItem]]; - [menu addItemWithTitle:@"Cut" - action:@selector(cut:) - keyEquivalent:@"x"]; - [menu addItemWithTitle:@"Copy" - action:@selector(copy:) - keyEquivalent:@"c"]; - [menu addItemWithTitle:@"Paste" - action:@selector(paste:) - keyEquivalent:@"v"]; - [menu addItemWithTitle:@"Delete" - action:@selector(delete:) - keyEquivalent:@""]; - [menu addItemWithTitle:@"Select All" - action:@selector(selectAll:) - keyEquivalent:@"a"]; - - NSMenuItem* menuItem = [NSMenuItem new]; - [menuItem setSubmenu:menu]; - - return menuItem; -} - -NSMenuItem* CreateWindowMenu() -{ - NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Window"]; - [NSApp setWindowsMenu:menu]; - - [menu addItemWithTitle:@"Minimize" - action:@selector(performMiniaturize:) - keyEquivalent:@"m"]; - [menu addItemWithTitle:@"Zoom" - action:@selector(performZoom:) - keyEquivalent:@""]; - [menu addItem:[NSMenuItem separatorItem]]; - [menu addItemWithTitle:@"Bring All to Front" - action:@selector(arrangeInFront:) - keyEquivalent:@""]; - - NSMenuItem* menuItem = [NSMenuItem new]; - [menuItem setSubmenu:menu]; - - return menuItem; -} - -void CreateMenu() -{ - NSMenu* menuBar = [NSMenu new]; - [menuBar addItem:CreateApplicationMenu()]; - [menuBar addItem:CreateEditMenu()]; - [menuBar addItem:CreateWindowMenu()]; - - [NSApp setMainMenu:menuBar]; -} - -DarwinVersion GetDarwinVersion() -{ - DarwinVersion result = {}; - - int mib[2] = { CTL_KERN, KERN_OSRELEASE }; - size_t size = 0; - - if (0 == sysctl(mib, 2, NULL, &size, NULL, 0)) - { - char* version = static_cast(alloca(size)); - - if (0 == sysctl(mib, 2, version, &size, NULL, 0)) - { - sscanf(version, "%hu.%hu.%hu", - &result.major, &result.minor, &result.bugfix); - } - } - - return result; -} - -void ReleaseApplicationController() -{ - if (NULL != appCtrl) - { - [NSApp setDelegate:nil]; - [NSApp deactivate]; - - [appCtrl release]; - appCtrl = NULL; - } -} - -} // unnamed namespace - - -const DarwinVersion darwinVersion = GetDarwinVersion(); - - -int main(int argc, char** argv) -{ - for (int i = 0; i <= argc; ++i) - { - const char* const argument = argv[i]; - - if (NULL == argument || '\0' == argument[0]) - { - continue; - } - - if (0 == strcmp(argument, "-wad_picker_restart")) - { - s_restartedFromWADPicker = true; - } - else - { - s_argvStorage.Push(argument); - s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); - } - } - - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - [NSApplication sharedApplication]; - - // The following code isn't mandatory, - // but it enables to run the application without a bundle - if ([NSApp respondsToSelector:@selector(setActivationPolicy:)]) - { - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - } - - CreateMenu(); - - atterm(ReleaseApplicationController); - - appCtrl = [ApplicationController new]; - [NSApp setDelegate:appCtrl]; - [NSApp run]; - - [pool release]; - - return EXIT_SUCCESS; -} diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h new file mode 100644 index 000000000..dc6bea2ab --- /dev/null +++ b/src/posix/cocoa/i_common.h @@ -0,0 +1,170 @@ +/* + ** i_common.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include + + +inline bool I_IsHiDPISupported() +{ + // The following value shoud be equal to NSAppKitVersionNumber10_7 + // and it's hard-coded in order to build on earlier SDKs + + return NSAppKitVersionNumber >= 1138; +} + +void I_ProcessKeyboardEvent(NSEvent* event); +void I_ProcessKeyboardFlagsEvent(NSEvent* event); + +void I_ProcessMouseMoveEvent(NSEvent* event); +void I_ProcessMouseButtonEvent(NSEvent* event); +void I_ProcessMouseWheelEvent(NSEvent* event); + +void I_StartupJoysticks(); +void I_ShutdownJoysticks(); +void I_ProcessJoysticks(); + +NSSize I_GetContentViewSize(const NSWindow* window); +void I_SetMainWindowVisible(bool visible); +void I_SetNativeMouse(bool wantNative); + + +// The following definitions are required to build with older OS X SDKs + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + +typedef unsigned int NSUInteger; +typedef int NSInteger; + +typedef float CGFloat; + +// From HIToolbox/Events.h +enum +{ + kVK_Return = 0x24, + kVK_Tab = 0x30, + kVK_Space = 0x31, + kVK_Delete = 0x33, + kVK_Escape = 0x35, + kVK_Command = 0x37, + kVK_Shift = 0x38, + kVK_CapsLock = 0x39, + kVK_Option = 0x3A, + kVK_Control = 0x3B, + kVK_RightShift = 0x3C, + kVK_RightOption = 0x3D, + kVK_RightControl = 0x3E, + kVK_Function = 0x3F, + kVK_F17 = 0x40, + kVK_VolumeUp = 0x48, + kVK_VolumeDown = 0x49, + kVK_Mute = 0x4A, + kVK_F18 = 0x4F, + kVK_F19 = 0x50, + kVK_F20 = 0x5A, + kVK_F5 = 0x60, + kVK_F6 = 0x61, + kVK_F7 = 0x62, + kVK_F3 = 0x63, + kVK_F8 = 0x64, + kVK_F9 = 0x65, + kVK_F11 = 0x67, + kVK_F13 = 0x69, + kVK_F16 = 0x6A, + kVK_F14 = 0x6B, + kVK_F10 = 0x6D, + kVK_F12 = 0x6F, + kVK_F15 = 0x71, + kVK_Help = 0x72, + kVK_Home = 0x73, + kVK_PageUp = 0x74, + kVK_ForwardDelete = 0x75, + kVK_F4 = 0x76, + kVK_End = 0x77, + kVK_F2 = 0x78, + kVK_PageDown = 0x79, + kVK_F1 = 0x7A, + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E +}; + +@interface NSView(SupportOutdatedOSX) +- (NSPoint)convertPointFromBase:(NSPoint)aPoint; +@end + +@implementation NSView(SupportOutdatedOSX) +- (NSPoint)convertPointFromBase:(NSPoint)aPoint +{ + return [self convertPoint:aPoint fromView:nil]; +} +@end + +#endif // prior to 10.5 + + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 + +enum +{ + NSApplicationActivationPolicyRegular +}; + +typedef NSInteger NSApplicationActivationPolicy; + +@interface NSApplication(ActivationPolicy) +- (BOOL)setActivationPolicy:(NSApplicationActivationPolicy)activationPolicy; +@end + +@interface NSWindow(SetStyleMask) +- (void)setStyleMask:(NSUInteger)styleMask; +@end + +#endif // prior to 10.6 + + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 + +@interface NSView(HiDPIStubs) +- (NSPoint)convertPointToBacking:(NSPoint)aPoint; +- (NSSize)convertSizeToBacking:(NSSize)aSize; +- (NSSize)convertSizeFromBacking:(NSSize)aSize; + +- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag; +@end + +@interface NSScreen(HiDPIStubs) +- (NSRect)convertRectToBacking:(NSRect)aRect; +@end + +#endif // prior to 10.7 diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm new file mode 100644 index 000000000..3abea1cb2 --- /dev/null +++ b/src/posix/cocoa/i_input.mm @@ -0,0 +1,692 @@ +/* + ** i_input.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include +#include + +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "d_event.h" +#include "d_gui.h" +#include "dikeys.h" +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" + +#include "i_common.h" +#include "i_rbopts.h" + + +EXTERN_CVAR(Int, m_use_mouse) + +CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) +{ + if (self < 0) + { + self = 0; + } + else if (self > 2) + { + self = 2; + } +} + + +extern int paused, chatmodeon; +extern constate_e ConsoleState; + +bool GUICapture; + + +namespace +{ + +// TODO: remove this magic! +size_t s_skipMouseMoves; + + +// --------------------------------------------------------------------------- + + +void CheckGUICapture() +{ + const bool wantCapture = (MENU_Off == menuactive) + ? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon) + : (MENU_On == menuactive || MENU_OnNoPause == menuactive); + + if (wantCapture != GUICapture) + { + GUICapture = wantCapture; + + ResetButtonStates(); + } +} + +void CenterCursor() +{ + NSWindow* window = [NSApp keyWindow]; + if (nil == window) + { + return; + } + + const NSRect displayRect = [[window screen] frame]; + const NSRect windowRect = [window frame]; + const CGPoint centerPoint = CGPointMake(NSMidX(windowRect), displayRect.size.height - NSMidY(windowRect)); + + CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + + if (NULL != eventSource) + { + CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource, + kCGEventMouseMoved, centerPoint, kCGMouseButtonLeft); + + if (NULL != mouseMoveEvent) + { + CGEventPost(kCGHIDEventTap, mouseMoveEvent); + CFRelease(mouseMoveEvent); + } + + CFRelease(eventSource); + } + + // TODO: remove this magic! + s_skipMouseMoves = 2; +} + + +bool IsInGame() +{ + switch (mouse_capturemode) + { + default: + case 0: + return gamestate == GS_LEVEL; + + case 1: + return gamestate == GS_LEVEL + || gamestate == GS_INTERMISSION + || gamestate == GS_FINALE; + + case 2: + return true; + } +} + +void CheckNativeMouse() +{ + const bool windowed = (NULL == screen) || !screen->IsFullscreen(); + bool wantNative; + + if (windowed) + { + if (![NSApp isActive] || !use_mouse) + { + wantNative = true; + } + else if (MENU_WaitKey == menuactive) + { + wantNative = false; + } + else + { + wantNative = (!m_use_mouse || MENU_WaitKey != menuactive) + && (!IsInGame() || GUICapture || paused || demoplayback); + } + } + else + { + // ungrab mouse when in the menu with mouse control on. + wantNative = m_use_mouse + && (MENU_On == menuactive || MENU_OnNoPause == menuactive); + } + + I_SetNativeMouse(wantNative); +} + +} // unnamed namespace + + +void I_GetEvent() +{ + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; +} + +void I_StartTic() +{ + CheckGUICapture(); + CheckNativeMouse(); + + I_ProcessJoysticks(); + I_GetEvent(); +} + +void I_StartFrame() +{ +} + + +void I_SetMouseCapture() +{ +} + +void I_ReleaseMouseCapture() +{ +} + +void I_SetNativeMouse(bool wantNative) +{ + static bool nativeMouse = true; + + if (wantNative != nativeMouse) + { + nativeMouse = wantNative; + + if (!wantNative) + { + CenterCursor(); + } + + CGAssociateMouseAndMouseCursorPosition(wantNative); + + if (wantNative) + { + [NSCursor unhide]; + } + else + { + [NSCursor hide]; + } + } +} + + +// --------------------------------------------------------------------------- + + +namespace +{ + +const size_t KEY_COUNT = 128; + + +// See Carbon -> HIToolbox -> Events.h for kVK_ constants + +const uint8_t KEYCODE_TO_DIK[KEY_COUNT] = +{ + DIK_A, DIK_S, DIK_D, DIK_F, DIK_H, DIK_G, DIK_Z, DIK_X, // 0x00 - 0x07 + DIK_C, DIK_V, 0, DIK_B, DIK_Q, DIK_W, DIK_E, DIK_R, // 0x08 - 0x0F + DIK_Y, DIK_T, DIK_1, DIK_2, DIK_3, DIK_4, DIK_6, DIK_5, // 0x10 - 0x17 + DIK_EQUALS, DIK_9, DIK_7, DIK_MINUS, DIK_8, DIK_0, DIK_RBRACKET, DIK_O, // 0x18 - 0x1F + DIK_U, DIK_LBRACKET, DIK_I, DIK_P, DIK_RETURN, DIK_L, DIK_J, DIK_APOSTROPHE, // 0x20 - 0x27 + DIK_K, DIK_SEMICOLON, DIK_BACKSLASH, DIK_COMMA, DIK_SLASH, DIK_N, DIK_M, DIK_PERIOD, // 0x28 - 0x2F + DIK_TAB, DIK_SPACE, DIK_GRAVE, DIK_BACK, 0, DIK_ESCAPE, 0, DIK_LWIN, // 0x30 - 0x37 + DIK_LSHIFT, DIK_CAPITAL, DIK_LMENU, DIK_LCONTROL, DIK_RSHIFT, DIK_RMENU, DIK_RCONTROL, 0, // 0x38 - 0x3F + 0, DIK_DECIMAL, 0, DIK_MULTIPLY, 0, DIK_ADD, 0, 0, // 0x40 - 0x47 + DIK_VOLUMEUP, DIK_VOLUMEDOWN, DIK_MUTE, DIK_SLASH, DIK_NUMPADENTER, 0, DIK_SUBTRACT, 0, // 0x48 - 0x4F + 0, DIK_NUMPAD_EQUALS, DIK_NUMPAD0, DIK_NUMPAD1, DIK_NUMPAD2, DIK_NUMPAD3, DIK_NUMPAD4, DIK_NUMPAD5, // 0x50 - 0x57 + DIK_NUMPAD6, DIK_NUMPAD7, 0, DIK_NUMPAD8, DIK_NUMPAD9, 0, 0, 0, // 0x58 - 0x5F + DIK_F5, DIK_F6, DIK_F7, DIK_F3, DIK_F8, DIK_F9, 0, DIK_F11, // 0x60 - 0x67 + 0, DIK_F13, 0, DIK_F14, 0, DIK_F10, 0, DIK_F12, // 0x68 - 0x6F + 0, DIK_F15, 0, DIK_HOME, 0, DIK_DELETE, DIK_F4, DIK_END, // 0x70 - 0x77 + DIK_F2, 0, DIK_F1, DIK_LEFT, DIK_RIGHT, DIK_DOWN, DIK_UP, 0, // 0x78 - 0x7F +}; + +const uint8_t KEYCODE_TO_ASCII[KEY_COUNT] = +{ + 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', // 0x00 - 0x07 + 'c', 'v', 0, 'b', 'q', 'w', 'e', 'r', // 0x08 - 0x0F + 'y', 't', '1', '2', '3', '4', '6', '5', // 0x10 - 0x17 + '=', '9', '7', '-', '8', '0', ']', 'o', // 0x18 - 0x1F + 'u', '[', 'i', 'p', 13, 'l', 'j', '\'', // 0x20 - 0x27 + 'k', ';', '\\', ',', '/', 'n', 'm', '.', // 0x28 - 0x2F + 9, ' ', '`', 12, 0, 27, 0, 0, // 0x30 - 0x37 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x47 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x48 - 0x4F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x57 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x78 - 0x7F +}; + + +uint8_t ModifierToDIK(const uint32_t modifier) +{ + switch (modifier) + { + case NSAlphaShiftKeyMask: return DIK_CAPITAL; + case NSShiftKeyMask: return DIK_LSHIFT; + case NSControlKeyMask: return DIK_LCONTROL; + case NSAlternateKeyMask: return DIK_LMENU; + case NSCommandKeyMask: return DIK_LWIN; + } + + return 0; +} + +SWORD ModifierFlagsToGUIKeyModifiers(NSEvent* theEvent) +{ + const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); + return ((modifiers & NSShiftKeyMask ) ? GKM_SHIFT : 0) + | ((modifiers & NSControlKeyMask ) ? GKM_CTRL : 0) + | ((modifiers & NSAlternateKeyMask) ? GKM_ALT : 0) + | ((modifiers & NSCommandKeyMask ) ? GKM_META : 0); +} + +bool ShouldGenerateGUICharEvent(NSEvent* theEvent) +{ + const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); + return !(modifiers & NSControlKeyMask) + && !(modifiers & NSAlternateKeyMask) + && !(modifiers & NSCommandKeyMask) + && !(modifiers & NSFunctionKeyMask); +} + + +NSStringEncoding GetEncodingForUnicodeCharacter(const unichar character) +{ + if (character >= L'\u0100' && character <= L'\u024F') + { + return NSWindowsCP1250StringEncoding; // Central and Eastern Europe + } + else if (character >= L'\u0370' && character <= L'\u03FF') + { + return NSWindowsCP1253StringEncoding; // Greek + } + else if (character >= L'\u0400' && character <= L'\u04FF') + { + return NSWindowsCP1251StringEncoding; // Cyrillic + } + + // TODO: add handling for other characters + // TODO: Turkish should use NSWindowsCP1254StringEncoding + + return NSWindowsCP1252StringEncoding; +} + +unsigned char GetCharacterFromNSEvent(NSEvent* theEvent) +{ + const NSString* unicodeCharacters = [theEvent characters]; + + if (0 == [unicodeCharacters length]) + { + return '\0'; + } + + const unichar unicodeCharacter = [unicodeCharacters characterAtIndex:0]; + const NSStringEncoding encoding = GetEncodingForUnicodeCharacter(unicodeCharacter); + + unsigned char character = '\0'; + + if (NSWindowsCP1252StringEncoding == encoding) + { + // TODO: make sure that the following is always correct + character = unicodeCharacter & 0xFF; + } + else + { + const NSData* const characters = + [[theEvent characters] dataUsingEncoding:encoding]; + + character = [characters length] > 0 + ? *static_cast([characters bytes]) + : '\0'; + } + + return character; +} + +void ProcessKeyboardEventInMenu(NSEvent* theEvent) +{ + event_t event = {}; + + event.type = EV_GUI_Event; + event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp; + event.data2 = GetCharacterFromNSEvent(theEvent); + event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); + + if (EV_GUI_KeyDown == event.subtype && [theEvent isARepeat]) + { + event.subtype = EV_GUI_KeyRepeat; + } + + const unsigned short keyCode = [theEvent keyCode]; + + switch (keyCode) + { + case kVK_Return: event.data1 = GK_RETURN; break; + case kVK_PageUp: event.data1 = GK_PGUP; break; + case kVK_PageDown: event.data1 = GK_PGDN; break; + case kVK_End: event.data1 = GK_END; break; + case kVK_Home: event.data1 = GK_HOME; break; + case kVK_LeftArrow: event.data1 = GK_LEFT; break; + case kVK_RightArrow: event.data1 = GK_RIGHT; break; + case kVK_UpArrow: event.data1 = GK_UP; break; + case kVK_DownArrow: event.data1 = GK_DOWN; break; + case kVK_Delete: event.data1 = GK_BACKSPACE; break; + case kVK_ForwardDelete: event.data1 = GK_DEL; break; + case kVK_Escape: event.data1 = GK_ESCAPE; break; + case kVK_F1: event.data1 = GK_F1; break; + case kVK_F2: event.data1 = GK_F2; break; + case kVK_F3: event.data1 = GK_F3; break; + case kVK_F4: event.data1 = GK_F4; break; + case kVK_F5: event.data1 = GK_F5; break; + case kVK_F6: event.data1 = GK_F6; break; + case kVK_F7: event.data1 = GK_F7; break; + case kVK_F8: event.data1 = GK_F8; break; + case kVK_F9: event.data1 = GK_F9; break; + case kVK_F10: event.data1 = GK_F10; break; + case kVK_F11: event.data1 = GK_F11; break; + case kVK_F12: event.data1 = GK_F12; break; + default: + event.data1 = KEYCODE_TO_ASCII[keyCode]; + break; + } + + if (event.data1 < 128) + { + event.data1 = toupper(event.data1); + + D_PostEvent(&event); + } + + if (!iscntrl(event.data2) + && EV_GUI_KeyUp != event.subtype + && ShouldGenerateGUICharEvent(theEvent)) + { + event.subtype = EV_GUI_Char; + event.data1 = event.data2; + event.data2 = event.data3 & GKM_ALT; + + D_PostEvent(&event); + } +} + + +void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) +{ + const NSWindow* window = [inEvent window]; + const NSView* view = [window contentView]; + + const NSPoint screenPos = [NSEvent mouseLocation]; + const NSPoint windowPos = [window convertScreenToBase:screenPos]; + + const NSPoint viewPos = I_IsHiDPISupported() + ? [view convertPointToBacking:windowPos] + : [view convertPoint:windowPos fromView:nil]; + + const CGFloat frameHeight = I_GetContentViewSize(window).height; + + const CGFloat posX = ( viewPos.x - rbOpts.shiftX) / rbOpts.pixelScale; + const CGFloat posY = (frameHeight - viewPos.y - rbOpts.shiftY) / rbOpts.pixelScale; + + outEvent->data1 = static_cast(posX); + outEvent->data2 = static_cast(posY); +} + +void ProcessMouseMoveInMenu(NSEvent* theEvent) +{ + event_t event = {}; + + event.type = EV_GUI_Event; + event.subtype = EV_GUI_MouseMove; + + NSEventToGameMousePosition(theEvent, &event); + + D_PostEvent(&event); +} + +void ProcessMouseMoveInGame(NSEvent* theEvent) +{ + if (!use_mouse) + { + return; + } + + // TODO: remove this magic! + + if (s_skipMouseMoves > 0) + { + --s_skipMouseMoves; + return; + } + + int x([theEvent deltaX]); + int y(-[theEvent deltaY]); + + if (0 == x && 0 == y) + { + return; + } + + if (!m_noprescale) + { + x *= 3; + y *= 2; + } + + event_t event = {}; + + static int lastX = 0, lastY = 0; + + if (m_filter) + { + event.x = (x + lastX) / 2; + event.y = (y + lastY) / 2; + } + else + { + event.x = x; + event.y = y; + } + + lastX = x; + lastY = y; + + if (0 != event.x | 0 != event.y) + { + event.type = EV_Mouse; + + D_PostEvent(&event); + } +} + +} // unnamed namespace + + +void I_ProcessKeyboardEvent(NSEvent* theEvent) +{ + const unsigned short keyCode = [theEvent keyCode]; + if (keyCode >= KEY_COUNT) + { + assert(!"Unknown keycode"); + return; + } + + if (GUICapture) + { + ProcessKeyboardEventInMenu(theEvent); + } + else + { + event_t event = {}; + + event.type = NSKeyDown == [theEvent type] ? EV_KeyDown : EV_KeyUp; + event.data1 = KEYCODE_TO_DIK[ keyCode ]; + + if (0 != event.data1) + { + event.data2 = KEYCODE_TO_ASCII[ keyCode ]; + + D_PostEvent(&event); + } + } +} + +void I_ProcessKeyboardFlagsEvent(NSEvent* theEvent) +{ + static const uint32_t FLAGS_MASK = + NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask; + + const uint32_t modifiers = [theEvent modifierFlags] & FLAGS_MASK; + static uint32_t oldModifiers = 0; + const uint32_t deltaModifiers = modifiers ^ oldModifiers; + + if (0 == deltaModifiers) + { + return; + } + + event_t event = {}; + + event.type = modifiers > oldModifiers ? EV_KeyDown : EV_KeyUp; + event.data1 = ModifierToDIK(deltaModifiers); + + oldModifiers = modifiers; + + // Caps Lock is a modifier key which generates one event per state change + // but not per actual key press or release. So treat any event as key down + // Also its event should be not be posted in menu and console + + if (DIK_CAPITAL == event.data1) + { + if (GUICapture) + { + return; + } + + event.type = EV_KeyDown; + } + + D_PostEvent(&event); +} + + +void I_ProcessMouseMoveEvent(NSEvent* theEvent) +{ + if (GUICapture) + { + ProcessMouseMoveInMenu(theEvent); + } + else + { + ProcessMouseMoveInGame(theEvent); + } +} + +void I_ProcessMouseButtonEvent(NSEvent* theEvent) +{ + event_t event = {}; + + const NSEventType cocoaEventType = [theEvent type]; + + if (GUICapture) + { + event.type = EV_GUI_Event; + + switch (cocoaEventType) + { + case NSLeftMouseDown: event.subtype = EV_GUI_LButtonDown; break; + case NSRightMouseDown: event.subtype = EV_GUI_RButtonDown; break; + case NSOtherMouseDown: event.subtype = EV_GUI_MButtonDown; break; + case NSLeftMouseUp: event.subtype = EV_GUI_LButtonUp; break; + case NSRightMouseUp: event.subtype = EV_GUI_RButtonUp; break; + case NSOtherMouseUp: event.subtype = EV_GUI_MButtonUp; break; + default: break; + } + + NSEventToGameMousePosition(theEvent, &event); + + D_PostEvent(&event); + } + else + { + switch (cocoaEventType) + { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + event.type = EV_KeyDown; + break; + + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + event.type = EV_KeyUp; + break; + + default: + break; + } + + event.data1 = MIN(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8)); + + D_PostEvent(&event); + } +} + +void I_ProcessMouseWheelEvent(NSEvent* theEvent) +{ + const CGFloat delta = [theEvent deltaY]; + const bool isZeroDelta = fabs(delta) < 1.0E-5; + + if (isZeroDelta && GUICapture) + { + return; + } + + event_t event = {}; + + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = delta > 0.0f ? EV_GUI_WheelUp : EV_GUI_WheelDown; + event.data3 = delta; + event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); + } + else + { + event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown; + event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN; + } + + D_PostEvent(&event); +} diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm new file mode 100644 index 000000000..8277e587b --- /dev/null +++ b/src/posix/cocoa/i_main.mm @@ -0,0 +1,626 @@ +/* + ** i_main.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include + +#include +#include + +#include "c_console.h" +#include "c_cvars.h" +#include "cmdlib.h" +#include "d_main.h" +#include "doomerrors.h" +#include "i_system.h" +#include "m_argv.h" +#include "s_sound.h" +#include "version.h" + +#include "i_common.h" +#include "i_osversion.h" +#include "i_rbopts.h" + + +#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) + + +// --------------------------------------------------------------------------- + + +EXTERN_CVAR(Int, vid_defwidth ) +EXTERN_CVAR(Int, vid_defheight) +EXTERN_CVAR(Bool, vid_vsync ) +EXTERN_CVAR(Bool, fullscreen ) + + +// --------------------------------------------------------------------------- + + +DArgs* Args; // command line arguments + +namespace +{ + +// The maximum number of functions that can be registered with atterm. +static const size_t MAX_TERMS = 64; + +static void (*TermFuncs[MAX_TERMS])(); +static const char *TermNames[MAX_TERMS]; +static size_t NumTerms; + +void call_terms() +{ + while (NumTerms > 0) + { + TermFuncs[--NumTerms](); + } +} + +} // unnamed namespace + + +void addterm(void (*func)(), const char *name) +{ + // Make sure this function wasn't already registered. + + for (size_t i = 0; i < NumTerms; ++i) + { + if (TermFuncs[i] == func) + { + return; + } + } + + if (NumTerms == MAX_TERMS) + { + func(); + I_FatalError("Too many exit functions registered."); + } + + TermNames[NumTerms] = name; + TermFuncs[NumTerms] = func; + + ++NumTerms; +} + +void popterm() +{ + if (NumTerms) + { + --NumTerms; + } +} + + +void Mac_I_FatalError(const char* const message) +{ + I_SetMainWindowVisible(false); + + const CFStringRef errorString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, + message, kCFStringEncodingASCII, kCFAllocatorNull); + + if (NULL != errorString) + { + CFOptionFlags dummy; + + CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, + CFSTR("Fatal Error"), errorString, CFSTR("Exit"), NULL, NULL, &dummy); + + CFRelease(errorString); + } +} + + +namespace +{ + +const int ARGC_MAX = 64; + +int s_argc; +char* s_argv[ARGC_MAX]; + +TArray s_argvStorage; + +bool s_restartedFromWADPicker; + + +void NewFailure() +{ + I_FatalError("Failed to allocate memory from system heap"); +} + + +int OriginalMain(int argc, char** argv) +{ + printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n", + GetVersionString(), GetGitTime(), __DATE__); + + seteuid(getuid()); + std::set_new_handler(NewFailure); + + // Set LC_NUMERIC environment variable in case some library decides to + // clear the setlocale call at least this will be correct. + // Note that the LANG environment variable is overridden by LC_* + setenv("LC_NUMERIC", "C", 1); + setlocale(LC_ALL, "C"); + + // Set reasonable default values for video settings + + const NSSize screenSize = [[NSScreen mainScreen] frame].size; + vid_defwidth = static_cast(screenSize.width); + vid_defheight = static_cast(screenSize.height); + vid_vsync = true; + fullscreen = true; + + try + { + Args = new DArgs(argc, argv); + + /* + killough 1/98: + + This fixes some problems with exit handling + during abnormal situations. + + The old code called I_Quit() to end program, + while now I_Quit() is installed as an exit + handler and exit() is called to exit, either + normally or abnormally. Seg faults are caught + and the error handler is used, to prevent + being left in graphics mode or having very + loud SFX noise because the sound card is + left in an unstable state. + */ + + atexit (call_terms); + atterm (I_Quit); + + // Should we even be doing anything with progdir on Unix systems? + char program[PATH_MAX]; + if (realpath (argv[0], program) == NULL) + strcpy (program, argv[0]); + char *slash = strrchr (program, '/'); + if (slash != NULL) + { + *(slash + 1) = '\0'; + progdir = program; + } + else + { + progdir = "./"; + } + + I_StartupJoysticks(); + atterm(I_ShutdownJoysticks); + + C_InitConsole(80 * 8, 25 * 8, false); + D_DoomMain(); + } + catch(const CDoomError& error) + { + const char* const message = error.GetMessage(); + + if (NULL != message) + { + fprintf(stderr, "%s\n", message); + Mac_I_FatalError(message); + } + + exit(-1); + } + catch(...) + { + call_terms(); + throw; + } + + return 0; +} + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +@interface ApplicationController : NSResponder +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + +#endif +{ +} + +- (void)keyDown:(NSEvent*)theEvent; +- (void)keyUp:(NSEvent*)theEvent; + +- (void)applicationDidBecomeActive:(NSNotification*)aNotification; +- (void)applicationWillResignActive:(NSNotification*)aNotification; + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification; + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; + +- (void)processEvents:(NSTimer*)timer; + +@end + + +ApplicationController* appCtrl; + + +@implementation ApplicationController + +- (void)keyDown:(NSEvent*)theEvent +{ + // Empty but present to avoid playing of 'beep' alert sound + + ZD_UNUSED(theEvent); +} + +- (void)keyUp:(NSEvent*)theEvent +{ + // Empty but present to avoid playing of 'beep' alert sound + + ZD_UNUSED(theEvent); +} + + +- (void)applicationDidBecomeActive:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + S_SetSoundPaused(1); +} + +- (void)applicationWillResignActive:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + S_SetSoundPaused(0); +} + + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification +{ + // When starting from command line with real executable path, e.g. ZDoom.app/Contents/MacOS/ZDoom + // application remains deactivated for an unknown reason. + // The following call resolves this issue + [NSApp activateIgnoringOtherApps:YES]; + + // Setup timer for custom event loop + + NSTimer* timer = [NSTimer timerWithTimeInterval:0 + target:self + selector:@selector(processEvents:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; + + exit(OriginalMain(s_argc, s_argv)); +} + + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename +{ + ZD_UNUSED(theApplication); + + if (s_restartedFromWADPicker + || 0 == [filename length] + || s_argc + 2 >= ARGC_MAX) + { + return FALSE; + } + + // Some parameters from command line are passed to this function + // These parameters need to be skipped to avoid duplication + // Note: SDL has different approach to fix this issue, see the same method in SDLMain.m + + const char* const charFileName = [filename UTF8String]; + + for (int i = 0; i < s_argc; ++i) + { + if (0 == strcmp(s_argv[i], charFileName)) + { + return FALSE; + } + } + + s_argvStorage.Push("-file"); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + + s_argvStorage.Push([filename UTF8String]); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + + return TRUE; +} + + +- (void)processEvents:(NSTimer*)timer +{ + ZD_UNUSED(timer); + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + while (true) + { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate dateWithTimeIntervalSinceNow:0] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (nil == event) + { + break; + } + + const NSEventType eventType = [event type]; + + switch (eventType) + { + case NSMouseMoved: + I_ProcessMouseMoveEvent(event); + break; + + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + I_ProcessMouseButtonEvent(event); + break; + + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + I_ProcessMouseButtonEvent(event); + I_ProcessMouseMoveEvent(event); + break; + + case NSScrollWheel: + I_ProcessMouseWheelEvent(event); + break; + + case NSKeyDown: + case NSKeyUp: + I_ProcessKeyboardEvent(event); + break; + + case NSFlagsChanged: + I_ProcessKeyboardFlagsEvent(event); + break; + + default: + break; + } + + [NSApp sendEvent:event]; + } + + [NSApp updateWindows]; + + [pool release]; +} + +@end + + +// --------------------------------------------------------------------------- + + +namespace +{ + +NSMenuItem* CreateApplicationMenu() +{ + NSMenu* menu = [NSMenu new]; + + [menu addItemWithTitle:[@"About " stringByAppendingString:@GAMENAME] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:[@"Hide " stringByAppendingString:@GAMENAME] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[menu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; + [menu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:[@"Quit " stringByAppendingString:@GAMENAME] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + if ([NSApp respondsToSelector:@selector(setAppleMenu:)]) + { + [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; + } + + return menuItem; +} + +NSMenuItem* CreateEditMenu() +{ + NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Edit"]; + + [menu addItemWithTitle:@"Undo" + action:@selector(undo:) + keyEquivalent:@"z"]; + [menu addItemWithTitle:@"Redo" + action:@selector(redo:) + keyEquivalent:@"Z"]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:@"Cut" + action:@selector(cut:) + keyEquivalent:@"x"]; + [menu addItemWithTitle:@"Copy" + action:@selector(copy:) + keyEquivalent:@"c"]; + [menu addItemWithTitle:@"Paste" + action:@selector(paste:) + keyEquivalent:@"v"]; + [menu addItemWithTitle:@"Delete" + action:@selector(delete:) + keyEquivalent:@""]; + [menu addItemWithTitle:@"Select All" + action:@selector(selectAll:) + keyEquivalent:@"a"]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + return menuItem; +} + +NSMenuItem* CreateWindowMenu() +{ + NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:menu]; + + [menu addItemWithTitle:@"Minimize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [menu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + return menuItem; +} + +void CreateMenu() +{ + NSMenu* menuBar = [NSMenu new]; + [menuBar addItem:CreateApplicationMenu()]; + [menuBar addItem:CreateEditMenu()]; + [menuBar addItem:CreateWindowMenu()]; + + [NSApp setMainMenu:menuBar]; +} + +DarwinVersion GetDarwinVersion() +{ + DarwinVersion result = {}; + + int mib[2] = { CTL_KERN, KERN_OSRELEASE }; + size_t size = 0; + + if (0 == sysctl(mib, 2, NULL, &size, NULL, 0)) + { + char* version = static_cast(alloca(size)); + + if (0 == sysctl(mib, 2, version, &size, NULL, 0)) + { + sscanf(version, "%hu.%hu.%hu", + &result.major, &result.minor, &result.bugfix); + } + } + + return result; +} + +void ReleaseApplicationController() +{ + if (NULL != appCtrl) + { + [NSApp setDelegate:nil]; + [NSApp deactivate]; + + [appCtrl release]; + appCtrl = NULL; + } +} + +} // unnamed namespace + + +const DarwinVersion darwinVersion = GetDarwinVersion(); + + +int main(int argc, char** argv) +{ + for (int i = 0; i <= argc; ++i) + { + const char* const argument = argv[i]; + + if (NULL == argument || '\0' == argument[0]) + { + continue; + } + + if (0 == strcmp(argument, "-wad_picker_restart")) + { + s_restartedFromWADPicker = true; + } + else + { + s_argvStorage.Push(argument); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + } + } + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + [NSApplication sharedApplication]; + + // The following code isn't mandatory, + // but it enables to run the application without a bundle + if ([NSApp respondsToSelector:@selector(setActivationPolicy:)]) + { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + } + + CreateMenu(); + + atterm(ReleaseApplicationController); + + appCtrl = [ApplicationController new]; + [NSApp setDelegate:appCtrl]; + [NSApp run]; + + [pool release]; + + return EXIT_SUCCESS; +} diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm new file mode 100644 index 000000000..61e160225 --- /dev/null +++ b/src/posix/cocoa/i_video.mm @@ -0,0 +1,1226 @@ +/* + ** i_video.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bitmap.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "hardware.h" +#include "i_system.h" +#include "m_argv.h" +#include "r_renderer.h" +#include "r_swrenderer.h" +#include "stats.h" +#include "textures.h" +#include "v_palette.h" +#include "v_pfx.h" +#include "v_text.h" +#include "v_video.h" +#include "version.h" + +#include "i_common.h" +#include "i_rbopts.h" + + +EXTERN_CVAR(Bool, ticker ) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Bool, vid_hidpi) + +CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + extern int NewWidth, NewHeight, NewBits, DisplayBits; + + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + + +RenderBufferOptions rbOpts; + + +// --------------------------------------------------------------------------- + + +namespace +{ + const NSInteger LEVEL_FULLSCREEN = NSMainMenuWindowLevel + 1; + const NSInteger LEVEL_WINDOWED = NSNormalWindowLevel; + + const NSUInteger STYLE_MASK_FULLSCREEN = NSBorderlessWindowMask; + const NSUInteger STYLE_MASK_WINDOWED = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + + NSCursor* s_cursor; +} + + +// --------------------------------------------------------------------------- + + +@interface CocoaWindow : NSWindow +{ +} + +- (BOOL)canBecomeKeyWindow; + +@end + + +@implementation CocoaWindow + +- (BOOL)canBecomeKeyWindow +{ + return true; +} + +@end + + +// --------------------------------------------------------------------------- + + +@interface CocoaView : NSOpenGLView +{ +} + +- (void)resetCursorRects; + +@end + + +@implementation CocoaView + +- (void)resetCursorRects +{ + [super resetCursorRects]; + [self addCursorRect: [self bounds] + cursor: s_cursor]; +} + +@end + + + +// --------------------------------------------------------------------------- + + +class CocoaVideo : public IVideo +{ +public: + explicit CocoaVideo(int multisample); + ~CocoaVideo(); + + virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } + virtual void SetWindowedScale(float scale); + + virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); + + virtual void StartModeIterator(int bits, bool fullscreen); + virtual bool NextMode(int* width, int* height, bool* letterbox); + + static bool IsFullscreen(); + static void UseHiDPI(bool hiDPI); + static void InvalidateCursorRects(); + static void SetWindowVisible(bool visible); + +private: + struct ModeIterator + { + size_t index; + int bits; + bool fullscreen; + }; + + ModeIterator m_modeIterator; + + CocoaWindow* m_window; + + int m_width; + int m_height; + bool m_fullscreen; + bool m_hiDPI; + + void SetStyleMask(NSUInteger styleMask); + void SetFullscreenMode(int width, int height); + void SetWindowedMode(int width, int height); + void SetMode(int width, int height, bool fullscreen, bool hiDPI); + + static CocoaVideo* GetInstance(); +}; + + +class CocoaFrameBuffer : public DFrameBuffer +{ +public: + CocoaFrameBuffer(int width, int height, bool fullscreen); + ~CocoaFrameBuffer(); + + virtual bool Lock(bool buffer); + virtual void Unlock(); + virtual void Update(); + + virtual PalEntry* GetPalette(); + virtual void GetFlashedPalette(PalEntry pal[256]); + virtual void UpdatePalette(); + + virtual bool SetGamma(float gamma); + virtual bool SetFlash(PalEntry rgb, int amount); + virtual void GetFlash(PalEntry &rgb, int &amount); + + virtual int GetPageCount(); + + virtual bool IsFullscreen(); + + virtual void SetVSync(bool vsync); + +private: + const size_t BYTES_PER_PIXEL = 4; + + PalEntry m_palette[256]; + bool m_needPaletteUpdate; + + BYTE m_gammaTable[3][256]; + float m_gamma; + bool m_needGammaUpdate; + + PalEntry m_flashColor; + int m_flashAmount; + + bool m_isUpdatePending; + + uint8_t* m_pixelBuffer; + GLuint m_texture; + + void Flip(); + + void UpdateColors(); +}; + + +// --------------------------------------------------------------------------- + + +EXTERN_CVAR(Float, Gamma) + +CUSTOM_CVAR(Float, rgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR(Float, ggamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR(Float, bgamma, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (NULL != screen) + { + screen->SetGamma(Gamma); + } +} + + +// --------------------------------------------------------------------------- + + +extern id appCtrl; + + +namespace +{ + +const struct +{ + uint16_t width; + uint16_t height; +} +VideoModes[] = +{ + { 320, 200 }, + { 320, 240 }, + { 400, 225 }, // 16:9 + { 400, 300 }, + { 480, 270 }, // 16:9 + { 480, 360 }, + { 512, 288 }, // 16:9 + { 512, 384 }, + { 640, 360 }, // 16:9 + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, // 16:10 + { 720, 540 }, + { 800, 450 }, // 16:9 + { 800, 480 }, + { 800, 500 }, // 16:10 + { 800, 600 }, + { 848, 480 }, // 16:9 + { 960, 600 }, // 16:10 + { 960, 720 }, + { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 + { 1024, 640 }, // 16:10 + { 1024, 768 }, + { 1088, 612 }, // 16:9 + { 1152, 648 }, // 16:9 + { 1152, 720 }, // 16:10 + { 1152, 864 }, + { 1280, 720 }, // 16:9 + { 1280, 854 }, + { 1280, 800 }, // 16:10 + { 1280, 960 }, + { 1280, 1024 }, // 5:4 + { 1360, 768 }, // 16:9 + { 1366, 768 }, + { 1400, 787 }, // 16:9 + { 1400, 875 }, // 16:10 + { 1400, 1050 }, + { 1440, 900 }, + { 1440, 960 }, + { 1440, 1080 }, + { 1600, 900 }, // 16:9 + { 1600, 1000 }, // 16:10 + { 1600, 1200 }, + { 1920, 1080 }, + { 1920, 1200 }, + { 2048, 1536 }, + { 2560, 1440 }, + { 2560, 1600 }, + { 2560, 2048 }, + { 2880, 1800 }, + { 3200, 1800 }, + { 3840, 2160 }, + { 3840, 2400 }, + { 4096, 2160 }, + { 5120, 2880 } +}; + + +cycle_t BlitCycles; +cycle_t FlipCycles; + + +CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) +{ + CocoaWindow* window = [[CocoaWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [window setOpaque:YES]; + [window makeFirstResponder:appCtrl]; + [window setAcceptsMouseMovedEvents:YES]; + + return window; +} + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +CocoaVideo::CocoaVideo(const int multisample) +: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +, m_width(-1) +, m_height(-1) +, m_fullscreen(false) +, m_hiDPI(false) +{ + memset(&m_modeIterator, 0, sizeof m_modeIterator); + + // Set attributes for OpenGL context + + NSOpenGLPixelFormatAttribute attributes[16]; + size_t i = 0; + + attributes[i++] = NSOpenGLPFADoubleBuffer; + attributes[i++] = NSOpenGLPFAColorSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(32); + attributes[i++] = NSOpenGLPFADepthSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(24); + attributes[i++] = NSOpenGLPFAStencilSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(8); + + if (multisample) + { + attributes[i++] = NSOpenGLPFAMultisample; + attributes[i++] = NSOpenGLPFASampleBuffers; + attributes[i++] = NSOpenGLPixelFormatAttribute(1); + attributes[i++] = NSOpenGLPFASamples; + attributes[i++] = NSOpenGLPixelFormatAttribute(multisample); + } + + attributes[i] = NSOpenGLPixelFormatAttribute(0); + + // Create OpenGL context and view + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + + const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; + NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect + pixelFormat:pixelFormat]; + [[glView openGLContext] makeCurrentContext]; + + [m_window setContentView:glView]; +} + +CocoaVideo::~CocoaVideo() +{ + [m_window release]; +} + +void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) +{ + m_modeIterator.index = 0; + m_modeIterator.bits = bits; + m_modeIterator.fullscreen = fullscreen; +} + +bool CocoaVideo::NextMode(int* const width, int* const height, bool* const letterbox) +{ + assert(NULL != width); + assert(NULL != height); + + const int bits = m_modeIterator.bits; + + if (8 != bits && 16 != bits && 24 != bits && 32 != bits) + { + return false; + } + + size_t& index = m_modeIterator.index; + + if (index < sizeof(VideoModes) / sizeof(VideoModes[0])) + { + *width = VideoModes[index].width; + *height = VideoModes[index].height; + + if (m_modeIterator.fullscreen && NULL != letterbox) + { + const NSSize screenSize = [[m_window screen] frame].size; + const float screenRatio = screenSize.width / screenSize.height; + const float modeRatio = float(*width) / *height; + + *letterbox = fabs(screenRatio - modeRatio) > 0.001f; + } + + ++index; + + return true; + } + + return false; +} + +DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool fullscreen, DFrameBuffer* const old) +{ + PalEntry flashColor = 0; + int flashAmount = 0; + + if (NULL != old) + { + if (width == m_width && height == m_height) + { + SetMode(width, height, fullscreen, vid_hidpi); + return old; + } + + old->GetFlash(flashColor, flashAmount); + old->ObjectFlags |= OF_YesReallyDelete; + + if (old == screen) + { + screen = NULL; + } + + delete old; + } + + CocoaFrameBuffer* fb = new CocoaFrameBuffer(width, height, fullscreen); + fb->SetFlash(flashColor, flashAmount); + + SetMode(width, height, fullscreen, vid_hidpi); + + return fb; +} + +void CocoaVideo::SetWindowedScale(float scale) +{ +} + + +bool CocoaVideo::IsFullscreen() +{ + CocoaVideo* const video = GetInstance(); + return NULL == video + ? false + : video->m_fullscreen; +} + +void CocoaVideo::UseHiDPI(const bool hiDPI) +{ + if (CocoaVideo* const video = GetInstance()) + { + video->SetMode(video->m_width, video->m_height, video->m_fullscreen, hiDPI); + } +} + +void CocoaVideo::InvalidateCursorRects() +{ + if (CocoaVideo* const video = GetInstance()) + { + [video->m_window invalidateCursorRectsForView:[video->m_window contentView]]; + } +} + +void CocoaVideo::SetWindowVisible(bool visible) +{ + if (CocoaVideo* const video = GetInstance()) + { + if (visible) + { + [video->m_window orderFront:nil]; + } + else + { + [video->m_window orderOut:nil]; + } + } +} + + +static bool HasModernFullscreenAPI() +{ + // The following value shoud be equal to NSAppKitVersionNumber10_6 + // and it's hard-coded in order to build on earlier SDKs + + return NSAppKitVersionNumber >= 1038; +} + +void CocoaVideo::SetStyleMask(const NSUInteger styleMask) +{ + // Before 10.6 it's impossible to change window's style mask + // To workaround this new window should be created with required style mask + // This method should not be called when running on Snow Leopard or newer + + assert(!HasModernFullscreenAPI()); + + CocoaWindow* tempWindow = CreateCocoaWindow(styleMask); + [tempWindow setContentView:[m_window contentView]]; + + [m_window close]; + m_window = tempWindow; +} + +void CocoaVideo::SetFullscreenMode(const int width, const int height) +{ + NSScreen* screen = [m_window screen]; + + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = vid_hidpi + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast(width ); + const float pixelScaleFactorY = displayHeight / static_cast(height); + + rbOpts.pixelScale = MIN(pixelScaleFactorX, pixelScaleFactorY); + + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; + + rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; + rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; + + if (!m_fullscreen) + { + if (HasModernFullscreenAPI()) + { + [m_window setLevel:LEVEL_FULLSCREEN]; + [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; + } + else + { + // Old Carbon-based way to make fullscreen window above dock and menu + // It's supported on 64-bit, but on 10.6 and later the following is preferred: + // [NSWindow setLevel:NSMainMenuWindowLevel + 1] + + SetSystemUIMode(kUIModeAllHidden, 0); + SetStyleMask(STYLE_MASK_FULLSCREEN); + } + + [m_window setHidesOnDeactivate:YES]; + } + + [m_window setFrame:displayRect display:YES]; + [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; +} + +void CocoaVideo::SetWindowedMode(const int width, const int height) +{ + rbOpts.pixelScale = 1.0f; + + rbOpts.width = static_cast(width ); + rbOpts.height = static_cast(height); + + rbOpts.shiftX = 0.0f; + rbOpts.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = vid_hidpi + ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + if (m_fullscreen) + { + if (HasModernFullscreenAPI()) + { + [m_window setLevel:LEVEL_WINDOWED]; + [m_window setStyleMask:STYLE_MASK_WINDOWED]; + } + else + { + SetSystemUIMode(kUIModeNormal, 0); + SetStyleMask(STYLE_MASK_WINDOWED); + } + + [m_window setHidesOnDeactivate:NO]; + } + + [m_window setContentSize:windowSize]; + [m_window center]; + + NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(terminate:)]; + [closeButton setTarget:NSApp]; +} + +void CocoaVideo::SetMode(const int width, const int height, const bool fullscreen, const bool hiDPI) +{ + if (fullscreen == m_fullscreen + && width == m_width + && height == m_height + && hiDPI == m_hiDPI) + { + return; + } + + if (I_IsHiDPISupported()) + { + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; + } + + if (fullscreen) + { + SetFullscreenMode(width, height); + } + else + { + SetWindowedMode(width, height); + } + + rbOpts.dirty = true; + + const NSSize viewSize = I_GetContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + CGLFlushDrawable(CGLGetCurrentContext()); + + static NSString* const TITLE_STRING = + [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + [m_window setTitle:TITLE_STRING]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; +} + + +CocoaVideo* CocoaVideo::GetInstance() +{ + return static_cast(Video); +} + + +CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) +: DFrameBuffer(width, height) +, m_needPaletteUpdate(false) +, m_gamma(0.0f) +, m_needGammaUpdate(false) +, m_flashAmount(0) +, m_isUpdatePending(false) +, m_pixelBuffer(new uint8_t[width * height * BYTES_PER_PIXEL]) +, m_texture(0) +{ + glEnable(GL_TEXTURE_RECTANGLE_ARB); + + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_texture); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, width, height, 0.0, -1.0, 1.0); + + GPfx.SetFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000); + + for (size_t i = 0; i < 256; ++i) + { + m_gammaTable[0][i] = m_gammaTable[1][i] = m_gammaTable[2][i] = i; + } + + memcpy(m_palette, GPalette.BaseColors, sizeof(PalEntry) * 256); + UpdateColors(); + + SetVSync(vid_vsync); +} + + +CocoaFrameBuffer::~CocoaFrameBuffer() +{ + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_texture); + + delete[] m_pixelBuffer; +} + +int CocoaFrameBuffer::GetPageCount() +{ + return 1; +} + +bool CocoaFrameBuffer::Lock(bool buffered) +{ + return DSimpleCanvas::Lock(buffered); +} + +void CocoaFrameBuffer::Unlock() +{ + if (m_isUpdatePending && LockCount == 1) + { + Update(); + } + else if (--LockCount <= 0) + { + Buffer = NULL; + LockCount = 0; + } +} + +void CocoaFrameBuffer::Update() +{ + if (LockCount != 1) + { + if (LockCount > 0) + { + m_isUpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff(); + + Buffer = NULL; + LockCount = 0; + m_isUpdatePending = false; + + BlitCycles.Reset(); + FlipCycles.Reset(); + BlitCycles.Clock(); + + GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, + Width, Height, FRACUNIT, FRACUNIT, 0, 0); + + FlipCycles.Clock(); + Flip(); + FlipCycles.Unclock(); + + BlitCycles.Unclock(); + + if (m_needGammaUpdate) + { + CalcGamma(rgamma == 0.0f ? m_gamma : m_gamma * rgamma, m_gammaTable[0]); + CalcGamma(ggamma == 0.0f ? m_gamma : m_gamma * ggamma, m_gammaTable[1]); + CalcGamma(bgamma == 0.0f ? m_gamma : m_gamma * bgamma, m_gammaTable[2]); + + m_needGammaUpdate = false; + m_needPaletteUpdate = true; + } + + if (m_needPaletteUpdate) + { + m_needPaletteUpdate = false; + UpdateColors(); + } +} + +void CocoaFrameBuffer::UpdateColors() +{ + PalEntry palette[256]; + + for (size_t i = 0; i < 256; ++i) + { + palette[i].r = m_gammaTable[0][m_palette[i].r]; + palette[i].g = m_gammaTable[1][m_palette[i].g]; + palette[i].b = m_gammaTable[2][m_palette[i].b]; + } + + if (0 != m_flashAmount) + { + DoBlending(palette, palette, 256, + m_gammaTable[0][m_flashColor.r], + m_gammaTable[1][m_flashColor.g], + m_gammaTable[2][m_flashColor.b], + m_flashAmount); + } + + GPfx.SetPalette(palette); +} + +PalEntry* CocoaFrameBuffer::GetPalette() +{ + return m_palette; +} + +void CocoaFrameBuffer::UpdatePalette() +{ + m_needPaletteUpdate = true; +} + +bool CocoaFrameBuffer::SetGamma(float gamma) +{ + m_gamma = gamma; + m_needGammaUpdate = true; + + return true; +} + +bool CocoaFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + m_flashColor = rgb; + m_flashAmount = amount; + m_needPaletteUpdate = true; + + return true; +} + +void CocoaFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = m_flashColor; + amount = m_flashAmount; +} + +void CocoaFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + memcpy(pal, m_palette, sizeof m_palette); + + if (0 != m_flashAmount) + { + DoBlending(pal, pal, 256, + m_flashColor.r, m_flashColor.g, m_flashColor.b, + m_flashAmount); + } +} + +bool CocoaFrameBuffer::IsFullscreen() +{ + return CocoaVideo::IsFullscreen(); +} + +void CocoaFrameBuffer::SetVSync(bool vsync) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + const long value = vsync ? 1 : 0; +#else // 10.5 or newer + const GLint value = vsync ? 1 : 0; +#endif // prior to 10.5 + + [[NSOpenGLContext currentContext] setValues:&value + forParameter:NSOpenGLCPSwapInterval]; +} + +void CocoaFrameBuffer::Flip() +{ + assert(NULL != screen); + + if (rbOpts.dirty) + { + glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); + + // TODO: Figure out why the following glClear() call is needed + // to avoid drawing of garbage in fullscreen mode when + // in-game's aspect ratio is different from display one + glClear(GL_COLOR_BUFFER_BIT); + + rbOpts.dirty = false; + } + +#ifdef __LITTLE_ENDIAN__ + static const GLenum format = GL_RGBA; +#else // __BIG_ENDIAN__ + static const GLenum format = GL_ABGR_EXT; +#endif // __LITTLE_ENDIAN__ + + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + + glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, 0.0f); + glTexCoord2f(Width, 0.0f); + glVertex2f(Width, 0.0f); + glTexCoord2f(Width, Height); + glVertex2f(Width, Height); + glTexCoord2f(0.0f, Height); + glVertex2f(0.0f, Height); + glEnd(); + + glFlush(); + + [[NSOpenGLContext currentContext] flushBuffer]; +} + + +ADD_STAT(blit) +{ + FString result; + result.Format("blit=%04.1f ms flip=%04.1f ms", BlitCycles.TimeMS(), FlipCycles.TimeMS()); + return result; +} + + +IVideo* Video; + + +void I_ShutdownGraphics() +{ + if (NULL != screen) + { + screen->ObjectFlags |= OF_YesReallyDelete; + delete screen; + screen = NULL; + } + + delete Video; + Video = NULL; +} + +void I_InitGraphics() +{ + UCVarValue val; + + val.Bool = !!Args->CheckParm("-devparm"); + ticker.SetGenericRepDefault(val, CVAR_Bool); + + Video = new CocoaVideo(0); + atterm(I_ShutdownGraphics); +} + + +static void I_DeleteRenderer() +{ + delete Renderer; + Renderer = NULL; +} + +void I_CreateRenderer() +{ + if (NULL == Renderer) + { + Renderer = new FSoftwareRenderer; + atterm(I_DeleteRenderer); + } +} + + +DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) +{ + return Video->CreateFrameBuffer(width, height, fullscreen, old); +} + +bool I_CheckResolution(const int width, const int height, const int bits) +{ + int twidth, theight; + + Video->StartModeIterator(bits, fullscreen); + + while (Video->NextMode(&twidth, &theight, NULL)) + { + if (width == twidth && height == theight) + { + return true; + } + } + + return false; +} + +void I_ClosestResolution(int *width, int *height, int bits) +{ + int twidth, theight; + int cwidth = 0, cheight = 0; + int iteration; + DWORD closest = DWORD(-1); + + for (iteration = 0; iteration < 2; ++iteration) + { + Video->StartModeIterator(bits, fullscreen); + + while (Video->NextMode(&twidth, &theight, NULL)) + { + if (twidth == *width && theight == *height) + { + return; + } + + if (iteration == 0 && (twidth < *width || theight < *height)) + { + continue; + } + + const DWORD dist = (twidth - *width) * (twidth - *width) + + (theight - *height) * (theight - *height); + + if (dist < closest) + { + closest = dist; + cwidth = twidth; + cheight = theight; + } + } + + if (closest != DWORD(-1)) + { + *width = cwidth; + *height = cheight; + return; + } + } +} + + +EXTERN_CVAR(Int, vid_maxfps); +EXTERN_CVAR(Bool, cl_capfps); + +// So Apple doesn't support POSIX timers and I can't find a good substitute short of +// having Objective-C Cocoa events or something like that. +void I_SetFPSLimit(int limit) +{ +} + +CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (vid_maxfps < TICRATE && vid_maxfps != 0) + { + vid_maxfps = TICRATE; + } + else if (vid_maxfps > 1000) + { + vid_maxfps = 1000; + } + else if (cl_capfps == 0) + { + I_SetFPSLimit(vid_maxfps); + } +} + +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (I_IsHiDPISupported()) + { + CocoaVideo::UseHiDPI(self); + } + else if (0 != self) + { + self = 0; + } +} + + +CCMD(vid_listmodes) +{ + if (Video == NULL) + { + return; + } + + static const char* const ratios[5] = { "", " - 16:9", " - 16:10", " - 17:10", " - 5:4" }; + int width, height; + bool letterbox; + + Video->StartModeIterator(32, screen->IsFullscreen()); + + while (Video->NextMode(&width, &height, &letterbox)) + { + const bool current = width == DisplayWidth && height == DisplayHeight; + const int ratio = CheckRatio(width, height); + + Printf(current ? PRINT_BOLD : PRINT_HIGH, "%s%4d x%5d x%3d%s%s\n", + current || !(ratio & 3) ? "" : TEXTCOLOR_GOLD, + width, height, 32, ratios[ratio], + current || !letterbox ? "" : TEXTCOLOR_BROWN " LB"); + } +} + +CCMD(vid_currentmode) +{ + Printf("%dx%dx%d\n", DisplayWidth, DisplayHeight, DisplayBits); +} + + +// --------------------------------------------------------------------------- + + +bool I_SetCursor(FTexture* cursorpic) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) + { + s_cursor = [NSCursor arrowCursor]; + } + else + { + // Create bitmap image representation + + const NSInteger imageWidth = cursorpic->GetWidth(); + const NSInteger imageHeight = cursorpic->GetHeight(); + const NSInteger imagePitch = imageWidth * 4; + + NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; + [bitmapImageRep initWithBitmapDataPlanes:NULL + pixelsWide:imageWidth + pixelsHigh:imageHeight + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:imagePitch + bitsPerPixel:0]; + + // Load bitmap data to representation + + BYTE* buffer = [bitmapImageRep bitmapData]; + memset(buffer, 0, imagePitch * imageHeight); + + FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); + cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); + + // Swap red and blue components in each pixel + + for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) + { + const size_t offset = i * 4; + + const BYTE temp = buffer[offset ]; + buffer[offset ] = buffer[offset + 2]; + buffer[offset + 2] = temp; + } + + // Create image from representation and set it as cursor + + NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType + properties:nil]; + NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; + + s_cursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSMakePoint(0.0f, 0.0f)]; + } + + CocoaVideo::InvalidateCursorRects(); + + [pool release]; + + return true; +} + + +NSSize I_GetContentViewSize(const NSWindow* const window) +{ + const NSView* const view = [window contentView]; + const NSSize frameSize = [view frame].size; + + // TODO: figure out why [NSView frame] returns different values in "fullscreen" and in window + // In "fullscreen" the result is multiplied by [NSScreen backingScaleFactor], but not in window + + return (vid_hidpi && !fullscreen) + ? [view convertSizeToBacking:frameSize] + : frameSize; +} + +void I_SetMainWindowVisible(bool visible) +{ + CocoaVideo::SetWindowVisible(visible); + I_SetNativeMouse(!visible); +} From 4662069b94ba0ab5b42b9e92e4506321907ce0fa Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 28 Dec 2014 17:11:30 +0200 Subject: [PATCH 065/117] Fixed compilation issues with GCC 4.2 --- src/posix/cocoa/i_common.h | 5 ++++- src/posix/cocoa/i_input.mm | 10 ++++++++-- src/posix/cocoa/i_main.mm | 9 +++++++-- src/posix/cocoa/i_video.mm | 25 ++++++++++++++----------- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index dc6bea2ab..09281713f 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -31,7 +31,10 @@ ** */ -#include +#import +#import +#import +#import inline bool I_IsHiDPISupported() diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 3abea1cb2..ad2a3d1bd 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -31,8 +31,12 @@ ** */ -#include -#include +#import +#import +#import + +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass #include "c_console.h" #include "c_cvars.h" @@ -47,6 +51,8 @@ #include "i_common.h" #include "i_rbopts.h" +#undef Class + EXTERN_CVAR(Int, m_use_mouse) diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index 8277e587b..f7f732124 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -33,8 +33,11 @@ #include -#include -#include +#import +#import + +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass #include "c_console.h" #include "c_cvars.h" @@ -50,6 +53,8 @@ #include "i_osversion.h" #include "i_rbopts.h" +#undef Class + #define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 61e160225..0a872e88e 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -31,16 +31,17 @@ ** */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#import +#import +#import +#import +#import +#import +#import +#import + +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass #include "bitmap.h" #include "c_dispatch.h" @@ -61,6 +62,8 @@ #include "i_common.h" #include "i_rbopts.h" +#undef Class + EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_vsync) @@ -215,7 +218,7 @@ public: virtual void SetVSync(bool vsync); private: - const size_t BYTES_PER_PIXEL = 4; + static const size_t BYTES_PER_PIXEL = 4; PalEntry m_palette[256]; bool m_needPaletteUpdate; From b5d0c5c357e3d74c1b90b9492cf176f14b1c7f72 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Dec 2014 22:15:12 +0100 Subject: [PATCH 066/117] - fixed: When a player drops his inventory, the dropped weapons must be checked for their class to ensure that they are not DehackedPickups which cannot be modified as intended. --- src/p_user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index bf656d2b9..e91766cf0 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1365,7 +1365,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags) weap->SpawnState != ::GetDefault()->SpawnState) { item = P_DropItem (this, weap->GetClass(), -1, 256); - if (item != NULL) + if (item != NULL && item->IsKindOf(RUNTIME_CLASS(AWeapon))) { if (weap->AmmoGive1 && weap->Ammo1) { From 51afe8a541837fd5ca9c9164e677f1073768df05 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 29 Dec 2014 10:29:16 +0200 Subject: [PATCH 067/117] Removed explicit release of window in Cocoa IVideo implementation It caused a crash on older OS X like 10.5 and it absence doesn't lead to a memory leak --- src/posix/cocoa/i_video.mm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 0a872e88e..f75f1abdf 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -152,7 +152,6 @@ class CocoaVideo : public IVideo { public: explicit CocoaVideo(int multisample); - ~CocoaVideo(); virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } virtual void SetWindowedScale(float scale); @@ -414,11 +413,6 @@ CocoaVideo::CocoaVideo(const int multisample) [m_window setContentView:glView]; } -CocoaVideo::~CocoaVideo() -{ - [m_window release]; -} - void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) { m_modeIterator.index = 0; From e3f92db3956460c5c158241db04a52515b7edbf8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 29 Dec 2014 11:34:57 +0200 Subject: [PATCH 068/117] Custom cursor in OS native backend without global variable --- src/posix/cocoa/i_video.mm | 42 ++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index f75f1abdf..4ad36b1fe 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -93,8 +93,6 @@ namespace const NSUInteger STYLE_MASK_FULLSCREEN = NSBorderlessWindowMask; const NSUInteger STYLE_MASK_WINDOWED = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; - - NSCursor* s_cursor; } @@ -125,10 +123,13 @@ namespace @interface CocoaView : NSOpenGLView { + NSCursor* m_cursor; } - (void)resetCursorRects; +- (void)setCursor:(NSCursor*)cursor; + @end @@ -137,8 +138,18 @@ namespace - (void)resetCursorRects { [super resetCursorRects]; - [self addCursorRect: [self bounds] - cursor: s_cursor]; + + NSCursor* const cursor = nil == m_cursor + ? [NSCursor arrowCursor] + : m_cursor; + + [self addCursorRect:[self bounds] + cursor:cursor]; +} + +- (void)setCursor:(NSCursor*)cursor +{ + m_cursor = cursor; } @end @@ -163,7 +174,7 @@ public: static bool IsFullscreen(); static void UseHiDPI(bool hiDPI); - static void InvalidateCursorRects(); + static void SetCursor(NSCursor* cursor); static void SetWindowVisible(bool visible); private: @@ -509,11 +520,15 @@ void CocoaVideo::UseHiDPI(const bool hiDPI) } } -void CocoaVideo::InvalidateCursorRects() +void CocoaVideo::SetCursor(NSCursor* cursor) { if (CocoaVideo* const video = GetInstance()) { - [video->m_window invalidateCursorRectsForView:[video->m_window contentView]]; + NSWindow* const window = video->m_window; + CocoaView* const view = [window contentView]; + + [view setCursor:cursor]; + [window invalidateCursorRectsForView:view]; } } @@ -1141,12 +1156,9 @@ CCMD(vid_currentmode) bool I_SetCursor(FTexture* cursorpic) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSCursor* cursor = nil; - if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) - { - s_cursor = [NSCursor arrowCursor]; - } - else + if (NULL != cursorpic && FTexture::TEX_Null != cursorpic->UseType) { // Create bitmap image representation @@ -1191,11 +1203,11 @@ bool I_SetCursor(FTexture* cursorpic) properties:nil]; NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; - s_cursor = [[NSCursor alloc] initWithImage:cursorImage - hotSpot:NSMakePoint(0.0f, 0.0f)]; + cursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSMakePoint(0.0f, 0.0f)]; } - CocoaVideo::InvalidateCursorRects(); + CocoaVideo::SetCursor(cursor); [pool release]; From 21d557ee110e175d45f26132ef064fde4a26a5e0 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 29 Dec 2014 12:10:18 +0200 Subject: [PATCH 069/117] Further cleanup of i_main.mm --- src/posix/cocoa/i_main.mm | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index f7f732124..dcb3b3ed5 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -51,7 +51,6 @@ #include "i_common.h" #include "i_osversion.h" -#include "i_rbopts.h" #undef Class @@ -71,17 +70,15 @@ EXTERN_CVAR(Bool, fullscreen ) // --------------------------------------------------------------------------- -DArgs* Args; // command line arguments - namespace { // The maximum number of functions that can be registered with atterm. -static const size_t MAX_TERMS = 64; +const size_t MAX_TERMS = 64; -static void (*TermFuncs[MAX_TERMS])(); -static const char *TermNames[MAX_TERMS]; -static size_t NumTerms; +void (*TermFuncs[MAX_TERMS])(); +const char *TermNames[MAX_TERMS]; +size_t NumTerms; void call_terms() { @@ -146,6 +143,9 @@ void Mac_I_FatalError(const char* const message) } +DArgs* Args; // command line arguments + + namespace { @@ -207,23 +207,12 @@ int OriginalMain(int argc, char** argv) left in an unstable state. */ - atexit (call_terms); - atterm (I_Quit); + atexit(call_terms); + atterm(I_Quit); - // Should we even be doing anything with progdir on Unix systems? - char program[PATH_MAX]; - if (realpath (argv[0], program) == NULL) - strcpy (program, argv[0]); - char *slash = strrchr (program, '/'); - if (slash != NULL) - { - *(slash + 1) = '\0'; - progdir = program; - } - else - { - progdir = "./"; - } + NSString* exePath = [[NSBundle mainBundle] executablePath]; + progdir = [[exePath stringByDeletingLastPathComponent] UTF8String]; + progdir += "/"; I_StartupJoysticks(); atterm(I_ShutdownJoysticks); From 1c102ef9c94d15bb1f6a2df9b63289e7d9eeaa19 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 29 Dec 2014 12:16:17 +0200 Subject: [PATCH 070/117] Internals of native OS X backend event processing are no longer exposed --- src/posix/cocoa/i_common.h | 7 +---- src/posix/cocoa/i_input.mm | 59 +++++++++++++++++++++++++++++++++----- src/posix/cocoa/i_main.mm | 45 ++--------------------------- 3 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index 09281713f..453f2898f 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -45,12 +45,7 @@ inline bool I_IsHiDPISupported() return NSAppKitVersionNumber >= 1138; } -void I_ProcessKeyboardEvent(NSEvent* event); -void I_ProcessKeyboardFlagsEvent(NSEvent* event); - -void I_ProcessMouseMoveEvent(NSEvent* event); -void I_ProcessMouseButtonEvent(NSEvent* event); -void I_ProcessMouseWheelEvent(NSEvent* event); +void I_ProcessEvent(NSEvent* event); void I_StartupJoysticks(); void I_ShutdownJoysticks(); diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index ad2a3d1bd..46f5e4450 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -535,10 +535,8 @@ void ProcessMouseMoveInGame(NSEvent* theEvent) } } -} // unnamed namespace - -void I_ProcessKeyboardEvent(NSEvent* theEvent) +void ProcessKeyboardEvent(NSEvent* theEvent) { const unsigned short keyCode = [theEvent keyCode]; if (keyCode >= KEY_COUNT) @@ -567,7 +565,7 @@ void I_ProcessKeyboardEvent(NSEvent* theEvent) } } -void I_ProcessKeyboardFlagsEvent(NSEvent* theEvent) +void ProcessKeyboardFlagsEvent(NSEvent* theEvent) { static const uint32_t FLAGS_MASK = NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask; @@ -606,7 +604,7 @@ void I_ProcessKeyboardFlagsEvent(NSEvent* theEvent) } -void I_ProcessMouseMoveEvent(NSEvent* theEvent) +void ProcessMouseMoveEvent(NSEvent* theEvent) { if (GUICapture) { @@ -618,7 +616,7 @@ void I_ProcessMouseMoveEvent(NSEvent* theEvent) } } -void I_ProcessMouseButtonEvent(NSEvent* theEvent) +void ProcessMouseButtonEvent(NSEvent* theEvent) { event_t event = {}; @@ -669,7 +667,7 @@ void I_ProcessMouseButtonEvent(NSEvent* theEvent) } } -void I_ProcessMouseWheelEvent(NSEvent* theEvent) +void ProcessMouseWheelEvent(NSEvent* theEvent) { const CGFloat delta = [theEvent deltaY]; const bool isZeroDelta = fabs(delta) < 1.0E-5; @@ -696,3 +694,50 @@ void I_ProcessMouseWheelEvent(NSEvent* theEvent) D_PostEvent(&event); } + +} // unnamed namespace + + +void I_ProcessEvent(NSEvent* event) +{ + const NSEventType eventType = [event type]; + + switch (eventType) + { + case NSMouseMoved: + ProcessMouseMoveEvent(event); + break; + + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + ProcessMouseButtonEvent(event); + break; + + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + ProcessMouseButtonEvent(event); + ProcessMouseMoveEvent(event); + break; + + case NSScrollWheel: + ProcessMouseWheelEvent(event); + break; + + case NSKeyDown: + case NSKeyUp: + ProcessKeyboardEvent(event); + break; + + case NSFlagsChanged: + ProcessKeyboardFlagsEvent(event); + break; + + default: + break; + } +} diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index dcb3b3ed5..323efa8b7 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -376,48 +376,9 @@ ApplicationController* appCtrl; { break; } - - const NSEventType eventType = [event type]; - - switch (eventType) - { - case NSMouseMoved: - I_ProcessMouseMoveEvent(event); - break; - - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: - case NSOtherMouseDown: - case NSOtherMouseUp: - I_ProcessMouseButtonEvent(event); - break; - - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - I_ProcessMouseButtonEvent(event); - I_ProcessMouseMoveEvent(event); - break; - - case NSScrollWheel: - I_ProcessMouseWheelEvent(event); - break; - - case NSKeyDown: - case NSKeyUp: - I_ProcessKeyboardEvent(event); - break; - - case NSFlagsChanged: - I_ProcessKeyboardFlagsEvent(event); - break; - - default: - break; - } - + + I_ProcessEvent(event); + [NSApp sendEvent:event]; } From 68dbd56eabcecaa072cdb52a38025ff1b1c8ffc8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 29 Dec 2014 18:03:36 +0200 Subject: [PATCH 071/117] Small cleanup in i_video.mm --- src/posix/cocoa/i_video.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 4ad36b1fe..cb988002c 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -38,7 +38,6 @@ #import #import #import -#import // Avoid collision between DObject class and Objective-C #define Class ObjectClass @@ -690,7 +689,7 @@ void CocoaVideo::SetMode(const int width, const int height, const bool fullscree glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - CGLFlushDrawable(CGLGetCurrentContext()); + [[NSOpenGLContext currentContext] flushBuffer]; static NSString* const TITLE_STRING = [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; @@ -943,7 +942,7 @@ void CocoaFrameBuffer::Flip() #endif // __LITTLE_ENDIAN__ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); From 4ddfd0f46a26df2b349182197b4a16c7a0e4f3f8 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 30 Dec 2014 19:59:31 -0600 Subject: [PATCH 072/117] - Added 3 new properties and 3 functions to control them. - Rippers will rip through anything with an equivalent ripper level, or if their level is between or on the min and max ranges. - If no min or max is defined, it simply checks if the monster's ripper level is lower than the missiles. - Functions: A_SetRipperLevel(int level), A_SetRipMin(int min), A_SetRipMax(int max) - Properties: RipperLevel, RipLevelMin, and RipLevelMax. - RipperLevel: Applicable to monsters and projectiles. - RipLevelMin and RipLevelMax are only useful on monsters. - By default, all are 0. --- src/actor.h | 3 ++ src/p_map.cpp | 12 +++++++- src/p_mobj.cpp | 7 +++++ src/thingdef/thingdef_codeptr.cpp | 42 ++++++++++++++++++++++++++++ src/thingdef/thingdef_properties.cpp | 27 ++++++++++++++++++ src/version.h | 2 +- wadsrc/static/actors/actor.txt | 6 ++++ 7 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/actor.h b/src/actor.h index 57736f43b..bc6e830f0 100644 --- a/src/actor.h +++ b/src/actor.h @@ -985,6 +985,9 @@ public: FNameNoInit DeathType; const PClass *TeleFogSourceType; const PClass *TeleFogDestType; + int RipperLevel; + int RipLevelMin; + int RipLevelMax; FState *SpawnState; FState *SeeState; diff --git a/src/p_map.cpp b/src/p_map.cpp index aecead6ed..233cc50cd 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1207,7 +1207,17 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { return true; } - if (tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) + // Rippers will rip through anything with an equivalent ripper level, + // or if the missile's ripper level is within the min/max range, + // or if there's no min/max range and the missile's ripper level is + // >= the monster's, then let 'er rip! + bool ripmin = (thing->RipLevelMin != 0) ? true : false; + bool ripmax = (thing->RipLevelMax != 0) ? true : false; + if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && + ((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) || + ((thing->RipperLevel == tm.thing->RipperLevel) || + (thing->RipLevelMin <= tm.thing->RipperLevel) && + (thing->RipLevelMax >= tm.thing->RipperLevel)))) { if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS)) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index dbce9e2ff..6a035933d 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -338,6 +338,13 @@ void AActor::Serialize (FArchive &arc) arc << TeleFogSourceType << TeleFogDestType; } + if (SaveVersion >= 4518) + { + arc << RipperLevel + << RipLevelMin + << RipLevelMax; + } + { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 50969ce85..54645d864 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5613,3 +5613,45 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) self->TeleFogDestType = temp; } } + +//=========================================================================== +// +// A_SetRipperLevel(int level) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(level, 0); + self->RipperLevel = level; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(min, 1); + self->RipLevelMin = min; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(max, 1); + self->RipLevelMax = max; +} \ No newline at end of file diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index b6a2937ae..a00ac62e1 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1436,6 +1436,33 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor) else defaults->TeleFogDestType = FindClassTentative(str, "TeleportFog"); } +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(ripperlevel, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipperLevel = id; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(riplevelmin, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipLevelMin = id; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(riplevelmax, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipLevelMax = id; +} + //========================================================================== // // Special inventory properties diff --git a/src/version.h b/src/version.h index 6863f0573..09b830438 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4517 +#define SAVEVER 4518 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 73a5e0e4d..c32ecb958 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -28,6 +28,9 @@ ACTOR Actor native //: Thinker DeathType Normal TeleFogSourceType "TeleportFog" TeleFogDestType "TeleportFog" + RipperLevel 0 + RipLevelMin 0 + RipLevelMax 0 // Variables for the expression evaluator // NOTE: fixed_t and angle_t are only used here to ensure proper conversion @@ -321,6 +324,9 @@ ACTOR Actor native //: Thinker action native A_TakeFromSiblings(class itemtype, int amount = 0); action native A_SetTeleFog(name oldpos, name newpos); action native A_SwapTeleFog(); + action native A_SetRipperLevel(int level); + action native A_SetRipMin(int min); + action native A_SetRipMax(int max); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From c57cc91d7c062b7629abc8f7c4fdaa95086432bf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 31 Dec 2014 10:13:15 +0100 Subject: [PATCH 073/117] - cleaned up the RipLevel logic a bit to be less confusing. --- src/p_map.cpp | 27 ++++++++++++++++----------- src/thingdef/thingdef_properties.cpp | 12 ++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 233cc50cd..68d0b21f0 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -887,6 +887,20 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) return true; } + +//========================================================================== +// +// Isolated to keep the code readable and fix the logic +// +//========================================================================== + +static bool CheckRipLevel(AActor *victim, AActor *projectile) +{ + if (victim->RipLevelMin > 0 && projectile->RipperLevel < victim->RipLevelMin) return false; + if (victim->RipLevelMax > 0 && projectile->RipperLevel > victim->RipLevelMax) return false; + return true; +} + //========================================================================== // // PIT_CheckThing @@ -1207,17 +1221,8 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { return true; } - // Rippers will rip through anything with an equivalent ripper level, - // or if the missile's ripper level is within the min/max range, - // or if there's no min/max range and the missile's ripper level is - // >= the monster's, then let 'er rip! - bool ripmin = (thing->RipLevelMin != 0) ? true : false; - bool ripmax = (thing->RipLevelMax != 0) ? true : false; - if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && - ((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) || - ((thing->RipperLevel == tm.thing->RipperLevel) || - (thing->RipLevelMin <= tm.thing->RipperLevel) && - (thing->RipLevelMax >= tm.thing->RipperLevel)))) + + if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && CheckRipLevel(thing, tm.thing)) { if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS)) { diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index a00ac62e1..677979276 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1442,6 +1442,10 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor) DEFINE_PROPERTY(ripperlevel, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipperLevel must not be negative"); + } defaults->RipperLevel = id; } @@ -1451,6 +1455,10 @@ DEFINE_PROPERTY(ripperlevel, I, Actor) DEFINE_PROPERTY(riplevelmin, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipLevelMin must not be negative"); + } defaults->RipLevelMin = id; } @@ -1460,6 +1468,10 @@ DEFINE_PROPERTY(riplevelmin, I, Actor) DEFINE_PROPERTY(riplevelmax, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipLevelMax must not be negative"); + } defaults->RipLevelMax = id; } From 26cf383ead2cbf14d268e6edcefe7ce6c58f02fc Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 31 Dec 2014 12:48:18 +0200 Subject: [PATCH 074/117] Fixed incorrect volume levels of Timidity instruments With Emulate TiMidity option on (midi_timiditylike CVAR set to true) GUS emulation tried to output tones with infinite volumes --- src/timidity/instrum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index 5ab55201e..7454ca7a4 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -485,7 +485,7 @@ fail: sample_t *tmp; for (i = sp->data_length, tmp = sp->data; i; --i) { - a = abs(*tmp++); + a = fabsf(*tmp++); if (a > maxamp) maxamp = a; } From ba3988290c245917e8366ea57eef9773938d825a Mon Sep 17 00:00:00 2001 From: Blue-Shadow Date: Thu, 1 Jan 2015 22:16:51 +0300 Subject: [PATCH 075/117] Added an option to A_Check[SightOr]Range to be able to perform a 2D-based distance check. --- src/thingdef/thingdef_codeptr.cpp | 26 +++++++++++++++----------- wadsrc/static/actors/actor.txt | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 54645d864..9554a3c25 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2613,7 +2613,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) // Useful for maps with many multi-actor special effects. // //=========================================================================== -static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) +static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool twodi) { if (camera == NULL) { @@ -2636,8 +2636,9 @@ static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) { dz = 0; } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range) - { // Within range + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ + // Within range return true; } @@ -2651,9 +2652,10 @@ static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); double range = EvalExpressionF(ParameterIndex+0, self); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! @@ -2663,13 +2665,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) if (playeringame[i]) { // Always check from each player. - if (DoCheckSightOrRange(self, players[i].mo, range)) + if (DoCheckSightOrRange(self, players[i].mo, range, twodi)) { return; } // If a player is viewing from a non-player, check that too. if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckSightOrRange(self, players[i].camera, range)) + DoCheckSightOrRange(self, players[i].camera, range, twodi)) { return; } @@ -2684,7 +2686,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) // Jumps if this actor is out of range of all players. // //=========================================================================== -static bool DoCheckRange(AActor *self, AActor *camera, double range) +static bool DoCheckRange(AActor *self, AActor *camera, double range, bool twodi) { if (camera == NULL) { @@ -2704,7 +2706,8 @@ static bool DoCheckRange(AActor *self, AActor *camera, double range) else{ dz = 0; } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range){ + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ // Within range return true; } @@ -2713,9 +2716,10 @@ static bool DoCheckRange(AActor *self, AActor *camera, double range) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); double range = EvalExpressionF(ParameterIndex+0, self); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! @@ -2725,13 +2729,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) if (playeringame[i]) { // Always check from each player. - if (DoCheckRange(self, players[i].mo, range)) + if (DoCheckRange(self, players[i].mo, range, twodi)) { return; } // If a player is viewing from a non-player, check that too. if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckRange(self, players[i].camera, range)) + DoCheckRange(self, players[i].camera, range, twodi)) { return; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index c32ecb958..8b1cf6a65 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -328,8 +328,8 @@ ACTOR Actor native //: Thinker action native A_SetRipMin(int min); action native A_SetRipMax(int max); - action native A_CheckSightOrRange(float distance, state label); - action native A_CheckRange(float distance, state label); + action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false); + action native A_CheckRange(float distance, state label, bool two_dimension = false); action native A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); action native A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); From 86986446a5d5bef67741accc5645fb90919931c5 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 1 Jan 2015 17:57:09 -0600 Subject: [PATCH 076/117] Fixed: TicSpecial could run out of space when writing data - Fixed: TicSpecial::CheckSpace() never thought it ran out of space due to unsigned math. - Fixed: TicSpecial::GetMoreSpace() assumed only relatively small amounts of data would be written at a time so made no effort to ensure it actually got enough space. This assumption could be violated by writing a very long string, which can happen when replicating a string cvar. --- src/d_net.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index ff70fdbf9..b3e70d410 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -185,7 +185,7 @@ static struct TicSpecial size_t used[BACKUPTICS]; BYTE *streamptr; size_t streamoffs; - int specialsize; + size_t specialsize; int lastmaketic; bool okay; @@ -224,11 +224,11 @@ static struct TicSpecial } // Make more room for special commands. - void GetMoreSpace () + void GetMoreSpace (size_t needed) { int i; - specialsize <<= 1; + specialsize = MAX(specialsize * 2, needed + 30); DPrintf ("Expanding special size to %d\n", specialsize); @@ -240,8 +240,8 @@ static struct TicSpecial void CheckSpace (size_t needed) { - if (streamoffs >= specialsize - needed) - GetMoreSpace (); + if (streamoffs + needed >= specialsize) + GetMoreSpace (streamoffs + needed); streamoffs += needed; } From aa1f51d3d2819e832a638618a564a339340c4c41 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 4 Jan 2015 14:08:53 +0200 Subject: [PATCH 077/117] Reimplemented gaming controllers support in native OS X backend using pure IOKit --- src/CMakeLists.txt | 8 - src/posix/cocoa/hid/HID_Config_Utilities.c | 926 -------------- src/posix/cocoa/hid/HID_Error_Handler.c | 108 -- src/posix/cocoa/hid/HID_Name_Lookup.c | 1210 ------------------ src/posix/cocoa/hid/HID_Queue_Utilities.c | 361 ------ src/posix/cocoa/hid/HID_Utilities.c | 1068 ---------------- src/posix/cocoa/hid/HID_Utilities_External.h | 417 ------ src/posix/cocoa/hid/IOHIDDevice_.c | 619 --------- src/posix/cocoa/hid/IOHIDDevice_.h | 422 ------ src/posix/cocoa/hid/IOHIDElement_.c | 509 -------- src/posix/cocoa/hid/IOHIDElement_.h | 339 ----- src/posix/cocoa/hid/IOHIDLib_.h | 111 -- src/posix/cocoa/hid/ImmrHIDUtilAddOn.c | 108 -- src/posix/cocoa/hid/ImmrHIDUtilAddOn.h | 50 - src/posix/cocoa/i_common.h | 4 +- src/posix/cocoa/i_joystick.cpp | 1124 +++++++++------- src/posix/cocoa/i_main.mm | 5 +- 17 files changed, 681 insertions(+), 6708 deletions(-) delete mode 100644 src/posix/cocoa/hid/HID_Config_Utilities.c delete mode 100644 src/posix/cocoa/hid/HID_Error_Handler.c delete mode 100644 src/posix/cocoa/hid/HID_Name_Lookup.c delete mode 100644 src/posix/cocoa/hid/HID_Queue_Utilities.c delete mode 100644 src/posix/cocoa/hid/HID_Utilities.c delete mode 100644 src/posix/cocoa/hid/HID_Utilities_External.h delete mode 100644 src/posix/cocoa/hid/IOHIDDevice_.c delete mode 100644 src/posix/cocoa/hid/IOHIDDevice_.h delete mode 100644 src/posix/cocoa/hid/IOHIDElement_.c delete mode 100644 src/posix/cocoa/hid/IOHIDElement_.h delete mode 100644 src/posix/cocoa/hid/IOHIDLib_.h delete mode 100644 src/posix/cocoa/hid/ImmrHIDUtilAddOn.c delete mode 100644 src/posix/cocoa/hid/ImmrHIDUtilAddOn.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 066a92c36..f6727da35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -566,14 +566,6 @@ set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm posix/osx/zdoom.icns ) set( PLAT_COCOA_SOURCES - posix/cocoa/hid/HID_Config_Utilities.c - posix/cocoa/hid/HID_Error_Handler.c - posix/cocoa/hid/HID_Name_Lookup.c - posix/cocoa/hid/HID_Queue_Utilities.c - posix/cocoa/hid/HID_Utilities.c - posix/cocoa/hid/IOHIDDevice_.c - posix/cocoa/hid/IOHIDElement_.c - posix/cocoa/hid/ImmrHIDUtilAddOn.c posix/cocoa/critsec.cpp posix/cocoa/i_input.mm posix/cocoa/i_joystick.cpp diff --git a/src/posix/cocoa/hid/HID_Config_Utilities.c b/src/posix/cocoa/hid/HID_Config_Utilities.c deleted file mode 100644 index 18d4672d3..000000000 --- a/src/posix/cocoa/hid/HID_Config_Utilities.c +++ /dev/null @@ -1,926 +0,0 @@ -// File: HID_Config_Utilities.c -// Abstract: Implementation of the HID configuration utilities -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#define LOG_SCORING 0 - -#include // malloc -#include // clock - -#include - -#include "HID_Utilities_External.h" - -// --------------------------------- - -// polls single device's elements for a change greater than kPercentMove. Times out after given time -// returns 1 and pointer to element if found -// returns 0 and NULL for both parameters if not found - -unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float timeout) { - if ( !inIOHIDDeviceRef ) { - return (0); - } - if ( 0 == HIDHaveDeviceList() ) { // if we do not have a device list - return (0); // return 0 - } - - Boolean found = FALSE; - - // build list of device and elements to save current values - int maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, kHIDElementTypeInput); - int *saveValueArray = (int *) calloc( maxElements, sizeof(int) ); // 2D array to save values - - // store initial values on first pass / compare to initial value on subsequent passes - Boolean first = TRUE; - - // get all the elements from this device - CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - // if that worked... - if ( elementCFArrayRef ) { - clock_t start = clock(), end; - - // poll all devices and elements - while ( !found ) { - int currElementIndex = 0; - CFIndex idx, cnt = CFArrayGetCount(elementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - *outIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); - if ( !*outIOHIDElementRef ) { - continue; - } - - // is this an input element? - IOHIDElementType type = IOHIDElementGetType(*outIOHIDElementRef); - - switch ( type ) { - // these types are inputs - case kIOHIDElementTypeInput_Misc: - case kIOHIDElementTypeInput_Button: - case kIOHIDElementTypeInput_Axis: - case kIOHIDElementTypeInput_ScanCodes: - default: - { - break; - } - case kIOHIDElementTypeOutput: - case kIOHIDElementTypeFeature: - case kIOHIDElementTypeCollection: - { - *outIOHIDElementRef = NULL; // these types are not ( Skip them ) - break; - } - } /* switch */ - if ( !*outIOHIDElementRef ) { - continue; // skip this element - } - - // get this elements current value - int value = 0; // default value is zero - IOHIDValueRef tIOHIDValueRef; - IOReturn ioReturn = IOHIDDeviceGetValue(inIOHIDDeviceRef, *outIOHIDElementRef, &tIOHIDValueRef); - if ( kIOReturnSuccess == ioReturn ) { - value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); - } - if ( first ) { - saveValueArray[currElementIndex] = value; - } else { - CFIndex min = IOHIDElementGetLogicalMin(*outIOHIDElementRef); - CFIndex max = IOHIDElementGetLogicalMax(*outIOHIDElementRef); - - int initialValue = saveValueArray[currElementIndex]; - int delta = (float)(max - min) * kPercentMove * 0.01; - // is the new value within +/- delta of the initial value? - if ( ( (initialValue + delta) < value ) || ( (initialValue - delta) > value ) ) { - found = 1; // ( yes! ) mark as found - break; - } - } // if ( first ) - - currElementIndex++; // bump element index - } // next idx - - first = FALSE; // no longer the first pass - - // are we done? - end = clock(); - double secs = (double)(end - start) / CLOCKS_PER_SEC; - if ( secs > timeout ) { - break; // ( yes ) timeout - } - } // while ( !found ) - - CFRelease(elementCFArrayRef); - } // if ( elementCFArrayRef ) - // return device and element moved - if ( found ) { - return (1); - } else { - *outIOHIDElementRef = NULL; - return (0); - } -} // HIDConfigureSingleDeviceAction - -//************************************************************************* -// -// HIDConfigureAction( outIOHIDDeviceRef, outIOHIDElementRef, inTimeout ) -// -// Purpose: polls all devices and elements for a change greater than kPercentMove. -// Times out after given time returns 1 and pointer to device and element -// if found; returns 0 and NULL for both parameters if not found -// -// Inputs: outIOHIDDeviceRef - address where to store the device -// outIOHIDElementRef - address where to store the element -// inTimeout - the timeout -// Returns: Boolean - if successful -// outIOHIDDeviceRef - the device -// outIOHIDElementRef - the element -// - -Boolean HIDConfigureAction(IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout) { - // param error? - if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) { - return (0); - } - if ( !gDeviceCFArrayRef ) { // if we do not have a device list - // and we can't build another list - if ( !HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef ) { - return (FALSE); // bail - } - } - - IOHIDDeviceRef tIOHIDDeviceRef; - IOHIDElementRef tIOHIDElementRef; - - // remember when we start; used to calculate timeout - clock_t start = clock(), end; - - // determine the maximum number of elements - CFIndex maxElements = 0; - CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); - for ( devIndex = 0; devIndex < devCount; devIndex++ ) { - tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); - if ( !tIOHIDDeviceRef ) { - continue; // skip this one - } - - CFIndex count = HIDCountDeviceElements(tIOHIDDeviceRef, kHIDElementTypeInput); - if ( count > maxElements ) { - maxElements = count; - } - } - - // allocate an array of int's in which to store devCount * maxElements values - double *saveValueArray = (double *) calloc( devCount * maxElements, sizeof(double) ); // clear 2D array to save values - - // on first pass store initial values / compare current values to initial values on subsequent passes - Boolean found = FALSE, first = TRUE; - - while ( !found ) { - double maxDeltaPercent = 0; // we want to find the one that moves the most ( percentage wise ) - for ( devIndex = 0; devIndex < devCount; devIndex++ ) { - tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); - if ( !tIOHIDDeviceRef ) { - continue; // skip this one - } - -#ifdef DEBUG - long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); - long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); -#endif - gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( gElementCFArrayRef ) { - CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); - for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { - tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); - if ( !tIOHIDElementRef ) { - continue; - } - - IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef); - // only care about inputs (no outputs or features) - if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) { - if ( IOHIDElementIsArray(tIOHIDElementRef) ) { - //printf( "ARRAY!\n" ); - continue; // skip array elements - } - - uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); - uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); - uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); -#ifdef DEBUG - if ( first ) { - IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); - printf("%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n", - __PRETTY_FUNCTION__, - tIOHIDDeviceRef, - vendorID, - productID, - tIOHIDElementRef, - cookie, - (long unsigned int) usagePage, - (long unsigned int) usage); - fflush(stdout); - if ( (0x054C == vendorID) && (0x0268 == productID) && (0x001E == (UInt32) cookie) ) { - //printf( "DING!\n" ); - } - } - -#endif -#if 1 // work-around for IOHIDValueGetScaledValue crash (when element report count > 1) - if ( reportCount > 1 ) { - //printf( "REPORT!\n" ); - continue; // skip reports - } - -#endif - // ignore PID elements and arrays - if ( (kHIDPage_PID != usagePage) && (((uint32_t)-1) != usage) ) { - // get this elements current value - double value = 0.0; // default value is zero - IOHIDValueRef tIOHIDValueRef; - IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef); - if ( kIOReturnSuccess == ioReturn ) { - value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); - } - if ( first ) { - saveValueArray[(devIndex * maxElements) + eleIndex] = value; - } else { - double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex]; - - CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef); - CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef); - - double deltaPercent = fabs( (initialValue - value) * 100.0 / (valueMax - valueMin) ); -#if 0 // debug code useful to dump out value info for specific (vendorID, productID, usagePage and usage) device - if ( !first ) { - // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, - // usage: 0x0001:0x0004, "Generic Desktop Joystick" - if ( (vendorID == 0x046D) && (productID == 0xC283) ) { - if ( (kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage) ) { - printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", - initialValue, - value, - fabs(initialValue - value), - deltaPercent); - } - } - } - - deltaPercent = 0.0; -#endif - if ( deltaPercent >= kPercentMove ) { - found = TRUE; - if ( deltaPercent > maxDeltaPercent ) { - maxDeltaPercent = deltaPercent; - - *outIOHIDDeviceRef = tIOHIDDeviceRef; - *outIOHIDElementRef = tIOHIDElementRef; - } - - break; - } - } // if first - - } // if usage - - } // if type - - } // for elements... - - CFRelease(gElementCFArrayRef); - gElementCFArrayRef = NULL; - } // if ( gElementCFArrayRef ) - if ( found ) { - // HIDDumpElementInfo( tIOHIDElementRef ); - break; // DONE! - } - } // for devices - - first = FALSE; // no longer the first pass - - // are we done? - end = clock(); - double secs = (double)(end - start) / CLOCKS_PER_SEC; - if ( secs > inTimeout ) { - break; // ( yes ) timeout - } - } // while ( !found ) - // return device and element moved - if ( !found ) { - *outIOHIDDeviceRef = NULL; - *outIOHIDElementRef = NULL; - } - - return (found); -} // HIDConfigureAction - -//************************************************************************* -// -// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inIOHIDDeviceRef, inIOHIDElementRef ) -// -// Purpose: Save the device & element values into the specified key in the specified applications preferences -// -// Inputs: inKeyCFStringRef - the preference key -// inAppCFStringRef - the application identifier -// inIOHIDDeviceRef - the device -// inIOHIDElementRef - the element -// Returns: Boolean - if successful -// - -Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, - CFStringRef inAppCFStringRef, - IOHIDDeviceRef inIOHIDDeviceRef, - IOHIDElementRef inIOHIDElementRef) { - Boolean success = FALSE; - if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) { - long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); - require(vendorID, Oops); - - long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); - require(productID, Oops); - - long locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); - require(locID, Oops); - - uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); - uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); - if ( !usagePage || !usage ) { - usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); - usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); - } - - require(usagePage && usage, Oops); - - uint32_t usagePageE = IOHIDElementGetUsagePage(inIOHIDElementRef); - uint32_t usageE = IOHIDElementGetUsage(inIOHIDElementRef); - IOHIDElementCookie eleCookie = IOHIDElementGetCookie(inIOHIDElementRef); - - CFStringRef prefCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}"), - vendorID, productID, locID, usagePage, usage, - usagePageE, usageE, eleCookie); - if ( prefCFStringRef ) { - CFPreferencesSetAppValue(inKeyCFStringRef, prefCFStringRef, inAppCFStringRef); - CFRelease(prefCFStringRef); - success = TRUE; - } - } - -Oops: ; - return (success); -} // HIDSaveElementPref - -//************************************************************************* -// -// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outIOHIDDeviceRef, outIOHIDElementRef ) -// -// Purpose: Find the specified preference in the specified application -// -// Inputs: inKeyCFStringRef - the preference key -// inAppCFStringRef - the application identifier -// outIOHIDDeviceRef - address where to restore the device -// outIOHIDElementRef - address where to restore the element -// Returns: Boolean - if successful -// outIOHIDDeviceRef - the device -// outIOHIDElementRef - the element -// - -Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef, - CFStringRef inAppCFStringRef, - IOHIDDeviceRef * outIOHIDDeviceRef, - IOHIDElementRef *outIOHIDElementRef) { - Boolean found = FALSE; - if ( inKeyCFStringRef && inAppCFStringRef && outIOHIDDeviceRef && outIOHIDElementRef ) { - CFPropertyListRef prefCFPropertyListRef = CFPreferencesCopyAppValue(inKeyCFStringRef, inAppCFStringRef); - if ( prefCFPropertyListRef ) { - if ( CFStringGetTypeID() == CFGetTypeID(prefCFPropertyListRef) ) { - char buffer[256]; - if ( CFStringGetCString( (CFStringRef) prefCFPropertyListRef, buffer, sizeof(buffer), - kCFStringEncodingUTF8 ) ) - { - HID_info_rec searchHIDInfo; - - int count = sscanf(buffer, - "d:{v:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%ld}", - &searchHIDInfo.device.vendorID, - &searchHIDInfo.device.productID, - &searchHIDInfo.device.locID, - &searchHIDInfo.device.usagePage, - &searchHIDInfo.device.usage, - &searchHIDInfo.element.usagePage, - &searchHIDInfo.element.usage, - (long *) &searchHIDInfo.element.cookie); - if ( 8 == count ) { // if we found all eight parameters… - // and can find a device & element that matches these… - if ( HIDFindDeviceAndElement(&searchHIDInfo, outIOHIDDeviceRef, outIOHIDElementRef) ) { - found = TRUE; - } - } - } - } else { - // We found the entry with this key but it's the wrong type; delete it. - CFPreferencesSetAppValue(inKeyCFStringRef, NULL, inAppCFStringRef); - (void) CFPreferencesAppSynchronize(inAppCFStringRef); - } - - CFRelease(prefCFPropertyListRef); - } - } - - return (found); -} // HIDRestoreElementPref - -//************************************************************************* -// -// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) -// -// Purpose: find the closest matching device and element for this action -// -// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage -// matches element: cookie, inUsagePage, usage, -// -// Inputs: inSearchInfo - the device & element info we searching for -// outFoundDevice - the address of the best matching device -// outFoundElement - the address of the best matching element -// -// Returns: Boolean - TRUE if we find a match -// outFoundDevice - the best matching device -// outFoundElement - the best matching element -// - -Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef *outFoundDevice, IOHIDElementRef *outFoundElement) { - Boolean result = FALSE; - - IOHIDDeviceRef bestIOHIDDeviceRef = NULL; - IOHIDElementRef bestIOHIDElementRef = NULL; - long bestScore = 0; - - CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); - for ( devIndex = 0; devIndex < devCount; devIndex++ ) { - long deviceScore = 1; - - IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); - if ( !tIOHIDDeviceRef ) { - continue; - } - // match vendorID, productID (+10, +8) - if ( inSearchInfo->device.vendorID ) { - long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); - if ( vendorID ) { - if ( inSearchInfo->device.vendorID == vendorID ) { - deviceScore += 10; - if ( inSearchInfo->device.productID ) { - long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); - if ( productID ) { - if ( inSearchInfo->device.productID == productID ) { - deviceScore += 8; - } // if ( inSearchInfo->device.productID == productID ) - - } // if ( productID ) - - } // if ( inSearchInfo->device.productID ) - - } // if (inSearchInfo->device.vendorID == vendorID) - - } // if vendorID - - } // if search->device.vendorID - // match usagePage & usage (+9) - if ( inSearchInfo->device.usagePage && inSearchInfo->device.usage ) { - uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef) ; - uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef); - if ( !usagePage || !usage ) { - usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); - usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); - } - if ( usagePage ) { - if ( inSearchInfo->device.usagePage == usagePage ) { - if ( usage ) { - if ( inSearchInfo->device.usage == usage ) { - deviceScore += 9; - } // if ( inSearchInfo->usage == usage ) - - } // if ( usage ) - - } // if ( inSearchInfo->usagePage == usagePage ) - - } // if ( usagePage ) - - } // if ( inSearchInfo->usagePage && inSearchInfo->usage ) - // match location ID (+5) - if ( inSearchInfo->device.locID ) { - long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); - if ( locID ) { - if ( inSearchInfo->device.locID == locID ) { - deviceScore += 5; - } - } - } - - // iterate over all elements of this device - gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); - if ( gElementCFArrayRef ) { - CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); - for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { - IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); - if ( !tIOHIDElementRef ) { - continue; - } - - long score = deviceScore; - // match usage page, usage & cookie - if ( inSearchInfo->element.usagePage && inSearchInfo->element.usage ) { - uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); - if ( inSearchInfo->element.usagePage == usagePage ) { - uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); - if ( inSearchInfo->element.usage == usage ) { - score += 5; - IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); - if ( inSearchInfo->element.cookie == cookie ) { - score += 4; - } // cookies match - - } else { - score = 0; - } // usages match - - } else { - score = 0; - } // usage pages match - - } // if ( search usage page & usage ) - -#if LOG_SCORING - if ( kHIDPage_KeyboardOrKeypad != tElementRef->usagePage ) { // skip keyboards here - printf("%s: ( %ld:%ld )-I-Debug, score: %ld\t", - __PRETTY_FUNCTION__, - inSearchInfo->element.usagePage, - inSearchInfo->element.usage, - score); - HIDPrintElement(tIOHIDElementRef); - } - -#endif // LOG_SCORING - if ( score > bestScore ) { - bestIOHIDDeviceRef = tIOHIDDeviceRef; - bestIOHIDElementRef = tIOHIDElementRef; - bestScore = score; -#if LOG_SCORING - printf("%s: ( %ld:%ld )-I-Debug, better score: %ld\t", - __PRETTY_FUNCTION__, - inSearchInfo->element.usagePage, - inSearchInfo->element.usage, - score); - HIDPrintElement(bestIOHIDElementRef); -#endif // LOG_SCORING - } - } // for elements... - - CFRelease(gElementCFArrayRef); - gElementCFArrayRef = NULL; - } // if ( gElementCFArrayRef ) - - } // for ( devIndex = 0; devIndex < devCount; devIndex++ ) - if ( bestIOHIDDeviceRef || bestIOHIDElementRef ) { - *outFoundDevice = bestIOHIDDeviceRef; - *outFoundElement = bestIOHIDElementRef; -#if LOG_SCORING - printf("%s: ( %ld:%ld )-I-Debug, best score: %ld\t", - __PRETTY_FUNCTION__, - inSearchInfo->element.usagePage, - inSearchInfo->element.usage, - bestScore); - HIDPrintElement(bestIOHIDElementRef); -#endif // LOG_SCORING - result = TRUE; - } - - return (result); -} // HIDFindDeviceAndElement - -// --------------------------------- - -// takes input records, save required info -// assume file is open and at correct position. -// will always write to file (if file exists) size of HID_info_rec, even if device and or element is bad - -void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) { - // must save: - // actionCookie - // Device: serial,vendorID, productID, location, usagePage, usage - // Element: cookie, usagePage, usage, - HID_info_rec hidInfoRec; - HIDSetElementConfig(&hidInfoRec, inIOHIDDeviceRef, inIOHIDElementRef, actionCookie); - // write to file - if ( fileRef ) { - fwrite( (void *)&hidInfoRec, sizeof(HID_info_rec), 1, fileRef ); - } -} // HIDSaveElementConfig - -// --------------------------------- - -// take file, read one record (assume file position is correct and file is open) -// search for matching device -// return pDevice, pElement and cookie for action - -int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { - // Device: serial,vendorID, productID, location, usagePage, usage - // Element: cookie, usagePage, usage, - - HID_info_rec hidInfoRec; - fread( (void *) &hidInfoRec, 1, sizeof(HID_info_rec), fileRef ); - return ( HIDGetElementConfig(&hidInfoRec, outIOHIDDeviceRef, outIOHIDElementRef) ); -} // HIDRestoreElementConfig - -// --------------------------------- - -// Set up a config record for saving -// takes an input records, returns record user can save as they want -// Note: the save rec must be pre-allocated by the calling app and will be filled out -void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, - IOHIDDeviceRef inIOHIDDeviceRef, - IOHIDElementRef inIOHIDElementRef, - int actionCookie) { - // must save: - // actionCookie - // Device: serial,vendorID, productID, location, usagePage, usage - // Element: cookie, usagePage, usage, - inHIDInfoPtr->actionCookie = actionCookie; - // device - // need to add serial number when I have a test case - if ( inIOHIDDeviceRef && inIOHIDElementRef ) { - inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); - inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); - inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); - inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); - inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); - - inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); - inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef); - inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); - inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); - inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef); - } else { - inHIDInfoPtr->device.vendorID = 0; - inHIDInfoPtr->device.productID = 0; - inHIDInfoPtr->device.locID = 0; - inHIDInfoPtr->device.usage = 0; - inHIDInfoPtr->device.usagePage = 0; - - inHIDInfoPtr->element.usagePage = 0; - inHIDInfoPtr->element.usage = 0; - inHIDInfoPtr->element.minReport = 0; - inHIDInfoPtr->element.maxReport = 0; - inHIDInfoPtr->element.cookie = 0; - } -} // HIDSetElementConfig - -// --------------------------------- -#if 0 // debug utility function to dump config record -void HIDDumpConfig(HID_info_ptr inHIDInfoPtr) { - printf( - "Config Record for action: %d\n\t vendor: %d product: %d location: %d\n\t usage: %d usagePage: %d\n\t element.usagePage: %d element.usage: %d\n\t minReport: %d maxReport: %d\n\t cookie: %d\n", - inHIDInfoPtr->actionCookie, - inHIDInfoPtr->device.vendorID, - inHIDInfoPtr->device.productID, - inHIDInfoPtr->locID, - inHIDInfoPtr->usage, - inHIDInfoPtr->usagePage, - inHIDInfoPtr->element.usagePage, - inHIDInfoPtr->element.usage, - inHIDInfoPtr->minReport, - inHIDInfoPtr->maxReport, - inHIDInfoPtr->cookie); -} // HIDDumpConfig -#endif // 0 -// --------------------------------- - -// Get matching element from config record -// takes a pre-allocated and filled out config record -// search for matching device -// return pDevice, pElement and cookie for action -int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { - if ( !inHIDInfoPtr->device.locID && !inHIDInfoPtr->device.vendorID && !inHIDInfoPtr->device.productID && !inHIDInfoPtr->device.usage - && !inHIDInfoPtr->device.usagePage ) // - { // - // early out - *outIOHIDDeviceRef = NULL; - *outIOHIDElementRef = NULL; - return (inHIDInfoPtr->actionCookie); - } - - IOHIDDeviceRef tIOHIDDeviceRef, foundIOHIDDeviceRef = NULL; - IOHIDElementRef tIOHIDElementRef, foundIOHIDElementRef = NULL; - - CFIndex devIdx, devCnt, idx, cnt; - // compare to current device list for matches - // look for device - if ( inHIDInfoPtr->device.locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) { // look for specific device - // type plug in to same port - devCnt = CFArrayGetCount(gDeviceCFArrayRef); - for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { - tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); - if ( !tIOHIDDeviceRef ) { - continue; // skip this device - } - - long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); - long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); - long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); - if ( (inHIDInfoPtr->device.locID == locID) - && (inHIDInfoPtr->device.vendorID == vendorID) - && (inHIDInfoPtr->device.productID == productID) ) - { - foundIOHIDDeviceRef = tIOHIDDeviceRef; - } - if ( foundIOHIDDeviceRef ) { - break; - } - } // next devIdx - if ( foundIOHIDDeviceRef ) { - CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( elementCFArrayRef ) { - cnt = CFArrayGetCount(elementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; // skip this element - } - - IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); - if ( inHIDInfoPtr->element.cookie == cookie ) { - foundIOHIDElementRef = tIOHIDElementRef; - } - if ( foundIOHIDElementRef ) { - break; - } - } - if ( !foundIOHIDElementRef ) { - cnt = CFArrayGetCount(elementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; // skip this element - } - - uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); - uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); - if ( (inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage) ) { - foundIOHIDElementRef = tIOHIDElementRef; - } - if ( foundIOHIDElementRef ) { - break; - } - } // next idx - - } // if ( !foundIOHIDElementRef ) - if ( foundIOHIDElementRef ) { // if same device - // setup the calibration - IOHIDElement_SetupCalibration(tIOHIDElementRef); - - IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); - IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); - } - - CFRelease(elementCFArrayRef); - } // if ( elementCFArrayRef ) - - } // if ( foundIOHIDDeviceRef ) - // if we have not found a match, look at just vendor and product - if ( (!foundIOHIDDeviceRef) && (inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID) ) { - devCnt = CFArrayGetCount(gDeviceCFArrayRef); - for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { - tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); - if ( !tIOHIDDeviceRef ) { - continue; // skip this device - } - - long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); - long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); - if ( (inHIDInfoPtr->device.vendorID == vendorID) - && (inHIDInfoPtr->device.productID == productID) ) - { - foundIOHIDDeviceRef = tIOHIDDeviceRef; - } - if ( foundIOHIDDeviceRef ) { - break; - } - } - // match elements by cookie since same device type - if ( foundIOHIDDeviceRef ) { - CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( elementCFArrayRef ) { - cnt = CFArrayGetCount(elementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; // skip this element - } - - IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); - if ( inHIDInfoPtr->element.cookie == cookie ) { - foundIOHIDElementRef = tIOHIDElementRef; - } - if ( foundIOHIDElementRef ) { - break; - } - } - // if no cookie match (should NOT occur) match on usage - if ( !foundIOHIDElementRef ) { - cnt = CFArrayGetCount(elementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; // skip this element - } - - uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); - uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); - if ( (inHIDInfoPtr->element.usage == usage) - && (inHIDInfoPtr->element.usagePage == usagePage) ) - { - foundIOHIDElementRef = tIOHIDElementRef; - } - if ( foundIOHIDElementRef ) { - break; - } - } // next idx - - } // if ( !foundIOHIDElementRef ) - if ( foundIOHIDElementRef ) { // if same device - // setup the calibration - IOHIDElement_SetupCalibration(tIOHIDElementRef); - IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); - IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); - } - - CFRelease(elementCFArrayRef); - } // if ( elementCFArrayRef ) - - } // if ( foundIOHIDDeviceRef ) - - } // if ( device not found & vendorID & productID ) - - } // if ( inHIDInfoPtr->locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) - // can't find matching device return NULL, do not return first device - if ( (!foundIOHIDDeviceRef) || (!foundIOHIDElementRef) ) { - // no HID device - *outIOHIDDeviceRef = NULL; - *outIOHIDElementRef = NULL; - return (inHIDInfoPtr->actionCookie); - } else { - // HID device - *outIOHIDDeviceRef = foundIOHIDDeviceRef; - *outIOHIDElementRef = foundIOHIDElementRef; - return (inHIDInfoPtr->actionCookie); - } -} // HIDGetElementConfig - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/HID_Error_Handler.c b/src/posix/cocoa/hid/HID_Error_Handler.c deleted file mode 100644 index 0802a2ca7..000000000 --- a/src/posix/cocoa/hid/HID_Error_Handler.c +++ /dev/null @@ -1,108 +0,0 @@ -// File: HID_Error_Handler.c -// Abstract: Implementation of the HID utilities error handlers -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#ifdef DEBUG // not used in release -#if !defined (kBuildingLibrary) -#define kVerboseErrors - -// system includes ---------------------------------------------------------- - -#ifdef kVerboseErrors -//#include -#endif -#endif // not kBuildingLibrary -#endif // DEBUG - -#include - -// project includes --------------------------------------------------------- - -#include "HID_Utilities_External.h" - -// globals (internal/private) ----------------------------------------------- - -// prototypes (internal/private) -------------------------------------------- - -// functions (internal/private) --------------------------------------------- - -#pragma mark - -// ------------------------------------- - -// central error reporting - -void HIDReportErrorNum(const char *strError, int numError) { - char errMsgCStr[256]; - - sprintf(errMsgCStr, "%s #%d (0x%x)", strError, numError, numError); - - // out as debug string -#ifdef kVerboseErrors - { - fputs(errMsgCStr, stderr); - } -#endif // kVerboseErrors -} // HIDReportErrorNum - -// ------------------------------------- - -void HIDReportError(const char *strError) { - char errMsgCStr[256]; - - sprintf(errMsgCStr, "%s", strError); - - // out as debug string -#ifdef kVerboseErrors - { - fputs(errMsgCStr, stderr); - } -#endif // kVerboseErrors -} // HIDReportError - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/HID_Name_Lookup.c b/src/posix/cocoa/hid/HID_Name_Lookup.c deleted file mode 100644 index c9a1ca264..000000000 --- a/src/posix/cocoa/hid/HID_Name_Lookup.c +++ /dev/null @@ -1,1210 +0,0 @@ -// File: HID_Name_Lookup.c -// Abstract: HID Name Lookup Utilities. -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#pragma mark - includes & imports -//***************************************************** -#include "HID_Utilities_External.h" -//***************************************************** -#pragma mark - typedefs, enums, defines, etc. -//***************************************************** -#define FAKE_MISSING_NAMES 0 // for debugging; returns the vendor, product & cookie ( or usage info ) as numbers. -#define VERBOSE_ELEMENT_NAMES 0 // set TRUE to include vender & product names in element names ( useful for debugging ) - -#define kNameKeyCFStringRef CFSTR("Name") -//***************************************************** -#pragma mark - local ( static ) function prototypes -//***************************************************** - -#if 0 // currently unused -static SInt32 hu_SaveToXMLFile(CFPropertyListRef inCFPRef, CFURLRef inCFURLRef); -static SInt32 hu_XMLSave(CFPropertyListRef inCFPropertyListRef, CFStringRef inResourceName, CFStringRef inResourceExtension); -#endif -static CFPropertyListRef hu_LoadFromXMLFile(CFURLRef inCFURLRef); -static CFPropertyListRef hu_XMLLoad(CFStringRef inResourceName, CFStringRef inResourceExtension); - -static Boolean hu_XMLSearchForElementNameByCookie(long inVendorID, long inProductID, IOHIDElementCookie inCookie, char *outCStr); -static Boolean hu_XMLSearchForElementNameByUsage(long inVendorID, long inProductID, long inUsagePage, long inUsage, char *outCStr); - -static Boolean hu_XMLSearchForVendorNameByVendorID(long inVendorID, char *outCStr); -static Boolean hu_XMLSearchForProductNameByVendorProductID(long inVendorID, long inProductID, char *outCStr); - -#if 0 // currently unused -static Boolean hu_AddVendorProductToCFDict(CFMutableDictionaryRef inCFMutableDictionaryRef, - long inVendorID, - CFStringRef inVendorCFStringRef, - long inProductID, - CFStringRef inProductCFStringRef); -static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef); -#endif -//***************************************************** -#pragma mark - exported globals -//***************************************************** -#pragma mark - local ( static ) globals -//***************************************************** -static CFPropertyListRef gCookieCFPropertyListRef = NULL; -static CFPropertyListRef gUsageCFPropertyListRef = NULL; - -//***************************************************** -#pragma mark - exported function implementations -//***************************************************** - -/************************************************************************* - * - * HIDGetVendorNameFromVendorID( inVendorID, inProductID, inCookie, outCStrName ) - * - * Purpose: Uses an devices vendor ID to generate a name for it. - * - * Notes: Now uses XML files to store dictionary of names - * - * Inputs: inVendorID - the elements vendor ID - * outCStrName - address where result will be returned - * Returns: Boolean - if successful - */ -Boolean HIDGetVendorNameFromVendorID(long inVendorID, char *outCStrName) { - Boolean result = FALSE; - *outCStrName = 0; // clear name - if ( hu_XMLSearchForVendorNameByVendorID(inVendorID, outCStrName) ) { - return (TRUE); - } - -#if FAKE_MISSING_NAMES - sprintf(outCStrName, "#{ V: %ld}#", inVendorID); - result = TRUE; -#endif // FAKE_MISSING_NAMES - return (result); -} // HIDGetVendorNameFromVendorID - -/************************************************************************* - * - * HIDGetProductNameFromVendorProductID( inVendorID, inProductID, outCStrName ) - * - * Purpose: Uses an elements vendor, product & usage info to generate a name for it. - * - * Notes: Now uses XML files to store dictionary of names - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inUsagePage - the elements usage page - * inUsage - the elements usage - * outCStrName - address where result will be returned - * Returns: Boolean - if successful - */ -Boolean HIDGetProductNameFromVendorProductID(long inVendorID, long inProductID, char *outCStrName) { - Boolean result = FALSE; - *outCStrName = 0; // clear name - if ( hu_XMLSearchForProductNameByVendorProductID(inVendorID, inProductID, outCStrName) ) { - return (TRUE); - } - -#if FAKE_MISSING_NAMES - sprintf(outCStrName, "#{ V: %ld, P: %ld, U: %ld: %ld}#", inVendorID, inProductID, inUsagePage, inUsage); - result = TRUE; -#endif // FAKE_MISSING_NAMES - return (result); -} // HIDGetProductNameFromVendorProductID - -/************************************************************************* - * - * HIDGetElementNameFromVendorProductCookie( inVendorID, inProductID, inCookie, outCStrName ) - * - * Purpose: Uses an elements vendor, product & cookie to generate a name for it. - * - * Notes: Now uses XML files to store dictionary of names - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inCookie - the elements cookie - * outCStrName - address where result will be returned - * Returns: Boolean - if successful - */ -Boolean HIDGetElementNameFromVendorProductCookie(int inVendorID, int inProductID, IOHIDElementCookie inCookie, char *outCStrName) { - Boolean result = FALSE; - *outCStrName = 0; // clear name - // Look in the XML file first - if ( hu_XMLSearchForElementNameByCookie(inVendorID, inProductID, inCookie, outCStrName) ) { - return (TRUE); - } - -#if FAKE_MISSING_NAMES - sprintf(outCStrName, "#{ V: %ld, P: %ld, C: %ld}#", inVendorID, inProductID, inCookie); -#else - result = FALSE; -#endif // FAKE_MISSING_NAMES - return (result); -} // HIDGetElementNameFromVendorProductCookie - -/************************************************************************* - * - * HIDGetElementNameFromVendorProductUsage( inVendorID, inProductID, inUsagePage, inUsage, outCStrName ) - * - * Purpose: Uses an elements vendor, product & usage info to generate a name for it. - * - * Notes: Now uses XML files to store dictionary of names - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inUsagePage - the elements usage page - * inUsage - the elements usage - * outCStrName - address where result will be returned - * Returns: Boolean - if successful - */ -Boolean HIDGetElementNameFromVendorProductUsage(long inVendorID, - long inProductID, - long inUsagePage, - long inUsage, - char *outCStrName) { - Boolean result = FALSE; - *outCStrName = 0; // clear name - if ( hu_XMLSearchForElementNameByUsage(inVendorID, inProductID, inUsagePage, inUsage, outCStrName) ) { - return (TRUE); - } - -#if FAKE_MISSING_NAMES - sprintf(outCStrName, "#{ V: %ld, P: %ld, U: %ld: %ld}#", inVendorID, inProductID, inUsagePage, inUsage); - result = TRUE; -#endif // FAKE_MISSING_NAMES - return (result); -} // HIDGetElementNameFromVendorProductUsage - -#if 0 // currently unused -/************************************************************************* - * - * HIDAddDeviceToXML( inDevice ) - * - * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file - * - * Inputs: inDevice - the device - * Returns: Boolean - if successful - */ -static Boolean HIDAddDeviceToXML(IOHIDDeviceRef inIOHIDDeviceRef) { - Boolean result = FALSE; - if ( HIDIsValidDevice(inIOHIDDeviceRef) ) { - CFStringRef vendorCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); - CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); - if ( vendorCFStringRef && productCFStringRef ) { -#if 0 // don't update the cookie xml file - gCookieCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_cookie_strings"), CFSTR("plist") ); - if ( gCookieCFPropertyListRef ) { - CFMutableDictionaryRef tCFMutableDictionaryRef = - CFDictionaryCreateMutableCopy( - kCFAllocatorDefault, - 0, - gCookieCFPropertyListRef); - if ( tCFMutableDictionaryRef ) { - if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, - productCFStringRef) ) - { - hu_XMLSave( tCFMutableDictionaryRef, - CFSTR( - "HID_cookie_strings"), CFSTR("plist") ); - result = TRUE; - } - - CFRelease(tCFMutableDictionaryRef); - } - } - -#endif - if ( gUsageCFPropertyListRef ) { - CFRelease(gUsageCFPropertyListRef); - } - - gUsageCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - if ( gUsageCFPropertyListRef ) { - CFMutableDictionaryRef tCFMutableDictionaryRef = - CFDictionaryCreateMutableCopy( - kCFAllocatorDefault, - 0, - gUsageCFPropertyListRef); - if ( tCFMutableDictionaryRef ) { - long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); - long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); - if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, - productCFStringRef) ) - { - hu_XMLSave( tCFMutableDictionaryRef, - CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - result = TRUE; - } - - CFRelease(tCFMutableDictionaryRef); - } - } - } - } - - return (result); -} // HIDAddDeviceToXML - -/************************************************************************* - * - * HIDAddDeviceElementToXML( inDevice, inElement ) - * - * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file - * - * Inputs: inDevice - the device - * inElement - the element - * - * Returns: Boolean - if successful - */ -Boolean HIDAddDeviceElementToXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { - Boolean result = FALSE; - if ( HIDIsValidElement(inIOHIDElementRef) ) { - if ( HIDAddDeviceToXML(inIOHIDDeviceRef) ) { - result = TRUE; - } - if ( hu_AddDeviceElementToUsageXML(inIOHIDDeviceRef, inIOHIDElementRef) ) { - result = TRUE; - } - } - - return (result); -} // HIDAddDeviceElementToXML -#endif -/************************************************************************* - * - * HIDGetTypeName( inIOHIDElementType, outCStrName ) - * - * Purpose: return a C string for a given element type( see IOHIDKeys.h ) - * Notes: returns "Unknown Type" for invalid types - * - * Inputs: inIOHIDElementType - type element type - * outCStrName - address where to store element type string - * - * Returns: outCStrName - the element type string - */ - -void HIDGetTypeName(IOHIDElementType inIOHIDElementType, char *outCStrName) { - switch ( inIOHIDElementType ) { - case kIOHIDElementTypeInput_Misc: - { - sprintf(outCStrName, "Miscellaneous Input"); - break; - } - - case kIOHIDElementTypeInput_Button: - { - sprintf(outCStrName, "Button Input"); - break; - } - - case kIOHIDElementTypeInput_Axis: - { - sprintf(outCStrName, "Axis Input"); - break; - } - - case kIOHIDElementTypeInput_ScanCodes: - { - sprintf(outCStrName, "Scan Code Input"); - break; - } - - case kIOHIDElementTypeOutput: - { - sprintf(outCStrName, "Output"); - break; - } - - case kIOHIDElementTypeFeature: - { - sprintf(outCStrName, "Feature"); - break; - } - - case kIOHIDElementTypeCollection: - { - sprintf(outCStrName, "Collection"); - break; - } - - default: - { - sprintf(outCStrName, "Unknown Type"); - break; - } - } // switch - -} // HIDGetTypeName - -//************************************************************************* -// -// HIDCopyUsageName( inUsagePage, inUsage ) -// -// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h ) -// -// Notes: returns usage page and usage values in CFString form for unknown values -// -// Inputs: inUsagePage - the usage page -// inUsage - the usage -// -// Returns: CFStringRef - the resultant string -// - -CFStringRef HIDCopyUsageName(long inUsagePage, long inUsage) { - static CFPropertyListRef tCFPropertyListRef = NULL; - CFStringRef result = NULL; - if ( !tCFPropertyListRef ) { - tCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_usage_strings"), CFSTR("plist") ); - } - if ( tCFPropertyListRef ) { - if ( - CFDictionaryGetTypeID() == CFGetTypeID(tCFPropertyListRef) ) - { - CFStringRef pageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%4.4lX"), inUsagePage); - if ( pageKeyCFStringRef ) { - CFDictionaryRef pageCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(tCFPropertyListRef, pageKeyCFStringRef, - (const void **) &pageCFDictionaryRef) ) - { - CFStringRef pageCFStringRef; - if ( CFDictionaryGetValueIfPresent(pageCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &pageCFStringRef) ) - { - CFStringRef usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "0x%4.4lX"), inUsage); - if ( usageKeyCFStringRef ) { - CFStringRef usageCFStringRef; - if ( CFDictionaryGetValueIfPresent(pageCFDictionaryRef, usageKeyCFStringRef, - (const void **) &usageCFStringRef) ) - { - result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@"), pageCFStringRef, usageCFStringRef); - } - -#if FAKE_MISSING_NAMES - else { - result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ #%@"), pageCFStringRef, usageKeyCFStringRef); - } -#endif - CFRelease(usageKeyCFStringRef); - } - } else { - // no name data for this page - } - } else { - // no data for this page - } - - CFRelease(pageKeyCFStringRef); - } - } - - // CFRelease( tCFPropertyListRef ); // Leak this! - // tCFPropertyListRef = NULL; - } - - return (result); -} // HIDCopyUsageName - -//***************************************************** -#pragma mark - local ( static ) function implementations -//***************************************************** -#if 0 // currently unused -/************************************************************************* - * - * hu_SaveToXMLFile( inCFPRef, inCFURLRef ) - * - * Purpose: save a property list into an XML file - * - * Inputs: inCFPRef - the data - * inCFURLRef - URL for the file - * - * Returns: SInt32 - error code ( if any ) - */ -static SInt32 hu_SaveToXMLFile(CFPropertyListRef inCFPRef, CFURLRef inCFURLRef) { - CFDataRef xmlCFDataRef; - SInt32 error = coreFoundationUnknownErr; - - // Convert the property list into XML data. - xmlCFDataRef = CFPropertyListCreateXMLData(kCFAllocatorDefault, inCFPRef); - if ( xmlCFDataRef ) { - // Write the XML data to the file. - (void) CFURLWriteDataAndPropertiesToResource(inCFURLRef, xmlCFDataRef, NULL, &error); - - // Release the XML data - CFRelease(xmlCFDataRef); - } - - return (error); -} // hu_SaveToXMLFile -#endif -/************************************************************************* - * - * hu_LoadFromXMLFile( inCFURLRef ) - * - * Purpose: load a property list from an XML file - * - * Inputs: inCFURLRef - URL for the file - * - * Returns: CFPropertyListRef - the data - */ -static CFPropertyListRef hu_LoadFromXMLFile(CFURLRef inCFURLRef) { - CFDataRef xmlCFDataRef; - CFPropertyListRef myCFPropertyListRef = NULL; - - // Read the XML file. - SInt32 error; - if ( CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, inCFURLRef, &xmlCFDataRef, NULL, NULL, &error) ) { - CFStringRef errorString; - // Reconstitute the dictionary using the XML data. - myCFPropertyListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, - xmlCFDataRef, - kCFPropertyListImmutable, - &errorString); - // Release the XML data - CFRelease(xmlCFDataRef); - } - - return (myCFPropertyListRef); -} // hu_LoadFromXMLFile - -#if 0 // currently unused -/************************************************************************* - * - * hu_XMLSave( inCFPropertyListRef, inResourceName, inResourceExtension ) - * - * Purpose: save a CFPropertyListRef into a resource( XML ) file - * - * Inputs: inCFPropertyListRef - the data - * inResourceName - name of the resource file - * inResourceExtension - extension of the resource file - * - * Returns: SInt32 - error code ( if any ) - */ -static SInt32 hu_XMLSave(CFPropertyListRef inCFPropertyListRef, CFStringRef inResourceName, CFStringRef inResourceExtension) { - CFURLRef resFileCFURLRef; - SInt32 error = -1; - - // check the main (application) bundle - resFileCFURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), inResourceName, inResourceExtension, NULL); - - if ( !resFileCFURLRef ) { - // check this specific (HID_Utilities framework) bundle - CFBundleRef tCFBundleRef = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HID_Utilities")); - if ( tCFBundleRef ) { - resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); - } - } - if ( !resFileCFURLRef ) { - // check bundles already loaded or otherwise known to the current process - CFArrayRef tCFArrayRef = CFBundleGetAllBundles(); - CFIndex idx, cnt = CFArrayGetCount(tCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - CFBundleRef tCFBundleRef = (CFBundleRef) CFArrayGetValueAtIndex(tCFArrayRef, idx) ; - if ( tCFBundleRef ) { - resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); - if ( resFileCFURLRef ) { - break; - } - } - } - } - if ( resFileCFURLRef ) { - error = hu_SaveToXMLFile(inCFPropertyListRef, resFileCFURLRef); - CFRelease(resFileCFURLRef); - } - - return (error); -} // hu_XMLSave -#endif -/************************************************************************* - * - * hu_XMLLoad( inResourceName, inResourceExtension ) - * - * Purpose: Load a resource( XML ) file into a CFPropertyListRef - * - * Inputs: inResourceName - name of the resource file - * inResourceExtension - extension of the resource file - * - * Returns: CFPropertyListRef - the data - */ -static CFPropertyListRef hu_XMLLoad(CFStringRef inResourceName, CFStringRef inResourceExtension) { - CFURLRef resFileCFURLRef; - CFPropertyListRef tCFPropertyListRef = NULL; - - // check the main (application) bundle - resFileCFURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), inResourceName, inResourceExtension, NULL); - - if ( !resFileCFURLRef ) { - // check this specific (HID_Utilities framework) bundle - CFBundleRef tCFBundleRef = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HID_Utilities")); - if ( tCFBundleRef ) { - resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); - } - } - if ( !resFileCFURLRef ) { - // check bundles already loaded or otherwise known to the current process - CFArrayRef tCFArrayRef = CFBundleGetAllBundles(); - CFIndex idx, cnt = CFArrayGetCount(tCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - CFBundleRef tCFBundleRef = (CFBundleRef) CFArrayGetValueAtIndex(tCFArrayRef, idx) ; - if ( tCFBundleRef ) { - resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); - if ( resFileCFURLRef ) { - break; - } - } - } - } - if ( resFileCFURLRef ) { - tCFPropertyListRef = hu_LoadFromXMLFile(resFileCFURLRef); - CFRelease(resFileCFURLRef); - } - - return (tCFPropertyListRef); -} // hu_XMLLoad - -/************************************************************************* - * - * hu_XMLSearchForVendorNameByVendorID( inVendorID, outCStr ) - * - * Purpose: Find a vendor string in the resource ( XML ) file - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * outCStr - address where result will be returned - * - * Returns: Boolean - if successful - */ -static Boolean hu_XMLSearchForVendorNameByVendorID(long inVendorID, char *outCStr) { - Boolean results = FALSE; - if ( !gUsageCFPropertyListRef ) { - gUsageCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - } - if ( gUsageCFPropertyListRef ) { - if ( - CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) - { - CFDictionaryRef vendorCFDictionaryRef; - CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); - if ( vendorKeyCFStringRef ) { - if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, - (const void **) &vendorCFDictionaryRef) ) - { - CFStringRef vendorCFStringRef = NULL; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &vendorCFStringRef) && vendorCFStringRef ) - { - // CFShow( vendorCFStringRef ); - results = - CFStringGetCString(vendorCFStringRef, outCStr, CFStringGetLength( - vendorCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); - } - } - - CFRelease(vendorKeyCFStringRef); - } - } - - // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! - } - - return (results); -} // hu_XMLSearchForVendorNameByVendorID - -/************************************************************************* - * - * hu_XMLSearchForProductNameByVendorProductID( inVendorID, inProductID, outCStr ) - * - * Purpose: Find an product string in the resource ( XML ) file - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * outCStr - address where result will be returned - * - * Returns: Boolean - if successful - */ -static Boolean hu_XMLSearchForProductNameByVendorProductID(long inVendorID, long inProductID, char *outCStr) { - Boolean results = FALSE; - if ( !gUsageCFPropertyListRef ) { - gUsageCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - } - if ( gUsageCFPropertyListRef ) { - if ( - CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) - { - // first we make our vendor ID key - CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); - if ( vendorKeyCFStringRef ) { - // and use it to look up our vendor dictionary - CFDictionaryRef vendorCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, - (const void **) &vendorCFDictionaryRef) ) - { - // pull our vendor name our of that dictionary - CFStringRef vendorCFStringRef = NULL; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &vendorCFStringRef) ) - { -#if FAKE_MISSING_NAMES - CFRetain(vendorCFStringRef); // so we can CFRelease it later - } else { - vendorCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "V: %@"), vendorKeyCFStringRef); -#endif - } - - // now we make our product ID key - CFStringRef productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%ld"), inProductID); - if ( productKeyCFStringRef ) { - // and use that key to look up our product dictionary in the vendor dictionary - CFDictionaryRef productCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, - (const void **) &productCFDictionaryRef) ) - { - // pull our product name our of the product dictionary - CFStringRef productCFStringRef = NULL; - if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &productCFStringRef) ) - { -#if FAKE_MISSING_NAMES - CFRetain(productCFStringRef); // so we can CFRelease it later - } else { - productCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "P: %@"), kNameKeyCFStringRef); -#endif - } - - CFStringRef fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@"), vendorCFStringRef, - productCFStringRef); - if ( fullCFStringRef ) { - // CFShow( fullCFStringRef ); - results = - CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( - fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); - CFRelease(fullCFStringRef); - } - -#if FAKE_MISSING_NAMES - if ( productCFStringRef ) { - CFRelease(productCFStringRef); - } - -#endif - } - - CFRelease(productKeyCFStringRef); - } - -#if FAKE_MISSING_NAMES - if ( vendorCFStringRef ) { - CFRelease(vendorCFStringRef); - } - -#endif - } - - CFRelease(vendorKeyCFStringRef); - } - } - - // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! - } - - return (results); -} // hu_XMLSearchForProductNameByVendorProductID - -/************************************************************************* - * - * hu_XMLSearchForElementNameByCookie( inVendorID, inProductID, inCookie, outCStr ) - * - * Purpose: Find an element string in the resource( XML ) file - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inCookie - the elements cookie - * outCStr - address where result will be returned - * - * Returns: Boolean - if successful - */ -static Boolean hu_XMLSearchForElementNameByCookie(long inVendorID, long inProductID, IOHIDElementCookie inCookie, char *outCStr) { - Boolean results = FALSE; - if ( !gCookieCFPropertyListRef ) { - gCookieCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_cookie_strings"), CFSTR("plist") ); - } - if ( gCookieCFPropertyListRef ) { - if ( - CFDictionaryGetTypeID() == CFGetTypeID(gCookieCFPropertyListRef) ) - { - CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); - if ( vendorKeyCFStringRef ) { - CFDictionaryRef vendorCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(gCookieCFPropertyListRef, vendorKeyCFStringRef, - (const void **) &vendorCFDictionaryRef) ) - { - CFDictionaryRef productCFDictionaryRef; - CFStringRef productKeyCFStringRef; - CFStringRef vendorCFStringRef; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &vendorCFStringRef) ) - { - // CFShow( vendorCFStringRef ); - } - - productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inProductID); - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, - (const void **) &productCFDictionaryRef) ) - { - CFStringRef fullCFStringRef = NULL; - CFStringRef cookieKeyCFStringRef; - CFStringRef productCFStringRef; - CFStringRef cookieCFStringRef; - if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &productCFStringRef) ) - { - // CFShow( productCFStringRef ); - } - - cookieKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inCookie); - if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, cookieKeyCFStringRef, - (const void **) &cookieCFStringRef) ) - { -#if VERBOSE_ELEMENT_NAMES - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@ %@"), vendorCFStringRef, productCFStringRef, - cookieCFStringRef); -#else - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), cookieCFStringRef); -#endif // VERBOSE_ELEMENT_NAMES - // CFShow( cookieCFStringRef ); - } - -#if FAKE_MISSING_NAMES - else { - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@ # %@"), vendorCFStringRef, productCFStringRef, - cookieKeyCFStringRef); - } -#endif // FAKE_MISSING_NAMES - if ( fullCFStringRef ) { - // CFShow( fullCFStringRef ); - results = - CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( - fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); - CFRelease(fullCFStringRef); - } - - CFRelease(cookieKeyCFStringRef); - } - - CFRelease(productKeyCFStringRef); - } - - CFRelease(vendorKeyCFStringRef); - } - } - - // ++ CFRelease( gCookieCFPropertyListRef ); // Leak this ! - } - - return (results); -} // hu_XMLSearchForElementNameByCookie - -/************************************************************************* - * - * hu_XMLSearchForElementNameByUsage( inVendorID, inProductID, inUsagePage, inUsage, outCStr ) - * - * Purpose: Find an element string in the resource( XML ) file - * - * Inputs: inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inUsagePage - the elements usage page - * inUsage - the elements usage - * outCStr - address where result will be returned - * - * Returns: Boolean - if successful - */ -static Boolean hu_XMLSearchForElementNameByUsage(long inVendorID, long inProductID, long inUsagePage, long inUsage, - char *outCStr) { - Boolean results = FALSE; - if ( !gUsageCFPropertyListRef ) { - gUsageCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - } - if ( gUsageCFPropertyListRef ) { - if ( - CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) - { - CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); - if ( vendorKeyCFStringRef ) { - CFDictionaryRef vendorCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, - (const void **) &vendorCFDictionaryRef) ) - { - CFStringRef vendorCFStringRef = NULL; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &vendorCFStringRef) ) - { - vendorCFStringRef = CFStringCreateCopy(kCFAllocatorDefault, vendorCFStringRef); - } else { - vendorCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("v: %ld"), inVendorID); - // CFShow( vendorCFStringRef ); - } - - CFStringRef productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%ld"), inProductID); - - CFDictionaryRef productCFDictionaryRef; - if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, - (const void **) &productCFDictionaryRef) ) - { - CFStringRef fullCFStringRef = NULL; - - CFStringRef productCFStringRef; - if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, - (const void **) &productCFStringRef) ) - { - // CFShow( productCFStringRef ); - } - - CFStringRef usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%ld:%ld"), inUsagePage, inUsage); - CFStringRef usageCFStringRef; - if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, usageKeyCFStringRef, - (const void **) &usageCFStringRef) ) - { -#if VERBOSE_ELEMENT_NAMES - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@ %@"), vendorCFStringRef, productCFStringRef, - usageCFStringRef); -#else - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), usageCFStringRef); -#endif // VERBOSE_ELEMENT_NAMES - // CFShow( usageCFStringRef ); - } - -#if FAKE_MISSING_NAMES - else { - fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( - "%@ %@ # %@"), vendorCFStringRef, productCFStringRef, - usageKeyCFStringRef); - } -#endif // FAKE_MISSING_NAMES - if ( fullCFStringRef ) { - // CFShow( fullCFStringRef ); - results = - CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( - fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); - CFRelease(fullCFStringRef); - } - - CFRelease(usageKeyCFStringRef); - } - if ( vendorCFStringRef ) { - CFRelease(vendorCFStringRef); - } - - CFRelease(productKeyCFStringRef); - } - - CFRelease(vendorKeyCFStringRef); - } - } - - // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! - } - - return (results); -} // hu_XMLSearchForElementNameByUsage - -#if 0 // currently unused -/************************************************************************* - * - * hu_AddVendorProductToCFDict( inCFMutableDictionaryRef, inVendorID, inVendorCFStringRef, inProductID, inProductCFStringRef ) - * - * Purpose: add a vendor & product to a dictionary - * - * Inputs: inCFMutableDictionaryRef - the dictionary - * inVendorID - the elements vendor ID - * inProductID - the elements product ID - * inProductCFStringRef - the string to be added - * - * Returns: Boolean - if successful - */ -static Boolean hu_AddVendorProductToCFDict(CFMutableDictionaryRef inCFMutableDictionaryRef, - long inVendorID, - CFStringRef inVendorCFStringRef, - long inProductID, - CFStringRef inProductCFStringRef) { - Boolean results = FALSE; - if ( inCFMutableDictionaryRef && ( CFDictionaryGetTypeID() == CFGetTypeID(inCFMutableDictionaryRef) ) ) { - CFMutableDictionaryRef vendorCFMutableDictionaryRef; - CFStringRef vendorKeyCFStringRef; - - CFMutableDictionaryRef productCFMutableDictionaryRef; - CFStringRef productKeyCFStringRef; - - // if the vendor dictionary doesn't exist - vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); - if ( CFDictionaryGetValueIfPresent(inCFMutableDictionaryRef, vendorKeyCFStringRef, - (const void **) &vendorCFMutableDictionaryRef) ) - { - // copy it. - vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vendorCFMutableDictionaryRef); - } else { // ...otherwise... - // create it. - vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - results = TRUE; - } - // if the vendor name key doesn't exist - if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) { - // create it. - CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, inVendorCFStringRef); - results = TRUE; - } - - // if the product key exists in the vendor dictionary - productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inProductID); - if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef, - (const void **) &productCFMutableDictionaryRef) ) - { - // copy it. - productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, productCFMutableDictionaryRef); - } else { // ...otherwise... - // create it. - productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - results = TRUE; - } - // if the product name key doesn't exist - if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) { - // create it. - CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, inProductCFStringRef); - results = TRUE; - } - if ( vendorCFMutableDictionaryRef ) { - if ( productCFMutableDictionaryRef ) { - if ( results ) { - CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef); - } - - CFRelease(productCFMutableDictionaryRef); - } - if ( results ) { - CFDictionarySetValue(inCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef); - } - - CFRelease(vendorCFMutableDictionaryRef); - } - if ( productKeyCFStringRef ) { - CFRelease(productKeyCFStringRef); - } - if ( vendorKeyCFStringRef ) { - CFRelease(vendorKeyCFStringRef); - } - } - - return (results); -} // hu_AddVendorProductToCFDict - -/************************************************************************* - * - * hu_AddDeviceElementToUsageXML( inDevice, inElement ) - * - * Purpose: add a device and it's elements to our usage( XML ) file - * - * Inputs: inDevice - the device - * inElement - the element - * - * Returns: Boolean - if successful - */ -static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { - Boolean results = FALSE; - if ( gUsageCFPropertyListRef ) { - CFRelease(gUsageCFPropertyListRef); - } - - gUsageCFPropertyListRef = - hu_XMLLoad( CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - if ( gUsageCFPropertyListRef ) { - CFMutableDictionaryRef tCFMutableDictionaryRef = - CFDictionaryCreateMutableCopy( - kCFAllocatorDefault, - 0, - gUsageCFPropertyListRef); - if ( tCFMutableDictionaryRef ) { - CFMutableDictionaryRef vendorCFMutableDictionaryRef; - - CFMutableDictionaryRef productCFMutableDictionaryRef; - CFStringRef productKeyCFStringRef; - - CFStringRef usageKeyCFStringRef; - - // if the vendor dictionary exists... - long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); - CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), vendorID); - if ( vendorKeyCFStringRef ) { - if ( CFDictionaryGetValueIfPresent(tCFMutableDictionaryRef, vendorKeyCFStringRef, - (const void **) &vendorCFMutableDictionaryRef) ) - { - // ...copy it... - vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, - 0, - vendorCFMutableDictionaryRef); - } else { // ...otherwise... - // ...create it. - vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - results = TRUE; - } - // if the vendor name key doesn't exist... - if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) { - CFStringRef manCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); - // ...create it. - CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, manCFStringRef); - results = TRUE; - } - - // if the product key exists in the vendor dictionary... - long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); - productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), productID); - if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef, - (const void **) &productCFMutableDictionaryRef) ) - { - // ...copy it... - productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, - 0, - productCFMutableDictionaryRef); - } else { // ...otherwise... - // ...create it. - productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - results = TRUE; - } - // if the product name key doesn't exist... - if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) { - CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); - // ...create it. - CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, productCFStringRef); - results = TRUE; - } - - // if the usage key doesn't exist in the product dictionary... - uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); - uint32_t usage = IOHIDElementGetUsagePage(inIOHIDElementRef); - usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld:%ld"), usagePage, usage); - if ( usageKeyCFStringRef ) { - if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, usageKeyCFStringRef) ) { - // find it's generic name - CFStringRef usageCFStringRef = HIDCopyUsageName(usagePage, usage); - if ( usageCFStringRef ) { - // and add that. - CFDictionaryAddValue(productCFMutableDictionaryRef, usageKeyCFStringRef, usageCFStringRef); - results = TRUE; - CFRelease(usageCFStringRef); - } - } - - CFRelease(usageKeyCFStringRef); - } - if ( vendorCFMutableDictionaryRef ) { - if ( productCFMutableDictionaryRef ) { - if ( results ) { - CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef); - } - - CFRelease(productCFMutableDictionaryRef); - } - if ( results ) { - CFDictionarySetValue(tCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef); - } - - CFRelease(vendorCFMutableDictionaryRef); - } - - CFRelease(vendorKeyCFStringRef); - } - if ( productKeyCFStringRef ) { - CFRelease(productKeyCFStringRef); - } - if ( results ) { - hu_XMLSave( tCFMutableDictionaryRef, - CFSTR( - "HID_device_usage_strings"), CFSTR("plist") ); - } - - CFRelease( - tCFMutableDictionaryRef); - } - } - - return (results); -} // hu_AddDeviceElementToUsageXML -#endif - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/HID_Queue_Utilities.c b/src/posix/cocoa/hid/HID_Queue_Utilities.c deleted file mode 100644 index ab1d83667..000000000 --- a/src/posix/cocoa/hid/HID_Queue_Utilities.c +++ /dev/null @@ -1,361 +0,0 @@ -// File: HID_Queue_Utilities.c -// Abstract: HID Queue Utilities. -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#include "HID_Utilities_External.h" - -// ================================== -// private functions - -// creates a queue for a device, creates and opens device interface if required -static IOReturn HIDCreateQueue(IOHIDDeviceRef inIOHIDDeviceRef) { - IOReturn result = kIOReturnSuccess; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - - // do we already have a queue? - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { // (yes) - assert( IOHIDQueueGetTypeID() == CFGetTypeID(tIOHIDQueueRef) ); - } else { - tIOHIDQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, inIOHIDDeviceRef, kDeviceQueueSize, kIOHIDOptionsTypeNone); - if ( tIOHIDQueueRef ) { // did that work - IOHIDDevice_SetQueue(inIOHIDDeviceRef, tIOHIDQueueRef); - result = kIOReturnSuccess; - } else { - HIDReportErrorNum("Failed to create queue via create", result); - } - } - } else { - HIDReportErrorNum("HID device ref does not exist for queue creation", result); - } - - return (result); -} /* HIDCreateQueue */ - -// --------------------------------- -// returns true if queue is empty false otherwise -// error if no device, empty if no queue -static unsigned char HIDIsDeviceQueueEmpty(IOHIDDeviceRef inIOHIDDeviceRef) { - if ( inIOHIDDeviceRef ) { // need device and queue - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { - IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); - - while ( tIOHIDElementRef ) { - if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { - return (false); - } - - tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); - } - } else { - HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); - } - } else { - HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); - } - - return (true); -} /* HIDIsDeviceQueueEmpty */ - -// --------------------------------- - -// disposes and releases queue, sets queue to NULL,. -// Note: will have no effect if device or queue do not exist -static IOReturn HIDDisposeReleaseQueue(IOHIDDeviceRef inIOHIDDeviceRef) { - IOReturn result = kIOReturnSuccess; - if ( inIOHIDDeviceRef ) { - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { - // stop queue - IOHIDQueueStop(tIOHIDQueueRef); - - // release the queue - CFRelease(tIOHIDQueueRef); - } - } else { - HIDReportError("NULL device passed to HIDDisposeReleaseQueue."); - } - - return (result); -} /* HIDDisposeReleaseQueue */ - -// ================================== -// public functions -// ---------------------------------- - -// queues specific element, performing any device queue set up required -// queue is started and ready to return events on exit from this function -int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { - IOReturn result = kIOReturnSuccess; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - if ( inIOHIDElementRef ) { - assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( !tIOHIDQueueRef ) { // if no queue create queue - result = HIDCreateQueue(inIOHIDDeviceRef); - if ( kIOReturnSuccess == result ) { - tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - } - } - if ( tIOHIDQueueRef ) { - // stop queue - IOHIDQueueStop(tIOHIDQueueRef); - // queue element - if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { - IOHIDQueueAddElement(tIOHIDQueueRef, inIOHIDElementRef); - } - - // restart queue - IOHIDQueueStart(tIOHIDQueueRef); - } else { - HIDReportError("No queue for device passed to HIDQueueElement."); - if ( kIOReturnSuccess == result ) { - result = kIOReturnError; - } - } - } else { - HIDReportError("NULL element passed to HIDQueueElement."); - result = kIOReturnBadArgument; - } - } else { - HIDReportError("NULL device passed to HIDQueueElement."); - result = kIOReturnBadArgument; - } - - return (result); -} /* HIDQueueElement */ -// --------------------------------- - -// adds all elements to queue, performing any device queue set up required -// queue is started and ready to return events on exit from this function -int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { - IOReturn result = kIOReturnSuccess; - // error checking - if ( !inIOHIDDeviceRef ) { - HIDReportError("Device does not exist, cannot queue device."); - return (kIOReturnBadArgument); - } - if ( !inIOHIDDeviceRef ) { // must have interface - HIDReportError("Device does not have hid device ref, cannot queue device."); - return (kIOReturnError); - } - - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( !tIOHIDQueueRef ) { // if no queue create queue - result = HIDCreateQueue(inIOHIDDeviceRef); - if ( kIOReturnSuccess == result ) { - tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - } - } - if ( (kIOReturnSuccess != result) || (!tIOHIDQueueRef) ) { - HIDReportErrorNum("Could not queue device due to problem creating queue.", result); - if ( kIOReturnSuccess != result ) { - return (result); - } else { - return (kIOReturnError); - } - } - - // stop queue - IOHIDQueueStop(tIOHIDQueueRef); - - // queue element - IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); - - while ( tIOHIDElementRef ) { - if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { - IOHIDQueueAddElement(tIOHIDQueueRef, tIOHIDElementRef); - } - - tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); - } - - // restart queue - IOHIDQueueStart(tIOHIDQueueRef); - - return (result); -} /* HIDQueueDevice */ - -// --------------------------------- -// removes element for queue, if last element in queue will release queue and closes device interface -int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { - IOReturn result = kIOReturnSuccess; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - if ( inIOHIDElementRef ) { - assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { - // stop queue - IOHIDQueueStop(tIOHIDQueueRef); - // de-queue element - if ( IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { - IOHIDQueueRemoveElement(tIOHIDQueueRef, inIOHIDElementRef); - } - // release device queue and close interface if queue empty - if ( HIDIsDeviceQueueEmpty(inIOHIDDeviceRef) ) { - result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); - if ( kIOReturnSuccess != result ) { - HIDReportErrorNum("Failed to dispose and release queue.", result); - } - } else { // not empty so restart queue - IOHIDQueueStart(tIOHIDQueueRef); - } - } else { - HIDReportError("No queue for device passed to HIDDequeueElement."); - if ( kIOReturnSuccess == result ) { - result = kIOReturnError; - } - } - } else { - HIDReportError("NULL element passed to HIDDequeueElement."); - result = kIOReturnBadArgument; - } - } else { - HIDReportError("NULL device passed to HIDDequeueElement."); - result = kIOReturnBadArgument; - } - - return (result); -} /* HIDDequeueElement */ - -// --------------------------------- -// completely removes all elements from queue and releases queue and closes device interface -// does not release device interfaces, application must call ReleaseHIDDeviceList on exit -int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { - IOReturn result = kIOReturnSuccess; - // error checking - if ( !inIOHIDDeviceRef ) { - HIDReportError("Device does not exist, cannot queue device."); - return (kIOReturnBadArgument); - } - if ( !inIOHIDDeviceRef ) { // must have interface - HIDReportError("Device does not have hid device ref, cannot queue device."); - return (kIOReturnError); - } - - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { - // iterate through elements and if queued, remove - IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); - - while ( tIOHIDElementRef ) { - // de-queue element - if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { - IOHIDQueueRemoveElement(tIOHIDQueueRef, tIOHIDElementRef); - } - - tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); - } - - // ensure queue is disposed and released - result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); - if ( kIOReturnSuccess != result ) { - HIDReportErrorNum("Failed to dispose and release queue.", result); - } - } else { - HIDReportError("No queue for device passed to HIDDequeueElement."); - if ( kIOReturnSuccess == result ) { - result = kIOReturnError; - } - } - - return (result); -} /* HIDDequeueDevice */ -// --------------------------------- - -// releases all device queues for quit or rebuild (must be called) -// does not release device interfaces, application must call ReleaseHIDDeviceList on exit -IOReturn HIDReleaseAllDeviceQueues(void) { - IOReturn result = kIOReturnSuccess; - IOHIDDeviceRef tIOHIDDeviceRef = HIDGetFirstDevice(); - - while ( tIOHIDDeviceRef ) { - result = HIDDequeueDevice(tIOHIDDeviceRef); - if ( kIOReturnSuccess != result ) { - HIDReportErrorNum("Could not dequeue device.", result); - } - - tIOHIDDeviceRef = HIDGetNextDevice(tIOHIDDeviceRef); - } - - return (result); -} /* HIDReleaseAllDeviceQueues */ - -// --------------------------------- -// Get the next event in the queue for a device -// elements or entire device should be queued prior to calling this with HIDQueueElement or HIDQueueDevice -// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise -// Note: kIOReturnUnderrun returned from getNextEvent indicates an empty queue not an error condition -// Note: application should pass in a pointer to a IOHIDEventStruct cast to a void (for CFM compatibility) -unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef) { - if ( inIOHIDDeviceRef ) { - IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( tIOHIDQueueRef ) { - if ( pIOHIDValueRef ) { - *pIOHIDValueRef = IOHIDQueueCopyNextValueWithTimeout(tIOHIDQueueRef, 0.0); - if ( *pIOHIDValueRef ) { - return (true); - } - } - } else { - HIDReportError("Could not get HID event, hid queue reference does not exist."); - } - } else { - HIDReportError("Could not get HID event, device does not exist."); - } - - return (false); // did not get event -} /* HIDGetEvent */ - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/HID_Utilities.c b/src/posix/cocoa/hid/HID_Utilities.c deleted file mode 100644 index 3152cfdd9..000000000 --- a/src/posix/cocoa/hid/HID_Utilities.c +++ /dev/null @@ -1,1068 +0,0 @@ -// File: HID_Utilities.c -// Abstract: Implementation of the HID utilities -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//*************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#pragma mark - includes & imports -//----------------------------------------------------- - -#include - -#include "HID_Utilities_External.h" - -//*************************************************** -#pragma mark - typedefs, enums, defines, etc. -//----------------------------------------------------- -#define FAKE_MISSING_NAMES 1 // set this to true while debuging to get more explicit element names; false for -// the -// generic ones - -#define kPercentMove 10 // precent of overall range a element must move to register -#define kNameKeyCFStringRef CFSTR("Name") // dictionary key - -//*************************************************** -#pragma mark - local ( static ) function prototypes -//----------------------------------------------------- - -static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context); -static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context); -static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage); - -//*************************************************** -#pragma mark - exported globals -//----------------------------------------------------- - -IOHIDManagerRef gIOHIDManagerRef = NULL; -CFMutableArrayRef gDeviceCFArrayRef = NULL; -CFIndex gDeviceIndex; -CFArrayRef gElementCFArrayRef = NULL; - -//*************************************************** -#pragma mark - local ( static ) globals -//----------------------------------------------------- - -//*************************************************** -#pragma mark - exported function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) -// -// Purpose: builds list of devices with elements -// -// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages -// inUsages - inNumDeviceTypes sized array of matching usages -// inNumDeviceTypes - number of usage pages & usages -// -// Returns: Boolean - if successful -// -Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { - Boolean result = FALSE; // assume failure ( pessimist! ) - Boolean first = (!gIOHIDManagerRef); // not yet created? - if ( first ) { - // create the manager - gIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - } - if ( gIOHIDManagerRef ) { - CFMutableArrayRef hidMatchingCFMutableArrayRef = NULL; - if ( inUsages && inUsagePages && inNumDeviceTypes ) { - hidMatchingCFMutableArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - if ( hidMatchingCFMutableArrayRef ) { - int idx; - for ( idx = 0; idx < inNumDeviceTypes; idx++ ) { // for all usage and usage page types - // Set up matching dictionary. returns NULL on error. - CFMutableDictionaryRef hidMatchingCFDictRef = hu_SetUpMatchingDictionary(inUsagePages[idx], inUsages[idx]); - if ( hidMatchingCFDictRef ) { - CFArrayAppendValue(hidMatchingCFMutableArrayRef, (void *) hidMatchingCFDictRef); - CFRelease(hidMatchingCFDictRef); - } else { - fprintf(stderr, "%s: Couldn’t create a matching dictionary.", __PRETTY_FUNCTION__); - } - } - } else { - fprintf(stderr, "%s: Couldn’t create a matching array.", __PRETTY_FUNCTION__); - } - } - - // set it for IOHIDManager to use to match against - IOHIDManagerSetDeviceMatchingMultiple(gIOHIDManagerRef, hidMatchingCFMutableArrayRef); - if ( hidMatchingCFMutableArrayRef ) { - CFRelease(hidMatchingCFMutableArrayRef); - } - if ( first ) { - // open it - IOReturn tIOReturn = IOHIDManagerOpen(gIOHIDManagerRef, kIOHIDOptionsTypeNone); - if ( kIOReturnSuccess != tIOReturn ) { - fprintf(stderr, "%s: Couldn’t open IOHIDManager.", __PRETTY_FUNCTION__); - goto Oops; - } - } - - HIDRebuildDevices(); - result = TRUE; - } else { - fprintf(stderr, "%s: Couldn’t create a IOHIDManager.", __PRETTY_FUNCTION__); - } - -Oops: ; - return (result); -} // HIDBuildMultiDeviceList - -/************************************************************************* - * - * HIDBuildDeviceList( inUsagePage, inUsage ) - * - * Purpose: builds list of devices with elements - * - * Notes: same as above but this uses a single inUsagePage and usage - * allocates memory and captures devices - * list is allocated internally within HID Utilites and can be accessed via accessor functions - * structures within list are considered flat and user accessable, but not user modifiable - * can be called again to rebuild list to account for new devices - * ( will do the right thing in case of disposing existing list ) - * - * Inputs: inUsagePage - usage page - * inUsage - usages - * - * Returns: Boolean - if successful - */ - -Boolean HIDBuildDeviceList(UInt32 inUsagePage, UInt32 inUsage) { - return ( HIDBuildMultiDeviceList(&inUsagePage, &inUsage, 1) ); // call HIDBuildMultiDeviceList with a single usage -} - -/************************************************************************* - * - * HIDUpdateDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) - * - * Purpose: updates the current device list for any new/removed devices - * - * Notes: if this is called before HIDBuildDeviceList then it functions like HIDBuildMultiDeviceList - * inUsagePage & inUsage are each a inNumDeviceTypes sized array of matching usage and usage pages - * - * Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages - * inUsages - inNumDeviceTypes sized array of matching usages - * inNumDeviceTypes - number of usage pages & usages - * - * Returns: Boolean - TRUE if the device config changed - */ - -Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { - return ( HIDBuildMultiDeviceList(inUsagePages, inUsages, inNumDeviceTypes) ); -} - -/************************************************************************* - * - * HIDReleaseDeviceList( void ) - * - * Purpose: release list built by above functions - * - * Notes: MUST be called prior to application exit to properly release devices - * if not called( or app crashes ) devices can be recovered by pluging into different location in USB chain - * - * Inputs: none - * - * Returns: none - */ - -void HIDReleaseDeviceList(void) { - if ( gDeviceCFArrayRef ) { - CFRelease(gDeviceCFArrayRef); - gDeviceCFArrayRef = NULL; - } -} // HIDReleaseDeviceList - -/************************************************************************* - * - * HIDHaveDeviceList( void ) - * - * Purpose: does a device list exist? - * - * Inputs: none - * - * Returns: Boolean - TRUE if we have previously built a device list - */ - -Boolean HIDHaveDeviceList(void) { - return (NULL != gDeviceCFArrayRef); -} - -//************************************************************************* -// -// HIDRebuildDevices( ) -// -// Purpose: rebuilds the (internal) list of IOHIDDevices -// -// Inputs: none -// -// Returns: none -// - -void HIDRebuildDevices(void) { - // get the set of devices from the IOHID manager - CFSetRef devCFSetRef = IOHIDManagerCopyDevices(gIOHIDManagerRef); - if ( devCFSetRef ) { - // if the existing array isn't empty... - if ( gDeviceCFArrayRef ) { - // release it - CFRelease(gDeviceCFArrayRef); - } - - // create an empty array - gDeviceCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - // now copy the set to the array - CFSetApplyFunction(devCFSetRef, CFSetApplierFunctionCopyToCFArray, (void *) gDeviceCFArrayRef); - // now sort the array by location ID's - CFIndex cnt = CFArrayGetCount(gDeviceCFArrayRef); - CFArraySortValues(gDeviceCFArrayRef, CFRangeMake(0, cnt), CFDeviceArrayComparatorFunction, NULL); - - // and release the set we copied from the IOHID manager - CFRelease(devCFSetRef); - } -} // HIDRebuildDevices - -// --------------------------------- - -// how many HID devices have been found -// returns 0 if no device list exist - -UInt32 HIDCountDevices(void) { - return ( CFArrayGetCount(gDeviceCFArrayRef) ); -} - -// --------------------------------- - -// how many elements does a specific device have -// returns 0 if device is invlaid or NULL - -UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { - int count = 0; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - - gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( gElementCFArrayRef ) { - CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; - } - - IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); - - switch ( type ) { - case kIOHIDElementTypeInput_Misc: - case kIOHIDElementTypeInput_Button: - case kIOHIDElementTypeInput_Axis: - case kIOHIDElementTypeInput_ScanCodes: - { - if ( typeMask & kHIDElementTypeInput ) { - count++; - } - - break; - } - - case kIOHIDElementTypeOutput: - { - if ( typeMask & kHIDElementTypeOutput ) { - count++; - } - - break; - } - - case kIOHIDElementTypeFeature: - { - if ( typeMask & kHIDElementTypeFeature ) { - count++; - } - - break; - } - - case kIOHIDElementTypeCollection: - { - if ( typeMask & kHIDElementTypeCollection ) { - count++; - } - - break; - } - default: { - break; - } - } // switch ( type ) - - } // next idx - - CFRelease(gElementCFArrayRef); - gElementCFArrayRef = NULL; - } // if ( gElementCFArrayRef ) - - } // if ( inIOHIDDeviceRef ) - - return (count); -} /* HIDCountDeviceElements */ - -// --------------------------------- - -// get the first device in the device list -// returns NULL if no list exists or it's empty - -IOHIDDeviceRef HIDGetFirstDevice(void) { - IOHIDDeviceRef result = NULL; - - gDeviceIndex = 0; - if ( gDeviceCFArrayRef ) { - CFIndex count = CFArrayGetCount(gDeviceCFArrayRef); - if ( (gDeviceIndex >= 0) && (gDeviceIndex < count) ) { - result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); - } - } - - return (result); -} /* HIDGetFirstDevice */ - -// --------------------------------- - -// get next device in list given current device as parameter -// returns NULL if end of list - -IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef) { - IOHIDDeviceRef result = NULL; - if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) { - CFIndex idx, cnt = CFArrayGetCount(gDeviceCFArrayRef); - // quick case to verify the current device index is valid for current device - if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { - result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); - if ( result && (result == inIOHIDDeviceRef) ) { - result = NULL; - gDeviceIndex++; // bump index - } else { - // previous index was invalid; - gDeviceIndex = -1; - // search for current device's index - for ( idx = 0; idx < cnt; idx++ ) { - result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, idx); - if ( (result) && (result == inIOHIDDeviceRef) ) { - gDeviceIndex = idx + 1; // found valid index; bump to next one - break; - } - } - - result = NULL; - } - if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { - result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); - } - } // if valid index - - } // if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) - - return (result); -} /* HIDGetNextDevice */ - -// --------------------------------- - -// get the first element of device passed in as parameter -// returns NULL if no list exists or device does not exists or is NULL -IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { - IOHIDElementRef result = NULL; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - - gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( gElementCFArrayRef ) { - CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; - } - - IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); - - switch ( type ) { - case kIOHIDElementTypeInput_Misc: - case kIOHIDElementTypeInput_Button: - case kIOHIDElementTypeInput_Axis: - case kIOHIDElementTypeInput_ScanCodes: - { - if ( typeMask & kHIDElementTypeInput ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeOutput: - { - if ( typeMask & kHIDElementTypeOutput ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeFeature: - { - if ( typeMask & kHIDElementTypeFeature ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeCollection: - { - if ( typeMask & kHIDElementTypeCollection ) { - result = tIOHIDElementRef; - } - - break; - } - default: { - break; - } - } // switch ( type ) - if ( result ) { - break; // DONE! - } - } // next idx - - CFRelease(gElementCFArrayRef); - gElementCFArrayRef = NULL; - } // if ( gElementCFArrayRef ) - - } // if ( inIOHIDDeviceRef ) - - return (result); -} /* HIDGetFirstDeviceElement */ - -// --------------------------------- - -// get next element of given device in list given current element as parameter -// will walk down each collection then to next element or collection (depthwise traverse) -// returns NULL if end of list -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality -IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHIDElementRef, HIDElementTypeMask typeMask) { - IOHIDElementRef result = NULL; - if ( inIOHIDElementRef ) { - assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); - - IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); - if ( tIOHIDDeviceRef ) { - Boolean found = FALSE; - - gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); - if ( gElementCFArrayRef ) { - CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); - for ( idx = 0; idx < cnt; idx++ ) { - IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); - if ( !tIOHIDElementRef ) { - continue; - } - if ( !found ) { - if ( inIOHIDElementRef == tIOHIDElementRef ) { - found = TRUE; - } - - continue; // next element - } else { - // we've found the current element; now find the next one of the right type - IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); - - switch ( type ) { - case kIOHIDElementTypeInput_Misc: - case kIOHIDElementTypeInput_Button: - case kIOHIDElementTypeInput_Axis: - case kIOHIDElementTypeInput_ScanCodes: - { - if ( typeMask & kHIDElementTypeInput ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeOutput: - { - if ( typeMask & kHIDElementTypeOutput ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeFeature: - { - if ( typeMask & kHIDElementTypeFeature ) { - result = tIOHIDElementRef; - } - - break; - } - - case kIOHIDElementTypeCollection: - { - if ( typeMask & kHIDElementTypeCollection ) { - result = tIOHIDElementRef; - } - - break; - } - default: { - break; - } - } // switch ( type ) - if ( result ) { - break; // DONE! - } - } // if ( !found ) - - } // next idx - - CFRelease(gElementCFArrayRef); - gElementCFArrayRef = NULL; - } // if ( gElementCFArrayRef ) - - } // if ( inIOHIDDeviceRef ) - - } // if ( inIOHIDElementRef ) - - return (result); -} /* HIDGetNextDeviceElement */ - -#if 0 -// --------------------------------- -// get previous element of given device in list given current element as parameter -// this wlaks directly up the tree to the top element and does not search at each level -// returns NULL if beginning of list -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get non-collection elements -IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef pElement, HIDElementTypeMask typeMask) { - IOHIDElementRef pPreviousElement = pElement->pPrevious; - - // walk back up tree to element prior - while ( pPreviousElement && !HIDMatchElementTypeMask(pPreviousElement->type, typeMask) ) { - pElement = pPreviousElement; // look at previous element - pPreviousElement = pElement->pPrevious; - } - - return (pPreviousElement); // return this element -} /* HIDGetPreviousDeviceElement */ - -#endif - -// utility routine to dump device info -void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef) { - char cstring[256]; - - printf("Device: %p = { ", inIOHIDDeviceRef); - - char manufacturer[256] = ""; // name of manufacturer - CFStringRef tCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); - if ( tCFStringRef ) { - verify( CFStringGetCString(tCFStringRef, manufacturer, sizeof(manufacturer), kCFStringEncodingUTF8) ); - } - - char product[256] = ""; // name of product - tCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); - if ( tCFStringRef ) { - verify( CFStringGetCString(tCFStringRef, product, sizeof(product), kCFStringEncodingUTF8) ); - } - - printf("%s - %s, ", manufacturer, product); - - long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); - if ( vendorID ) { -#if 1 - printf(" vendorID: 0x%04lX, ", vendorID); -#else - if ( HIDGetVendorNameFromVendorID(vendorID, cstring) ) { - printf(" vendorID: 0x%04lX (\"%s\"), ", vendorID, cstring); - } else { - printf(" vendorID: 0x%04lX, ", vendorID); - } - -#endif - } - - long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); - if ( productID ) { -#if 1 - printf(" productID: 0x%04lX, ", productID); -#else - if ( HIDGetProductNameFromVendorProductID(vendorID, productID, cstring) ) { - printf(" productID: 0x%04lX (\"%s\"), ", productID, cstring); - } else { - printf(" productID: 0x%04lX, ", productID); - } - -#endif - } - - uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); - uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); - if ( !usagePage || !usage ) { - usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); - usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); - } - - printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); - -#if 1 - tCFStringRef = HIDCopyUsageName(usagePage, usage); - if ( tCFStringRef ) { - verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); - printf("\"%s\", ", cstring); - CFRelease(tCFStringRef); - } - -#endif - -#if 1 - tCFStringRef = IOHIDDevice_GetTransport(inIOHIDDeviceRef); - if ( tCFStringRef ) { - verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); - printf("Transport: \"%s\", ", cstring); - } - - long vendorIDSource = IOHIDDevice_GetVendorIDSource(inIOHIDDeviceRef); - if ( vendorIDSource ) { - printf("VendorIDSource: %ld, ", vendorIDSource); - } - - long version = IOHIDDevice_GetVersionNumber(inIOHIDDeviceRef); - if ( version ) { - printf("version: %ld, ", version); - } - - tCFStringRef = IOHIDDevice_GetSerialNumber(inIOHIDDeviceRef); - if ( tCFStringRef ) { - verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); - printf("SerialNumber: \"%s\", ", cstring); - } - - long country = IOHIDDevice_GetCountryCode(inIOHIDDeviceRef); - if ( country ) { - printf("CountryCode: %ld, ", country); - } - - long locationID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); - if ( locationID ) { - printf("locationID: 0x%08lX, ", locationID); - } - -#if 0 - CFArrayRef pairs = IOHIDDevice_GetUsagePairs(inIOHIDDeviceRef); - if ( pairs ) { - CFIndex idx, cnt = CFArrayGetCount(pairs); - for ( idx = 0; idx < cnt; idx++ ) { - const void *pair = CFArrayGetValueAtIndex(pairs, idx); - CFShow(pair); - } - } - -#endif - long maxInputReportSize = IOHIDDevice_GetMaxInputReportSize(inIOHIDDeviceRef); - if ( maxInputReportSize ) { - printf("MaxInputReportSize: %ld, ", maxInputReportSize); - } - - long maxOutputReportSize = IOHIDDevice_GetMaxOutputReportSize(inIOHIDDeviceRef); - if ( maxOutputReportSize ) { - printf("MaxOutputReportSize: %ld, ", maxOutputReportSize); - } - - long maxFeatureReportSize = IOHIDDevice_GetMaxFeatureReportSize(inIOHIDDeviceRef); - if ( maxFeatureReportSize ) { - printf("MaxFeatureReportSize: %ld, ", maxOutputReportSize); - } - - long reportInterval = IOHIDDevice_GetReportInterval(inIOHIDDeviceRef); - if ( reportInterval ) { - printf("ReportInterval: %ld, ", reportInterval); - } - - IOHIDQueueRef queueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); - if ( queueRef ) { - printf("queue: %p, ", queueRef); - } - - IOHIDTransactionRef transactionRef = IOHIDDevice_GetTransaction(inIOHIDDeviceRef); - if ( transactionRef ) { - printf("transaction: %p, ", transactionRef); - } - -#endif - printf("}\n"); - fflush(stdout); -} // HIDDumpDeviceInfo - -// utility routine to dump element info -void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef) { - if ( inIOHIDElementRef ) { - printf(" Element: %p = { ", inIOHIDElementRef); -#if 0 - IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); - printf("Device: %p, ", tIOHIDDeviceRef); -#endif - IOHIDElementRef parentIOHIDElementRef = IOHIDElementGetParent(inIOHIDElementRef); - printf("parent: %p, ", parentIOHIDElementRef); -#if 0 - CFArrayRef childrenCFArrayRef = IOHIDElementGetChildren(inIOHIDElementRef); - printf("children: %p: { ", childrenCFArrayRef); - fflush(stdout); - CFShow(childrenCFArrayRef); - fflush(stdout); - printf(" }, "); -#endif - IOHIDElementCookie tIOHIDElementCookie = IOHIDElementGetCookie(inIOHIDElementRef); - printf("cookie: %p, ", tIOHIDElementCookie); - - IOHIDElementType tIOHIDElementType = IOHIDElementGetType(inIOHIDElementRef); - - switch ( tIOHIDElementType ) { - case kIOHIDElementTypeInput_Misc: - { - printf("type: Misc, "); - break; - } - - case kIOHIDElementTypeInput_Button: - { - printf("type: Button, "); - break; - } - - case kIOHIDElementTypeInput_Axis: - { - printf("type: Axis, "); - break; - } - - case kIOHIDElementTypeInput_ScanCodes: - { - printf("type: ScanCodes, "); - break; - } - - case kIOHIDElementTypeOutput: - { - printf("type: Output, "); - break; - } - - case kIOHIDElementTypeFeature: - { - printf("type: Feature, "); - break; - } - - case kIOHIDElementTypeCollection: - { - IOHIDElementCollectionType tIOHIDElementCollectionType = IOHIDElementGetCollectionType(inIOHIDElementRef); - - switch ( tIOHIDElementCollectionType ) { - case kIOHIDElementCollectionTypePhysical: - { - printf("type: Physical Collection, "); - break; - } - - case kIOHIDElementCollectionTypeApplication: - { - printf("type: Application Collection, "); - break; - } - - case kIOHIDElementCollectionTypeLogical: - { - printf("type: Logical Collection, "); - break; - } - - case kIOHIDElementCollectionTypeReport: - { - printf("type: Report Collection, "); - break; - } - - case kIOHIDElementCollectionTypeNamedArray: - { - printf("type: Named Array Collection, "); - break; - } - - case kIOHIDElementCollectionTypeUsageSwitch: - { - printf("type: Usage Switch Collection, "); - break; - } - - case kIOHIDElementCollectionTypeUsageModifier: - { - printf("type: Usage Modifier Collection, "); - break; - } - - default: - { - printf("type: %p Collection, ", (void *) tIOHIDElementCollectionType); - break; - } - } // switch - - break; - } - - default: - { - printf("type: %p, ", (void *) tIOHIDElementType); - break; - } - } /* switch */ - - uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); - uint32_t usage = IOHIDElementGetUsage(inIOHIDElementRef); - printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); -#if 1 - CFStringRef tCFStringRef = HIDCopyUsageName(usagePage, usage); - if ( tCFStringRef ) { - char usageString[256] = ""; - verify( CFStringGetCString(tCFStringRef, usageString, sizeof(usageString), kCFStringEncodingUTF8) ); - printf("\"%s\", ", usageString); - CFRelease(tCFStringRef); - } - -#endif - CFStringRef nameCFStringRef = IOHIDElementGetName(inIOHIDElementRef); - char buffer[256]; - if ( nameCFStringRef && CFStringGetCString(nameCFStringRef, buffer, sizeof(buffer), kCFStringEncodingUTF8) ) { - printf("name: %s, ", buffer); - } - - uint32_t reportID = IOHIDElementGetReportID(inIOHIDElementRef); - uint32_t reportSize = IOHIDElementGetReportSize(inIOHIDElementRef); - uint32_t reportCount = IOHIDElementGetReportCount(inIOHIDElementRef); - printf("report: { ID: %lu, Size: %lu, Count: %lu }, ", - (long unsigned int) reportID, (long unsigned int) reportSize, (long unsigned int) reportCount); - - uint32_t unit = IOHIDElementGetUnit(inIOHIDElementRef); - uint32_t unitExp = IOHIDElementGetUnitExponent(inIOHIDElementRef); - if ( unit || unitExp ) { - printf("unit: %lu * 10^%lu, ", (long unsigned int) unit, (long unsigned int) unitExp); - } - - CFIndex logicalMin = IOHIDElementGetLogicalMin(inIOHIDElementRef); - CFIndex logicalMax = IOHIDElementGetLogicalMax(inIOHIDElementRef); - if ( logicalMin != logicalMax ) { - printf("logical: {min: %ld, max: %ld}, ", logicalMin, logicalMax); - } - - CFIndex physicalMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); - CFIndex physicalMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); - if ( physicalMin != physicalMax ) { - printf("physical: {min: %ld, max: %ld}, ", physicalMin, physicalMax); - } - - Boolean isVirtual = IOHIDElementIsVirtual(inIOHIDElementRef); - if ( isVirtual ) { - printf("isVirtual, "); - } - - Boolean isRelative = IOHIDElementIsRelative(inIOHIDElementRef); - if ( isRelative ) { - printf("isRelative, "); - } - - Boolean isWrapping = IOHIDElementIsWrapping(inIOHIDElementRef); - if ( isWrapping ) { - printf("isWrapping, "); - } - - Boolean isArray = IOHIDElementIsArray(inIOHIDElementRef); - if ( isArray ) { - printf("isArray, "); - } - - Boolean isNonLinear = IOHIDElementIsNonLinear(inIOHIDElementRef); - if ( isNonLinear ) { - printf("isNonLinear, "); - } - - Boolean hasPreferredState = IOHIDElementHasPreferredState(inIOHIDElementRef); - if ( hasPreferredState ) { - printf("hasPreferredState, "); - } - - Boolean hasNullState = IOHIDElementHasNullState(inIOHIDElementRef); - if ( hasNullState ) { - printf("hasNullState, "); - } - - printf(" }\n"); - } -} // HIDDumpElementInfo - -void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef) { - printf(" Element: %p = { ", inIOHIDElementRef); - - CFIndex calMin = IOHIDElement_GetCalibrationMin(inIOHIDElementRef); - CFIndex calMax = IOHIDElement_GetCalibrationMax(inIOHIDElementRef); - printf("cal: {min: %ld, max: %ld}, ", calMin, calMax); - - CFIndex satMin = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); - CFIndex satMax = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); - printf("sat: {min: %ld, max: %ld}, ", satMin, satMax); - - CFIndex deadMin = IOHIDElement_GetCalibrationDeadZoneMin(inIOHIDElementRef); - CFIndex deadMax = IOHIDElement_GetCalibrationDeadZoneMax(inIOHIDElementRef); - printf("dead: {min: %ld, max: %ld}, ", deadMin, deadMax); - - double_t granularity = IOHIDElement_GetCalibrationGranularity(inIOHIDElementRef); - printf("granularity: %6.2f }\n", granularity); -} // HIDDumpElementCalibrationInfo - -//*************************************************** -#pragma mark - local ( static ) function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// CFSetApplierFunctionCopyToCFArray( value, context ) -// -// Purpose: CFSetApplierFunction to copy the CFSet to a CFArray -// -// Notes: called one time for each item in the CFSet -// -// Inputs: value - the current element of the CFSet -// context - the CFMutableArrayRef we're adding the CFSet elements to -// -// Returns: nothing -// -static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context) { - // printf( "%s: 0x%08lX\n", __PRETTY_FUNCTION__, (long unsigned int) value ); - CFArrayAppendValue( (CFMutableArrayRef) context, value ); -} // CFSetApplierFunctionCopyToCFArray - -// --------------------------------- -// used to sort the CFDevice array after copying it from the (unordered) (CF)set. -// we compare based on the location ID's since they're consistant (across boots & launches). -// -static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context) { -#pragma unused( context ) - CFComparisonResult result = kCFCompareEqualTo; - - long loc1 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val1 ); - long loc2 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val2 ); - if ( loc1 < loc2 ) { - result = kCFCompareLessThan; - } else if ( loc1 > loc2 ) { - result = kCFCompareGreaterThan; - } - - return (result); -} // CFDeviceArrayComparatorFunction - -//************************************************************************* -// -// hu_SetUpMatchingDictionary( inUsagePage, inUsage ) -// -// Purpose: builds a matching dictionary based on usage page and usage -// -// Notes: Only called by HIDBuildMultiDeviceList -// -// Inputs: inUsagePage - usage page -// inUsage - usages -// -// Returns: CFMutableDictionaryRef - the matching dictionary -// - -static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) { - // create a dictionary to add usage page/usages to - CFMutableDictionaryRef refHIDMatchDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if ( refHIDMatchDictionary ) { - if ( inUsagePage ) { - // Add key for device type to refine the matching dictionary. - CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage); - if ( pageCFNumberRef ) { - CFDictionarySetValue(refHIDMatchDictionary, - CFSTR(kIOHIDPrimaryUsagePageKey), pageCFNumberRef); - CFRelease(pageCFNumberRef); - // note: the usage is only valid if the usage page is also defined - if ( inUsage ) { - CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage); - if ( usageCFNumberRef ) { - CFDictionarySetValue(refHIDMatchDictionary, - CFSTR(kIOHIDPrimaryUsageKey), usageCFNumberRef); - CFRelease(usageCFNumberRef); - } else { - fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__); - } - } - } else { - fprintf(stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__); - } - } - } else { - fprintf(stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__); - } - - return (refHIDMatchDictionary); -} // hu_SetUpMatchingDictionary - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/HID_Utilities_External.h b/src/posix/cocoa/hid/HID_Utilities_External.h deleted file mode 100644 index bd498bc51..000000000 --- a/src/posix/cocoa/hid/HID_Utilities_External.h +++ /dev/null @@ -1,417 +0,0 @@ -// File: HID_Utilities_External.h -// Abstract: External interface for HID Utilities, can be used with either library or source. -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** -#ifndef _HID_Utilities_External_h_ -#define _HID_Utilities_External_h_ - -// ================================== - -#ifdef __cplusplus -extern "C" { -#endif - -// ================================== - -//includes - -#include -#include "IOHIDLib_.h" - -// ================================== - -#ifndef _IOKIT_HID_IOHIDKEYS_H_ -/*! - @typedef IOHIDElementCookie - @abstract Abstract data type used as a unique identifier for an element. - */ -#ifdef __LP64__ -typedef uint32_t IOHIDElementCookie; -#else -typedef void *IOHIDElementCookie; -#endif -#endif - -// Device and Element Interfaces - -enum HIDElementTypeMask { - kHIDElementTypeInput = 1 << 1, - kHIDElementTypeOutput = 1 << 2, - kHIDElementTypeFeature = 1 << 3, - kHIDElementTypeCollection = 1 << 4, - kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature, - kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection -}; -typedef enum HIDElementTypeMask HIDElementTypeMask; - -// ================================== - -//***************************************************** -#pragma mark - exported globals -//----------------------------------------------------- - -extern IOHIDManagerRef gIOHIDManagerRef; -extern CFMutableArrayRef gDeviceCFArrayRef; -extern CFArrayRef gElementCFArrayRef; - -//************************************************************************* -// -// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) -// -// Purpose: builds list of devices with elements (allocates memory and captures devices) in which -// the devices could be of different types/usages list is allocated internally within HID -// Utilites and can be accessed via accessor functions structures within list are considered -// flat and user accessable, but not user modifiable can be called again to rebuild list to -// account for new devices (will do the right thing in case of disposing existing list) -// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages -// returns true if succesful -// -// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages -// inUsages - inNumDeviceTypes sized array of matching usages -// inNumDeviceTypes - number of usage pages & usages -// -// Returns: Boolean - if successful -// -extern Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes); - -// same as above but this uses a single usagePage and usage -extern Boolean HIDBuildDeviceList(UInt32 usagePage, UInt32 usage); - -// updates the current device list for any new/removed devices -// if this is called before HIDBuildDeviceList the it functions like HIDBuildMultiDeviceList -// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages -// returns true if successful which means if any device were added or removed (the device config changed) -extern Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes); - -// release list built by above function -// MUST be called prior to application exit to properly release devices -// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain -extern void HIDReleaseDeviceList(void); - -//************************************************************************* -// -// HIDRebuildDevices( ) -// -// Purpose: rebuilds the (internal) list of devices -// -// Inputs: none -// -// Returns: none -// - -extern void HIDRebuildDevices(void); - -// does a device list exist -extern unsigned char HIDHaveDeviceList(void); - -// how many HID devices have been found -// returns 0 if no device list exist -extern UInt32 HIDCountDevices(void); - -// how many elements does a specific device have -// returns 0 if device is invalid or NULL -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get non-collection elements -extern UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask); - -// get the first device in the device list -// returns NULL if no list exists -extern IOHIDDeviceRef HIDGetFirstDevice(void); - -// get next device in list given current device as parameter -// returns NULL if end of list -extern IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef); - -// get the first element of device passed in as parameter -// returns NULL if no list exists or device does not exists or is NULL -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get previous HIDGetFirstDeviceElement functionality -extern IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask); - -// get next element of given device in list given current element as parameter -// will walk down each collection then to next element or collection (depthwise traverse) -// returns NULL if end of list -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality -extern IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask); - -// get previous element of given device in list given current element as parameter -// this walks directly up the tree to the top element and does not search at each level -// returns NULL if beginning of list -// uses mask of HIDElementTypeMask to restrict element found -// use kHIDElementTypeIO to get non-collection elements -extern IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask); - -// returns C string type name given a type enumeration passed in as parameter( see IOHIDKeys.h ) -// returns empty string for invalid types -extern void HIDGetTypeName(IOHIDElementType inIOHIDElementType, char *outCStrName); - -//************************************************************************* -// -// HIDCopyUsageName( inUsagePage, inUsage ) -// -// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h ) -// -// Notes: returns usage page and usage values in CFString form for unknown values -// -// Inputs: inUsagePage - the usage page -// inUsage - the usage -// -// Returns: CFStringRef - the resultant string -// - -extern CFStringRef HIDCopyUsageName(long inUsagePage, long inUsage); - -// ================================== - -// Element Event Queue and Value Interfaces - -enum { - kDefaultUserMin = 0, // default user min and max used for scaling - kDefaultUserMax = 255 -}; - -enum { - kDeviceQueueSize = 50 // this is wired kernel memory so should be set to as small as possible - // but should account for the maximum possible events in the queue - // USB updates will likely occur at 100 Hz so one must account for this rate of - // if states change quickly (updates are only posted on state changes) -}; - -// ================================== - -// queues specific element, performing any device queue set up required -extern int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef); - -// adds all elements to queue, performing any device queue set up required -extern int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef); - -// removes element for queue, if last element in queue will release queue and device -extern int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef); - -// completely removes all elements from queue and releases queue and device -extern int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef); - -// releases all device queues for quit or rebuild (must be called) -extern int HIDReleaseAllDeviceQueues(void); - -// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise -// pHIDEvent is a poiner to a IOHIDEventStruct, using void here for compatibility, users can cast a required -extern unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef); - -// ================================== - -// Conguration and Save Interfaces - -enum { - kPercentMove = 10 // precent of overall range a element must move to register -}; - -typedef struct HID_info_struct { - int actionCookie; - // device - // need to add serial number when I have a test case - struct { - int vendorID, productID; - int locID; - uint32_t usagePage, usage; - } device; - // elements - struct { - uint32_t usagePage, usage; - int minReport, maxReport; - IOHIDElementCookie cookie; // always 32 bits - } element; -}HID_info_rec, *HID_info_ptr; - -// get vendor name from vendor ID -extern Boolean HIDGetVendorNameFromVendorID(long inVendorID, char *outCStrName); - -// get product name from vendor/product ID -extern Boolean HIDGetProductNameFromVendorProductID(long inVendorID, long inProductID, char *outCStrName); - -// get element name from vendor id/product id look up ( using element cookie ) -extern Boolean HIDGetElementNameFromVendorProductCookie(int inVendorID, - int inProductID, - IOHIDElementCookie inCookie, - char * outCStrName); - -// get element name from vendor id/product id look up ( using element usage page & usage ) -extern Boolean HIDGetElementNameFromVendorProductUsage(long inVendorID, - long inProductID, - long inUsagePage, - long inUsage, - char *inCStrName); - -// utility routines to dump device or element info -extern void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef); -extern void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef); -extern void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef); - -// polls single device's elements for a change greater than kPercentMove. Times out after given time -// returns 1 and pointer to element if found -// returns 0 and NULL for both parameters if not found -extern unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, - IOHIDElementRef *outIOHIDElementRef, - float timeout); - -//************************************************************************* -// -// HIDConfigureAction( outDeviceRef, outElementRef, inTimeout ) -// -// Purpose: polls all devices and elements for a change greater than kPercentMove. -// Times out after given time returns 1 and pointer to device and element -// if found; returns 0 and NULL for both parameters if not found -// -// Inputs: outDeviceRef - address where to store the device -// outElementRef - address where to store the element -// inTimeout - the timeout -// Returns: Boolean - TRUE if successful -// outDeviceRef - the device -// outElementRef - the element -// - -extern Boolean HIDConfigureAction(IOHIDDeviceRef *outDeviceRef, IOHIDElementRef *outElementRef, float inTimeout); - -//************************************************************************* -// -// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inDeviceRef, inElementRef ) -// -// Purpose: Save the device & element values into the specified key in the specified applications preferences -// -// Inputs: inKeyCFStringRef - the preference key -// inAppCFStringRef - the application identifier -// inDeviceRef - the device -// inElementRef - the element -// Returns: Boolean - if successful -// - -extern Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, - CFStringRef inAppCFStringRef, - IOHIDDeviceRef inDeviceRef, - IOHIDElementRef inElementRef); - -//************************************************************************* -// -// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outDeviceRef, outElementRef ) -// -// Purpose: Find the specified preference in the specified application -// -// Inputs: inKeyCFStringRef - the preference key -// inAppCFStringRef - the application identifier -// outDeviceRef - address where to restore the device -// outElementRef - address where to restore the element -// Returns: Boolean - if successful -// outDeviceRef - the device -// outElementRef - the element -// - -extern Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef, - CFStringRef inAppCFStringRef, - IOHIDDeviceRef * outDeviceRef, - IOHIDElementRef *outElementRef); - -//************************************************************************* -// -// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) -// -// Purpose: find the closest matching device and element for this action -// -// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage -// matches element: cookie, inUsagePage, usage, -// -// Inputs: inSearchInfo - the device & element info we searching for -// outFoundDevice - the address of the best matching device -// outFoundElement - the address of the best matching element -// -// Returns: Boolean - TRUE if we find a match -// outFoundDevice - the best matching device -// outFoundElement - the best matching element -// - -extern Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, - IOHIDDeviceRef * outFoundDevice, - IOHIDElementRef * outFoundElement); - -// -- These are routines to use if the applcationwants HID Utilities to do the file handling -- -// Note: the FILE * is a MachO posix FILE and will not likely work directly with MW MSL FILE * type. - -// take input records, save required info -// assume file is open and at correct position. -void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef, int actionCookie); - -// takes a file, reads one record (assume file position is correct and file is open) -// search for matching device -// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action -int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef); - -// -- These are routines to use if the client wants to use their own file handling -- - -// Set up a config record for saving -// takes an input records, returns record user can save as they want -// Note: the save rec must be pre-allocated by the calling app and will be filled out -void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, - IOHIDDeviceRef inIOHIDDeviceRef, - IOHIDElementRef inIOHidElementRef, - int actionCookie); - -// Get matching element from config record -// takes a pre-allocated and filled out config record -// search for matching device -// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action -int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef); - -// ================================== - -// Error reporter, can be set to report however the application desires -extern void HIDReportError(const char *strError); - -// Error with numeric code reporter, can be set to report however the application desires -extern void HIDReportErrorNum(const char *strError, int numError); - -#ifdef __cplusplus -} -#endif - -#endif // _HID_Utilities_External_h_ diff --git a/src/posix/cocoa/hid/IOHIDDevice_.c b/src/posix/cocoa/hid/IOHIDDevice_.c deleted file mode 100644 index 30c01dc7c..000000000 --- a/src/posix/cocoa/hid/IOHIDDevice_.c +++ /dev/null @@ -1,619 +0,0 @@ -// File: IOHIDDevice_.c -// Abstract: convieance functions for IOHIDDeviceGetProperty -// Version: 2.0 + 5.3 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#pragma mark - includes & imports -//----------------------------------------------------- - -#include "IOHIDDevice_.h" - -//***************************************************** -#pragma mark - typedef's, struct's, enums, defines, etc. -//----------------------------------------------------- - -#define kIOHIDDevice_TransactionKey "DeviceTransactionRef" -#define kIOHIDDevice_QueueKey "DeviceQueueRef" - -//***************************************************** -#pragma mark - local (static) function prototypes -//----------------------------------------------------- - -static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, - CFStringRef inKey, - uint32_t * outValue); -// static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue); - -static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, - CFStringRef inKey, - void ** outValue); -static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, - CFStringRef inKey, - void * inValue); - -//***************************************************** -#pragma mark - exported globals -//----------------------------------------------------- - -//***************************************************** -#pragma mark - local (static) globals -//----------------------------------------------------- - -//***************************************************** -#pragma mark - exported function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// HIDIsValidDevice( inIOHIDDeviceRef ) -// -// Purpose: validate this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: Boolean - TRUE if we find the device in our( internal ) device list -// - -Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef) { - Boolean result = FALSE; // assume failure (pessimist!) - if ( inIOHIDDeviceRef ) { - if ( CFGetTypeID(inIOHIDDeviceRef) ==IOHIDDeviceGetTypeID() ) { - result = TRUE; - } - } - - return (result); -} // HIDIsValidDevice - -//************************************************************************* -// -// IOHIDDevice_GetTransport( inIOHIDDeviceRef ) -// -// Purpose: get the Transport CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: CFStringRef - the Transport for this device -// - -CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDTransportKey) ) ); -} - -//************************************************************************* -// -// IOHIDDevice_GetVendorID( inIOHIDDeviceRef ) -// -// Purpose: get the vendor ID for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the vendor ID for this device -// - -uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey), &result); - return (result); -} // IOHIDDevice_GetVendorID - -//************************************************************************* -// -// IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef ) -// -// Purpose: get the VendorIDSource for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the VendorIDSource for this device -// - -uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDSourceKey), &result); - return (result); -} // IOHIDDevice_GetVendorIDSource - -//************************************************************************* -// -// IOHIDDevice_GetProductID( inIOHIDDeviceRef ) -// -// Purpose: get the product ID for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the product ID for this device -// - -uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey), &result); - return (result); -} // IOHIDDevice_GetProductID - -//************************************************************************* -// -// IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef ) -// -// Purpose: get the VersionNumber CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the VersionNumber for this device -// - -uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVersionNumberKey), &result); - return (result); -} // IOHIDDevice_GetVersionNumber - -//************************************************************************* -// -// IOHIDDevice_GetManufacturer( inIOHIDDeviceRef ) -// -// Purpose: get the Manufacturer CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: CFStringRef - the Manufacturer for this device -// - -CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDManufacturerKey) ) ); -} // IOHIDDevice_GetManufacturer - -//************************************************************************* -// -// IOHIDDevice_GetProduct( inIOHIDDeviceRef ) -// -// Purpose: get the Product CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: CFStringRef - the Product for this device -// - -CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDProductKey) ) ); -} // IOHIDDevice_GetProduct - -//************************************************************************* -// -// IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef ) -// -// Purpose: get the SerialNumber CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: CFStringRef - the SerialNumber for this device -// - -CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDSerialNumberKey) ) ); -} - -//************************************************************************* -// -// IOHIDDevice_GetCountryCode( inIOHIDDeviceRef ) -// -// Purpose: get the CountryCode CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the CountryCode for this device -// - -uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDCountryCodeKey), &result); - return (result); -} // IOHIDDevice_GetCountryCode - -//************************************************************************* -// -// IOHIDDevice_GetLocationID( inIOHIDDeviceRef ) -// -// Purpose: get the location ID for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the location ID for this device -// - -uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey), &result); - return (result); -} // IOHIDDevice_GetLocationID - -//************************************************************************* -// -// IOHIDDevice_GetUsage( inIOHIDDeviceRef ) -// -// Purpose: get the usage for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the usage for this device -// - -uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), &result); - return (result); -} // IOHIDDevice_GetUsage - -//************************************************************************* -// -// IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ) -// -// Purpose: get the usage page for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the usage page for this device -// - -uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePageKey), &result); - return (result); -} // IOHIDDevice_GetUsagePage - -//************************************************************************* -// -// IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef ) -// -// Purpose: get the UsagePairs CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: CFArrayRef - the UsagePairs for this device -// - -CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePairsKey) ) ); -} - -//************************************************************************* -// -// IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ) -// -// Purpose: get the PrimaryUsage CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the PrimaryUsage for this device -// - -uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey), &result); - return (result); -} // IOHIDDevice_GetPrimaryUsage - -//************************************************************************* -// -// IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ) -// -// Purpose: get the PrimaryUsagePage CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the PrimaryUsagePage for this device -// - -uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey), &result); - return (result); -} // IOHIDDevice_GetPrimaryUsagePage - -//************************************************************************* -// -// IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef ) -// -// Purpose: get the MaxInputReportSize CFString for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the MaxInputReportSize for this device -// - -uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxInputReportSizeKey), &result); - return (result); -} // IOHIDDevice_GetMaxInputReportSize - -//************************************************************************* -// -// IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef ) -// -// Purpose: get the MaxOutputReportSize for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the MaxOutput for this device -// - -uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey), &result); - return (result); -} // IOHIDDevice_GetMaxOutputReportSize - -//************************************************************************* -// -// IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef ) -// -// Purpose: get the MaxFeatureReportSize for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the MaxFeatureReportSize for this device -// - -uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey), &result); - return (result); -} // IOHIDDevice_GetMaxFeatureReportSize - -//************************************************************************* -// -// IOHIDDevice_GetReportInterval( inIOHIDDeviceRef ) -// -// Purpose: get the ReportInterval for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: uint32_t - the ReportInterval for this device -// -#ifndef kIOHIDReportIntervalKey -#define kIOHIDReportIntervalKey "ReportInterval" -#endif -uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { - uint32_t result = 0; - (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDReportIntervalKey), &result); - return (result); -} // IOHIDDevice_GetReportInterval - -//************************************************************************* -// -// IOHIDDevice_GetQueue( inIOHIDDeviceRef ) -// -// Purpose: get the Queue for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: IOHIDQueueRef - the Queue for this device -// - -IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef) { - IOHIDQueueRef result = 0; - (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (void *) &result); - if ( result ) { - assert( IOHIDQueueGetTypeID() == CFGetTypeID(result) ); - } - - return (result); -} // IOHIDDevice_GetQueue - -//************************************************************************* -// -// IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef ) -// -// Purpose: Set the Queue for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// inQueueRef - the Queue reference -// -// Returns: nothing -// - -void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef) { - IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), inQueueRef); -} - -//************************************************************************* -// -// IOHIDDevice_GetTransaction( inIOHIDDeviceRef ) -// -// Purpose: get the Transaction for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// -// Returns: IOHIDTransactionRef - the Transaction for this device -// - -IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef) { - IOHIDTransactionRef result = 0; - (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (void *) &result); - return (result); -} // IOHIDDevice_GetTransaction - -//************************************************************************* -// -// IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef ) -// -// Purpose: Set the Transaction for this device -// -// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device -// inTransactionRef - the Transaction reference -// -// Returns: nothing -// - -void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef) { - IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), inTransactionRef); -} - -//***************************************************** -#pragma mark - local (static) function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// IOHIDDevice_GetUInt32Property( inIOHIDDeviceRef, inKey, outValue ) -// -// Purpose: convieance function to return a uint32_t property of a device -// -// Inputs: inIOHIDDeviceRef - the device -// inKey - CFString for the -// outValue - address where to restore the element -// Returns: the action cookie -// outValue - the device -// - -static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t *outValue) { - Boolean result = FALSE; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - - CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); - if ( tCFTypeRef ) { - // if this is a number - if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { - // get it's value - result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); - } - } - } - - return (result); -} // IOHIDDevice_GetUInt32Property - -//************************************************************************* -// -// IOHIDDevice_SetUInt32Property( inIOHIDDeviceRef, inKey, inValue ) -// -// Purpose: convieance function to set a long property of an Device -// -// Inputs: inIOHIDDeviceRef - the Device -// inKey - CFString for the key -// inValue - the value to set it to -// Returns: nothing -// -#if 0 // unused -static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue) { - CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); - if ( tCFNumberRef ) { - IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef); - CFRelease(tCFNumberRef); - } -} // IOHIDDevice_SetUInt32Property -#endif -//************************************************************************* -// -// IOHIDDevice_GetPtrProperty( inIOHIDDeviceRef, inKey, outValue ) -// -// Purpose: convieance function to return a pointer property of a device -// -// Inputs: inIOHIDDeviceRef - the device -// inKey - CFString for the -// outValue - address where to restore the element -// Returns: the action cookie -// outValue - the device -// - -static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void **outValue) { - Boolean result = FALSE; - if ( inIOHIDDeviceRef ) { - assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); - - CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); - if ( tCFTypeRef ) { - // if this is a number - if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { - // get it's value -#ifdef __LP64__ - result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt64Type, outValue ); -#else - result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); -#endif // ifdef __LP64__ - } - } - } - - return (result); -} // IOHIDDevice_GetPtrProperty - -//************************************************************************* -// -// IOHIDDevice_SetPtrProperty( inIOHIDDeviceRef, inKey, inValue ) -// -// Purpose: convieance function to set a long property of an Device -// -// Inputs: inIOHIDDeviceRef - the Device -// inKey - CFString for the key -// inValue - the value to set it to -// Returns: nothing -// - -static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void *inValue) { -#ifdef __LP64__ - CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &inValue); -#else - CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); -#endif // ifdef __LP64__ - if ( tCFNumberRef ) { - IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef); - CFRelease(tCFNumberRef); - } -} // IOHIDDevice_SetPtrProperty - -//***************************************************** - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/IOHIDDevice_.h b/src/posix/cocoa/hid/IOHIDDevice_.h deleted file mode 100644 index eebccd667..000000000 --- a/src/posix/cocoa/hid/IOHIDDevice_.h +++ /dev/null @@ -1,422 +0,0 @@ -// File: IOHIDDevice_.h -// Abstract: convieance functions for IOHIDDeviceGetProperty -// Version: 2.0 + 5.3 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2014 Apple Inc. All Rights Reserved. -// -//***************************************************** -#ifndef __IOHIDDevice__ -#define __IOHIDDevice__ - -//***************************************************** -#pragma mark - includes & imports - -#include - -#include "IOHIDLib_.h" - -//***************************************************** -#if PRAGMA_ONCE -#pragma once -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if PRAGMA_IMPORT -#pragma import on -#endif - -#if PRAGMA_STRUCT_ALIGN -#pragma options align=mac68k -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(push, 2) -#elif PRAGMA_STRUCT_PACK -#pragma pack(2) -#endif - - //***************************************************** -#pragma mark - typedef's, struct's, enums, defines, etc. - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported globals - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported function prototypes - //----------------------------------------------------- - - //************************************************************************* - // - // HIDIsValidDevice( inIOHIDDeviceRef ) - // - // Purpose: validate this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: Boolean - TRUE if we find the device in our( internal ) device list - // - - extern Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetTransport( inIOHIDDeviceRef ) - // - // Purpose: get the Transport CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the Transport CFString for this device - // - - extern CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetVendorID( inIOHIDDeviceRef ) - // - // Purpose: get the vendor ID for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the vendor ID for this device - // - - extern uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef ) - // - // Purpose: get the VendorIDSource for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the VendorIDSource for this device - // - - extern uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetProductID( inIOHIDDeviceRef ) - // - // Purpose: get the product ID for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the product ID for this device - // - - extern uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef ) - // - // Purpose: get the VersionNumber CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the VersionNumber for this device - // - - extern uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetManufacturer( inIOHIDDeviceRef ) - // - // Purpose: get the Manufacturer CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the Manufacturer CFString for this device - // - - extern CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetProduct( inIOHIDDeviceRef ) - // - // Purpose: get the Product CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the Product CFString for this device - // - - extern CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef ) - // - // Purpose: get the SerialNumber CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the SerialNumber CFString for this device - // - - extern CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetCountryCode( inIOHIDDeviceRef ) - // - // Purpose: get the CountryCode CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the CountryCode for this device - // - - extern uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetLocationID( inIOHIDDeviceRef ) - // - // Purpose: get the location ID for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the location ID for this device - // - - extern uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetUsage( inIOHIDDeviceRef ) - // - // Purpose: get the usage for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the usage for this device - // - - extern uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ) - // - // Purpose: get the usage page for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the usage page for this device - // - - extern uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef ) - // - // Purpose: get the UsagePairs CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFArrayRef - the UsagePairs for this device - // - - extern CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ) - // - // Purpose: get the PrimaryUsage CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the PrimaryUsage CFString for this device - // - - extern uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ) - // - // Purpose: get the PrimaryUsagePage CFString for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: CFStringRef - the PrimaryUsagePage CFString for this device - // - - extern uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef ) - // - // Purpose: get the MaxInputReportSize for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the MaxInputReportSize for this device - // - - extern uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef ) - // - // Purpose: get the MaxOutputReportSize for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the MaxOutputReportSize for this device - // - - extern uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef ) - // - // Purpose: get the MaxFeatureReportSize for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the MaxFeatureReportSize for this device - // - - extern uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetReportInterval( inIOHIDDeviceRef ) - // - // Purpose: get the ReportInterval for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: uint32_t - the ReportInterval for this device - // - - extern uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_GetQueue( inIOHIDDeviceRef ) - // - // Purpose: get the Queue for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: IOHIDQueueRef - the Queue for this device - // - - extern IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef ) - // - // Purpose: Set the Queue for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // inQueueRef - the Queue - // - // Returns: nothing - // - - extern void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef); - - //************************************************************************* - // - // IOHIDDevice_GetTransaction( inIOHIDDeviceRef ) - // - // Purpose: get the Transaction for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // - // Returns: IOHIDTransactionRef - the Transaction for this device - // - - extern IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef); - - //************************************************************************* - // - // IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef ) - // - // Purpose: Set the Transaction for this device - // - // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device - // inTransactionRef - the Transaction - // - // Returns: nothing - // - - extern void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef); - - //***************************************************** -#if PRAGMA_STRUCT_ALIGN -#pragma options align=reset -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(pop) -#elif PRAGMA_STRUCT_PACK -#pragma pack() -#endif - -#ifdef PRAGMA_IMPORT_OFF -#pragma import off -#elif PRAGMA_IMPORT -#pragma import reset -#endif - -#ifdef __cplusplus -} -#endif - -#endif // __IOHIDDevice__ // diff --git a/src/posix/cocoa/hid/IOHIDElement_.c b/src/posix/cocoa/hid/IOHIDElement_.c deleted file mode 100644 index 906d2926a..000000000 --- a/src/posix/cocoa/hid/IOHIDElement_.c +++ /dev/null @@ -1,509 +0,0 @@ -// File: IOHIDElement_.c -// Abstract: convieance functions for IOHIDElementGetProperty -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#pragma mark - includes & imports -//----------------------------------------------------- - -#include "IOHIDElement_.h" - -//***************************************************** -#pragma mark - typedef's, struct's, enums, defines, etc. -//----------------------------------------------------- - -//***************************************************** -#pragma mark - local (static) function prototypes -//----------------------------------------------------- - -// static Boolean IOHIDElement_GetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long * outValue ); -// static void IOHIDElement_SetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long inValue ); - -//***************************************************** -#pragma mark - exported globals -//----------------------------------------------------- - -//***************************************************** -#pragma mark - local (static) globals -//----------------------------------------------------- - -//***************************************************** -#pragma mark - exported function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// HIDIsValidElement( inIOHIDElementRef ) -// -// Purpose: validate this element -// -// Inputs: inIOHIDElementRef - the element -// -// Returns: Boolean - TRUE if this is a valid element ref -// -Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef) { - Boolean result = FALSE; // assume failure (pessimist!) - if ( inIOHIDElementRef ) { - if ( CFGetTypeID(inIOHIDElementRef) ==IOHIDElementGetTypeID() ) { - result = TRUE; - } - } - - return (result); -} // HIDIsValidElement - -//************************************************************************* -// -// IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType ) -// -// Purpose: returns the current value for an element( polling ) -// -// Notes: will return 0 on error conditions which should be accounted for by application -// -// Inputs: inElementRef - the element -// inIOHIDValueScaleType - scale type ( calibrated or physical ) -// -// Returns: double - current value for element -// -double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType) { - long result = 0; - IOHIDValueRef tIOHIDValueRef; - if ( kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(inElementRef), inElementRef, &tIOHIDValueRef) ) { - result = IOHIDValueGetScaledValue(tIOHIDValueRef, inIOHIDValueScaleType); - } - - return (result); -} // IOHIDElement_GetValue - -//************************************************************************* -// -// IOHIDElement_GetCalibrationMin( inElementRef ) -// -// Purpose: get the minimum bounds for a calibrated value for this element -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the minimum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMaxKey), &result) ) { - result = 0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationMin - -//************************************************************************* -// -// IOHIDElement_SetCalibrationMin( inElementRef, inValue ) -// -// Purpose: set the minimum bounds for a calibrated value for this element -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the minimum bounds for a calibrated value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), inValue); -} // IOHIDElement_SetCalibrationMin - -//************************************************************************* -// -// IOHIDElement_GetCalibrationMax( inElementRef ) -// -// Purpose: get the maximum bounds for a calibrated value for this element -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the maximum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationMax - -//************************************************************************* -// -// IOHIDElement_SetCalibrationMax( inElementRef, inValue ) -// -// Purpose: set the maximum bounds for a calibrated value for this element -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the maximum Calibration value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), inValue); -} // IOHIDElement_SetCalibrationMax - -//************************************************************************* -// -// IOHIDElement_GetCalibrationSaturationMin( inElementRef ) -// -// Purpose: get the mininum tolerance to be used when calibrating a logical element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the maximum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationSaturationMin - -//************************************************************************* -// -// IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue ) -// -// Purpose: set the mininum tolerance to be used when calibrating a logical element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the maximum Calibration value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), inValue); -} // IOHIDElement_SetCalibrationSaturationMin - -//************************************************************************* -// -// IOHIDElement_GetCalibrationSaturationMax( inElementRef ) -// -// Purpose: get the maximum tolerance to be used when calibrating a logical element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the maximum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationSaturationMax - -//************************************************************************* -// -// IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue ) -// -// Purpose: set the maximum tolerance to be used when calibrating a logical element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the maximum Calibration value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), inValue); -} // IOHIDElement_SetCalibrationSaturationMax - -//************************************************************************* -// -// IOHIDElement_GetCalibrationDeadZoneMin( inElementRef ) -// -// Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the maximum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationDeadZoneMin - -//************************************************************************* -// -// IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue ) -// -// Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the maximum Calibration value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), inValue); -} // IOHIDElement_SetCalibrationDeadZoneMin - -//************************************************************************* -// -// IOHIDElement_GetCalibrationDeadZoneMax( inElementRef ) -// -// Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: CFIndex - the maximum Calibration value for this element -// - -CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationDeadZoneMax - -//************************************************************************* -// -// IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue ) -// -// Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the maximum Calibration value for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), inValue); -} // IOHIDElement_SetCalibrationDeadZoneMax - -//************************************************************************* -// -// IOHIDElement_GetCalibrationGranularity( inElementRef ) -// -// Purpose: get the level of detail returned for a calibrated element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: double_t - the maximum Calibration value for this element -// - -double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef) { - CFIndex result; - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), &result) ) { - if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { - result = -0x7FFFFFFF; - } - - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), result); - } - - return (result); -} // IOHIDElement_GetCalibrationGranularity - -//************************************************************************* -// -// IOHIDElement_SetCalibrationGranularity( inElementRef, inValue ) -// -// Purpose: set the level of detail returned for a calibrated element value -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// inValue - the the level of detail for this element -// -// Returns: nothing -// - -void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue) { - IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), inValue); -} // IOHIDElement_SetCalibrationGranularity - -//************************************************************************* -// -// IOHIDElement_SetupCalibration( inElementRef ) -// -// Purpose: set default values for the element calibration parameters -// -// Inputs: inElementRef - the IOHIDElementRef for this element -// -// Returns: nothing -// -void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef) { - // these are the min/max values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated ); - IOHIDElement_SetCalibrationMin( inIOHIDElementRef, IOHIDElementGetLogicalMin(inIOHIDElementRef) ); - IOHIDElement_SetCalibrationMax( inIOHIDElementRef, IOHIDElementGetLogicalMax(inIOHIDElementRef) ); - - // this is the granularity of the values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated ); - // for example if set to 0.1 the values returned will be multiples of 0.1 ( 0.1, 0.2, 0.3, etc. ) - IOHIDElement_SetCalibrationGranularity(inIOHIDElementRef, 0.); - - // these define the dead zone (like in the middel of joystick axis) - IOHIDElement_SetCalibrationDeadZoneMin(inIOHIDElementRef, 0); - IOHIDElement_SetCalibrationDeadZoneMax(inIOHIDElementRef, 0); -#if 1 - // get the current value of this element - double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical); - // use it as our min/mas saturation - IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value); - IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value); -#else - // calculate the middle physical value we would expect from this element - CFIndex valueMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); - CFIndex valueMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); - CFIndex valueMid = (valueMin + valueMax) / 2; - - // use it as our min/mas saturation - // this value determines the min/max values that have been recieved from the device element - IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, valueMid); - IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, valueMid); - - // get the current value of this element - double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical); - // and use it to adjust the current saturation values if it's outside their range - if ( value < IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef) ) { - IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value); - } - if ( value > IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef) ) { - IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value); - } - -#endif -} // IOHIDElement_SetupCalibration -//***************************************************** -#pragma mark - local (static) function implementations -//----------------------------------------------------- - -//************************************************************************* -// -// IOHIDElement_GetLongProperty( inElementRef, inKey, outValue ) -// -// Purpose: convieance function to return a long property of an element -// -// Inputs: inElementRef - the element -// inKey - CFString for the key -// outValue - address where to store the value -// Returns: Boolean - TRUE if successful -// outValue - the long property's value -// - -Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue) { - Boolean result = FALSE; - - CFTypeRef tCFTypeRef = IOHIDElementGetProperty(inElementRef, inKey); - if ( tCFTypeRef ) { - // if this is a number - if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { - // get it's value - result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); - } - } - - return (result); -} /* IOHIDElement_GetLongProperty */ - -//************************************************************************* -// -// IOHIDElement_SetLongProperty( inElementRef, inKey, inValue ) -// -// Purpose: convieance function to set a long property of an element -// -// Inputs: inElementRef - the element -// inKey - CFString for the key -// inValue - the value to set it to -// -// Returns: nothing -// - -void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue) { - CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); - if ( tCFNumberRef ) { - IOHIDElementSetProperty(inElementRef, inKey, tCFNumberRef); - CFRelease(tCFNumberRef); - } -} // IOHIDElement_SetLongProperty - -//***************************************************** - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/IOHIDElement_.h b/src/posix/cocoa/hid/IOHIDElement_.h deleted file mode 100644 index a8a631668..000000000 --- a/src/posix/cocoa/hid/IOHIDElement_.h +++ /dev/null @@ -1,339 +0,0 @@ -// File: IOHIDElement_.h -// Abstract: convieance functions for IOHIDElementGetProperty -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** -#ifndef __IOHIDElement___ -#define __IOHIDElement___ - -//***************************************************** -#pragma mark - includes & imports - -#include - -#include "IOHIDLib_.h" -//***************************************************** -#if PRAGMA_ONCE -#pragma once -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if PRAGMA_IMPORT -#pragma import on -#endif - -#if PRAGMA_STRUCT_ALIGN -#pragma options align=mac68k -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(push, 2) -#elif PRAGMA_STRUCT_PACK -#pragma pack(2) -#endif - - //***************************************************** -#pragma mark - typedef's, struct's, enums, defines, etc. - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported globals - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported function prototypes - //----------------------------------------------------- - - //************************************************************************* - // - // HIDIsValidElement( inIOHIDElementRef ) - // - // Purpose: validate this element - // - // Inputs: inIOHIDElementRef - the element - // - // Returns: Boolean - TRUE if this is a valid element ref - // - extern Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef); - - //************************************************************************* - // - // IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType ) - // - // Purpose: returns the current value for an element( polling ) - // - // Notes: will return 0 on error conditions which should be accounted for by application - // - // Inputs: inElementRef - the element - // inIOHIDValueScaleType - scale type ( calibrated or physical ) - // - // Returns: double - current value for element - // - extern double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationMin( inElementRef ) - // - // Purpose: get the minimum bounds for a calibrated value for this element - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the minimum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationMin( inElementRef, inValue ) - // - // Purpose: set the minimum bounds for a calibrated value for this element - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the minimum bounds for a calibrated value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationMax( inElementRef ) - // - // Purpose: get the maximum bounds for a calibrated value for this element - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the maximum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationMax( inElementRef, inValue ) - // - // Purpose: set the maximum bounds for a calibrated value for this element - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the maximum Calibration value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationSaturationMin( inElementRef ) - // - // Purpose: get the mininum tolerance to be used when calibrating a logical element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the maximum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue ) - // - // Purpose: set the mininum tolerance to be used when calibrating a logical element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the maximum Calibration value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationSaturationMax( inElementRef ) - // - // Purpose: get the maximum tolerance to be used when calibrating a logical element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the maximum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue ) - // - // Purpose: set the maximum tolerance to be used when calibrating a logical element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the maximum Calibration value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationDeadZoneMin( inElementRef ) - // - // Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the maximum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue ) - // - // Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the maximum Calibration value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationDeadZoneMax( inElementRef ) - // - // Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: CFIndex - the maximum Calibration value for this element - // - - extern CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue ) - // - // Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the maximum Calibration value for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue); - - //************************************************************************* - // - // IOHIDElement_GetCalibrationGranularity( inElementRef ) - // - // Purpose: get the level of detail returned for a calibrated element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: double_t - the maximum Calibration value for this element - // - - extern double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef); - - //************************************************************************* - // - // IOHIDElement_SetCalibrationGranularity( inElementRef, inValue ) - // - // Purpose: set the level of detail returned for a calibrated element value - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // inValue - the the level of detail for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue); - - //************************************************************************* - // - // IOHIDElement_SetupCalibration( inElementRef ) - // - // Purpose: set default values for the element calibration parameters - // - // Inputs: inElementRef - the IOHIDElementRef for this element - // - // Returns: nothing - // - - extern void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef); - - extern Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue); - extern void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue); - - //***************************************************** -#if PRAGMA_STRUCT_ALIGN -#pragma options align=reset -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(pop) -#elif PRAGMA_STRUCT_PACK -#pragma pack() -#endif - -#ifdef PRAGMA_IMPORT_OFF -#pragma import off -#elif PRAGMA_IMPORT -#pragma import reset -#endif - -#ifdef __cplusplus -} -#endif - -#endif // __IOHIDElement___ // diff --git a/src/posix/cocoa/hid/IOHIDLib_.h b/src/posix/cocoa/hid/IOHIDLib_.h deleted file mode 100644 index 38c6248ac..000000000 --- a/src/posix/cocoa/hid/IOHIDLib_.h +++ /dev/null @@ -1,111 +0,0 @@ -// File: IOHIDLib_.h -// Abstract: Single include file for all header files of IOHIDLib -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** -#ifndef __IOHIDLib___ -#define __IOHIDLib___ - -//***************************************************** -#pragma mark - includes & imports -//----------------------------------------------------- -#include - -#include "IOHIDDevice_.h" -#include "IOHIDElement_.h" - -#include "ImmrHIDUtilAddOn.h" - -//***************************************************** -#if PRAGMA_ONCE -#pragma once -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if PRAGMA_IMPORT -#pragma import on -#endif - -#if PRAGMA_STRUCT_ALIGN -#pragma options align=mac68k -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(push, 2) -#elif PRAGMA_STRUCT_PACK -#pragma pack(2) -#endif - - //***************************************************** -#pragma mark - typedef's, struct's, enums, defines, etc. - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported globals - //----------------------------------------------------- - - //***************************************************** -#pragma mark - exported function prototypes - //----------------------------------------------------- - - //***************************************************** -#if PRAGMA_STRUCT_ALIGN -#pragma options align=reset -#elif PRAGMA_STRUCT_PACKPUSH -#pragma pack(pop) -#elif PRAGMA_STRUCT_PACK -#pragma pack() -#endif - -#ifdef PRAGMA_IMPORT_OFF -#pragma import off -#elif PRAGMA_IMPORT -#pragma import reset -#endif - -#ifdef __cplusplus -} -#endif - -#endif // __IOHIDLib___ diff --git a/src/posix/cocoa/hid/ImmrHIDUtilAddOn.c b/src/posix/cocoa/hid/ImmrHIDUtilAddOn.c deleted file mode 100644 index 4937d3687..000000000 --- a/src/posix/cocoa/hid/ImmrHIDUtilAddOn.c +++ /dev/null @@ -1,108 +0,0 @@ -// File: ImmrHIDUtilAddOn.c -// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#include -#include - -#include "ImmrHIDUtilAddOn.h" - -//--------------------------------------------------------------------------------- -// -// AllocateHIDObjectFromIOHIDDeviceRef( ) -// -// returns: -// NULL, or acceptable io_object_t -// -//--------------------------------------------------------------------------------- -io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef) { - io_service_t result = 0L; - if ( inIOHIDDeviceRef ) { - // Set up the matching criteria for the devices we're interested in. - // We are interested in instances of class IOHIDDevice. - // matchingDict is consumed below( in IOServiceGetMatchingService ) - // so we have no leak here. - CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOHIDDeviceKey); - if ( matchingDict ) { - // Add a key for locationID to our matching dictionary. This works for matching to - // IOHIDDevices, so we will only look for a device attached to that particular port - // on the machine. - CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey) ); - if ( tCFTypeRef ) { - CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDLocationIDKey), tCFTypeRef); - // CFRelease( tCFTypeRef ); // don't release objects that we "Get". - - // IOServiceGetMatchingService assumes that we already know that there is only one device - // that matches. This way we don't have to do the whole iteration dance to look at each - // device that matches. This is a new API in 10.2 - result = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict); - } - - // Note: We're not leaking the matchingDict. - // One reference is consumed by IOServiceGetMatchingServices - } - } - - return (result); -} // AllocateHIDObjectFromIOHIDDeviceRef - -//--------------------------------------------------------------------------------- -// -// FreeHIDObject( ) -// -//--------------------------------------------------------------------------------- -bool FreeHIDObject(io_service_t inHIDObject) { - kern_return_t kr; - - kr = IOObjectRelease(inHIDObject); - - return (kIOReturnSuccess == kr); -} // FreeHIDObject - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/posix/cocoa/hid/ImmrHIDUtilAddOn.h b/src/posix/cocoa/hid/ImmrHIDUtilAddOn.h deleted file mode 100644 index 72de752e3..000000000 --- a/src/posix/cocoa/hid/ImmrHIDUtilAddOn.h +++ /dev/null @@ -1,50 +0,0 @@ -// File: ImmrHIDUtilAddOn.h -// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's -// Version: 2.0 -// -// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple -// Inc. ("Apple") in consideration of your agreement to the following -// terms, and your use, installation, modification or redistribution of -// this Apple software constitutes acceptance of these terms. If you do -// not agree with these terms, please do not use, install, modify or -// redistribute this Apple software. -// -// In consideration of your agreement to abide by the following terms, and -// subject to these terms, Apple grants you a personal, non-exclusive -// license, under Apple's copyrights in this original Apple software (the -// "Apple Software"), to use, reproduce, modify and redistribute the Apple -// Software, with or without modifications, in source and/or binary forms; -// provided that if you redistribute the Apple Software in its entirety and -// without modifications, you must retain this notice and the following -// text and disclaimers in all such redistributions of the Apple Software. -// Neither the name, trademarks, service marks or logos of Apple Inc. may -// be used to endorse or promote products derived from the Apple Software -// without specific prior written permission from Apple. Except as -// expressly stated in this notice, no other rights or licenses, express or -// implied, are granted by Apple herein, including but not limited to any -// patent rights that may be infringed by your derivative works or by other -// works in which the Apple Software may be incorporated. -// -// The Apple Software is provided by Apple on an "AS IS" basis. APPLE -// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. -// -// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Copyright (C) 2009 Apple Inc. All Rights Reserved. -// -//***************************************************** -#include -#include - -extern io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef); -extern bool FreeHIDObject(io_object_t inHIDObject); diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index 453f2898f..ea51e1c88 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -2,7 +2,7 @@ ** i_common.h ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -47,8 +47,6 @@ inline bool I_IsHiDPISupported() void I_ProcessEvent(NSEvent* event); -void I_StartupJoysticks(); -void I_ShutdownJoysticks(); void I_ProcessJoysticks(); NSSize I_GetContentViewSize(const NSWindow* window); diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index 29f98fcd1..b102cf5a0 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -2,7 +2,7 @@ ** i_joystick.cpp ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,116 +31,147 @@ ** */ -#include "m_joy.h" - -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - -#include "hid/HID_Utilities_External.h" +#include +#include +#include +#include #include "d_event.h" #include "doomdef.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_joy.h" #include "templates.h" -#include "i_osversion.h" +#include "v_text.h" namespace { -FString ToFString( const CFStringRef string ) +FString ToFString(const CFStringRef string) { - if ( NULL == string ) + if (NULL == string) { return FString(); } - const CFIndex stringLength = CFStringGetLength( string ); - - if ( 0 == stringLength ) + const CFIndex stringLength = CFStringGetLength(string); + + if (0 == stringLength) { return FString(); } - const size_t bufferSize = CFStringGetMaximumSizeForEncoding( stringLength, kCFStringEncodingUTF8 ) + 1; - - char buffer[ bufferSize ]; - memset( buffer, 0, bufferSize ); - - CFStringGetCString( string, buffer, bufferSize, kCFStringEncodingUTF8 ); - - return FString( buffer ); + const size_t bufferSize = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 1; + + char buffer[bufferSize]; + memset(buffer, 0, bufferSize); + + CFStringGetCString(string, buffer, bufferSize, kCFStringEncodingUTF8); + + return FString(buffer); } class IOKitJoystick : public IJoystickConfig { public: - explicit IOKitJoystick( IOHIDDeviceRef device ); + explicit IOKitJoystick(io_object_t device); virtual ~IOKitJoystick(); - + virtual FString GetName(); virtual float GetSensitivity(); - virtual void SetSensitivity( float scale ); - + virtual void SetSensitivity(float scale); + virtual int GetNumAxes(); - virtual float GetAxisDeadZone( int axis ); - virtual EJoyAxis GetAxisMap( int axis ); - virtual const char* GetAxisName( int axis ); - virtual float GetAxisScale( int axis ); - - virtual void SetAxisDeadZone( int axis, float deadZone ); - virtual void SetAxisMap( int axis, EJoyAxis gameAxis ); - virtual void SetAxisScale( int axis, float scale ); - + virtual float GetAxisDeadZone(int axis); + virtual EJoyAxis GetAxisMap(int axis); + virtual const char* GetAxisName(int axis); + virtual float GetAxisScale(int axis); + + virtual void SetAxisDeadZone(int axis, float deadZone); + virtual void SetAxisMap(int axis, EJoyAxis gameAxis); + virtual void SetAxisScale(int axis, float scale); + virtual bool IsSensitivityDefault(); - virtual bool IsAxisDeadZoneDefault( int axis ); - virtual bool IsAxisMapDefault( int axis ); - virtual bool IsAxisScaleDefault( int axis ); - + virtual bool IsAxisDeadZoneDefault(int axis); + virtual bool IsAxisMapDefault(int axis); + virtual bool IsAxisScaleDefault(int axis); + virtual void SetDefaultConfig(); virtual FString GetIdentifier(); - - void AddAxes( float axes[ NUM_JOYAXIS ] ) const; - + + void AddAxes(float axes[NUM_JOYAXIS]) const; + void Update(); - + private: - IOHIDDeviceRef m_device; - + IOHIDDeviceInterface** m_interface; + IOHIDQueueInterface** m_queue; + + FString m_name; + FString m_identifier; + float m_sensitivity; - - struct AxisInfo + + struct AnalogAxis { - char name[ 64 ]; - + IOHIDElementCookie cookie; + + char name[64]; + float value; - + + int32_t minValue; + int32_t maxValue; + float deadZone; float defaultDeadZone; float sensitivity; float defaultSensitivity; - + EJoyAxis gameAxis; EJoyAxis defaultGameAxis; - - IOHIDElementRef element; - }; - - TArray< AxisInfo > m_axes; - - TArray< IOHIDElementRef > m_buttons; - TArray< IOHIDElementRef > m_POVs; - + AnalogAxis() + { + memset(this, 0, sizeof *this); + } + }; + + TArray m_axes; + + struct DigitalButton + { + IOHIDElementCookie cookie; + int32_t value; + + DigitalButton(const IOHIDElementCookie cookie) + : cookie(cookie) + { } + }; + + TArray m_buttons; + TArray m_POVs; + + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; - - - bool ProcessAxis ( const IOHIDValueRef value ); - bool ProcessButton( const IOHIDValueRef value ); - bool ProcessPOV ( const IOHIDValueRef value ); - + + void ProcessAxes(); + bool ProcessAxis (const IOHIDEventStruct& event); + bool ProcessButton(const IOHIDEventStruct& event); + bool ProcessPOV (const IOHIDEventStruct& event); + + void GatherDeviceInfo(io_object_t device, CFDictionaryRef properties); + + static void GatherElementsHandler(const void* value, void* parameter); + void GatherCollectionElements(CFDictionaryRef properties); + + void AddAxis(CFDictionaryRef element); + void AddButton(CFDictionaryRef element); + void AddPOV(CFDictionaryRef element); + void AddToQueue(IOHIDElementCookie cookie); }; @@ -148,90 +179,136 @@ const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f; const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f; -IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device ) -: m_device( device ) -, m_sensitivity( DEFAULT_SENSITIVITY ) +IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device) { - assert(NULL != device); - assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device)); + IOCFPlugInInterface** plugInInterface = NULL; + SInt32 score = 0; - CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - assert(NULL != elements); - assert(CFArrayGetTypeID() == CFGetTypeID(elements)); + const kern_return_t pluginResult = IOCreatePlugInInterfaceForService(device, + kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); - for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) + IOHIDDeviceInterface** interface = NULL; + + if (KERN_SUCCESS == pluginResult) { - const IOHIDElementRef element = - static_cast(const_cast(CFArrayGetValueAtIndex(elements, i))); - assert(NULL != element); - assert(IOHIDElementGetTypeID() == CFGetTypeID(element)); + // Call a method of the intermediate plug-in to create the device interface - const uint32_t usagePage = IOHIDElementGetUsagePage( element ); + const HRESULT queryResult = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), reinterpret_cast(&interface)); - if ( kHIDPage_GenericDesktop == usagePage ) + IODestroyPlugInInterface(plugInInterface); // [?] or maybe (*plugInInterface)->Release(plugInInterface); + + if (S_OK == queryResult) { - const uint32_t usage = IOHIDElementGetUsage( element ); + const IOReturn openResult = (*interface)->open(interface, 0); - if ( kHIDUsage_GD_Slider == usage - || kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage - || kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage ) + if (kIOReturnSuccess != openResult) { - AxisInfo axis; - memset( &axis, 0, sizeof( axis ) ); + (*interface)->Release(interface); - if ( const CFStringRef name = IOHIDElementGetName( element ) ) - { - CFStringGetCString( name, axis.name, sizeof( axis.name ) - 1, kCFStringEncodingUTF8 ); - } - else - { - snprintf( axis.name, sizeof( axis.name ), "Axis %i", m_axes.Size() + 1 ); - } - - axis.element = element; - - m_axes.Push( axis ); - - IOHIDElement_SetCalibrationMin( element, -1 ); - IOHIDElement_SetCalibrationMax( element, 1 ); - - HIDQueueElement( m_device, element ); - } - else if ( kHIDUsage_GD_Hatswitch == usage && m_POVs.Size() < 4 ) - { - m_POVs.Push( element ); - - HIDQueueElement( m_device, element ); + Printf(TEXTCOLOR_RED "IOHIDDeviceInterface::open() failed with code 0x%08X\n", openResult); + return NULL; } } - else if ( kHIDPage_Button == usagePage ) + else { - m_buttons.Push( element ); - - HIDQueueElement( m_device, element ); + Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", queryResult); + return NULL; } } + else + { + Printf(TEXTCOLOR_RED "IOCreatePlugInInterfaceForService() failed with code %i\n", pluginResult); + return NULL; + } - CFRelease(elements); + return interface; +} + +IOHIDQueueInterface** CreateDeviceQueue(IOHIDDeviceInterface** const interface) +{ + if (NULL == interface) + { + return NULL; + } + + IOHIDQueueInterface** queue = (*interface)->allocQueue(interface); + + if (NULL == queue) + { + Printf(TEXTCOLOR_RED "IOHIDDeviceInterface::allocQueue() failed\n"); + return NULL; + } + + static const uint32_t QUEUE_FLAGS = 0; + static const uint32_t QUEUE_DEPTH = 0; + + const IOReturn queueResult = (*queue)->create(queue, QUEUE_FLAGS, QUEUE_DEPTH); + + if (kIOReturnSuccess != queueResult) + { + (*queue)->Release(queue); + + Printf(TEXTCOLOR_RED "IOHIDQueueInterface::create() failed with code 0x%08X\n", queueResult); + return NULL; + } + + return queue; +} + + +IOKitJoystick::IOKitJoystick(const io_object_t device) +: m_interface(CreateDeviceInterface(device)) +, m_queue(CreateDeviceQueue(m_interface)) +, m_sensitivity(DEFAULT_SENSITIVITY) +{ + if (NULL == m_interface || NULL == m_queue) + { + return; + } + + CFMutableDictionaryRef properties = NULL; + const kern_return_t propertiesResult = + IORegistryEntryCreateCFProperties(device, &properties, kCFAllocatorDefault, kNilOptions); + + if (KERN_SUCCESS != propertiesResult || NULL == properties) + { + Printf(TEXTCOLOR_RED "IORegistryEntryCreateCFProperties() failed with code %i\n", propertiesResult); + return; + } + + GatherDeviceInfo(device, properties); + GatherCollectionElements(properties); + + CFRelease(properties); + + (*m_queue)->start(m_queue); SetDefaultConfig(); } IOKitJoystick::~IOKitJoystick() { - M_SaveJoystickConfig( this ); + M_SaveJoystickConfig(this); + + if (NULL != m_queue) + { + (*m_queue)->stop(m_queue); + (*m_queue)->dispose(m_queue); + (*m_queue)->Release(m_queue); + } + + if (NULL != m_interface) + { + (*m_interface)->close(m_interface); + (*m_interface)->Release(m_interface); + } } FString IOKitJoystick::GetName() { - FString result; - - result += ToFString( IOHIDDevice_GetManufacturer( m_device ) ); - result += " "; - result += ToFString( IOHIDDevice_GetProduct( m_device ) ); - - return result; + return m_name; } @@ -240,7 +317,7 @@ float IOKitJoystick::GetSensitivity() return m_sensitivity; } -void IOKitJoystick::SetSensitivity( float scale ) +void IOKitJoystick::SetSensitivity(float scale) { m_sensitivity = scale; } @@ -248,54 +325,54 @@ void IOKitJoystick::SetSensitivity( float scale ) int IOKitJoystick::GetNumAxes() { - return static_cast< int >( m_axes.Size() ); + return static_cast(m_axes.Size()); } -#define IS_AXIS_VALID ( static_cast< unsigned int >( axis ) < m_axes.Size() ) +#define IS_AXIS_VALID (static_cast(axis) < m_axes.Size()) -float IOKitJoystick::GetAxisDeadZone( int axis ) +float IOKitJoystick::GetAxisDeadZone(int axis) { - return IS_AXIS_VALID ? m_axes[ axis ].deadZone : 0.0f; + return IS_AXIS_VALID ? m_axes[axis].deadZone : 0.0f; } -EJoyAxis IOKitJoystick::GetAxisMap( int axis ) +EJoyAxis IOKitJoystick::GetAxisMap(int axis) { - return IS_AXIS_VALID ? m_axes[ axis ].gameAxis : JOYAXIS_None; + return IS_AXIS_VALID ? m_axes[axis].gameAxis : JOYAXIS_None; } -const char* IOKitJoystick::GetAxisName( int axis ) +const char* IOKitJoystick::GetAxisName(int axis) { - return IS_AXIS_VALID ? m_axes[ axis ].name : "Invalid"; + return IS_AXIS_VALID ? m_axes[axis].name : "Invalid"; } -float IOKitJoystick::GetAxisScale( int axis ) +float IOKitJoystick::GetAxisScale(int axis) { - return IS_AXIS_VALID ? m_axes[ axis ].sensitivity : 0.0f; + return IS_AXIS_VALID ? m_axes[axis].sensitivity : 0.0f; } -void IOKitJoystick::SetAxisDeadZone( int axis, float deadZone ) +void IOKitJoystick::SetAxisDeadZone(int axis, float deadZone) { - if ( IS_AXIS_VALID ) + if (IS_AXIS_VALID) { - m_axes[ axis ].deadZone = clamp( deadZone, 0.0f, 1.0f ); + m_axes[axis].deadZone = clamp(deadZone, 0.0f, 1.0f); } } -void IOKitJoystick::SetAxisMap( int axis, EJoyAxis gameAxis ) +void IOKitJoystick::SetAxisMap(int axis, EJoyAxis gameAxis) { - if ( IS_AXIS_VALID ) + if (IS_AXIS_VALID) { - m_axes[ axis ].gameAxis = ( gameAxis > JOYAXIS_None && gameAxis < NUM_JOYAXIS ) + m_axes[axis].gameAxis = (gameAxis> JOYAXIS_None && gameAxis = 3 ) + + else if (axisCount >= 3) { m_axes[0].gameAxis = JOYAXIS_Side; m_axes[1].gameAxis = JOYAXIS_Forward; m_axes[2].gameAxis = JOYAXIS_Yaw; - + // Four axes? First two are movement, last two are looking around. - - if ( axisCount >= 4 ) + + if (axisCount >= 4) { m_axes[3].gameAxis = JOYAXIS_Pitch; // ??? m_axes[3].sensitivity = 0.75f; - + // Five axes? Use the fifth one for moving up and down. - - if ( axisCount >= 5 ) + + if (axisCount >= 5) { m_axes[4].gameAxis = JOYAXIS_Up; } } } - + // If there is only one axis, then we make no assumptions about how // the user might want to use it. - + // Preserve defaults for config saving. - - for ( size_t i = 0; i < axisCount; ++i ) + + for (size_t i = 0; i < axisCount; ++i) { m_axes[i].defaultDeadZone = m_axes[i].deadZone; m_axes[i].defaultSensitivity = m_axes[i].sensitivity; @@ -389,180 +466,400 @@ void IOKitJoystick::SetDefaultConfig() FString IOKitJoystick::GetIdentifier() { - char identifier[ 32 ] = {0}; - - snprintf( identifier, sizeof( identifier ), "VID_%04x_PID_%04x", - IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) ); - - return FString( identifier ); + return m_identifier; } -void IOKitJoystick::AddAxes( float axes[ NUM_JOYAXIS ] ) const +void IOKitJoystick::AddAxes(float axes[NUM_JOYAXIS]) const { - for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) { const EJoyAxis axis = m_axes[i].gameAxis; - - if ( JOYAXIS_None == axis ) + + if (JOYAXIS_None == axis) { continue; } - - axes[ axis ] -= m_axes[i].value; + + axes[axis] -= m_axes[i].value; } } void IOKitJoystick::Update() { - IOHIDValueRef value = NULL; - - while ( HIDGetEvent( m_device, &value ) && NULL != value ) + if (NULL == m_queue) { - ProcessAxis( value ) || ProcessButton( value ) || ProcessPOV( value ); - - CFRelease( value ); + return; + } + + IOHIDEventStruct event = { }; + AbsoluteTime zeroTime = { }; + + const IOReturn eventResult = (*m_queue)->getNextEvent(m_queue, &event, zeroTime, 0); + + if (kIOReturnSuccess == eventResult) + { + if (use_joystick) + { + ProcessAxis(event) || ProcessButton(event) || ProcessPOV(event); + } + } + else if (kIOReturnUnderrun != eventResult) + { + Printf(TEXTCOLOR_RED "IOHIDQueueInterface::getNextEvent() failed with code 0x%08X\n", eventResult); } } -bool IOKitJoystick::ProcessAxis( const IOHIDValueRef value ) +void IOKitJoystick::ProcessAxes() { - const IOHIDElementRef element = IOHIDValueGetElement( value ); - - if ( NULL == element ) + if (NULL == m_interface) { - return false; + return; } - for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) { - if ( element != m_axes[i].element ) + AnalogAxis& axis = m_axes[i]; + + static const double scaledMin = -1; + static const double scaledMax = 1; + + IOHIDEventStruct event; + + if (kIOReturnSuccess == (*m_interface)->getElementValue(m_interface, axis.cookie, &event)) + { + const double scaledValue = scaledMin + + (event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue); + const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL); + + axis.value = static_cast(filteredValue * m_sensitivity * axis.sensitivity); + } + else + { + axis.value = 0.0f; + } + } +} + + +bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event) +{ + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + if (event.elementCookie != m_axes[i].cookie) { continue; } - - AxisInfo& axis = m_axes[i]; - - const double scaledValue = IOHIDValueGetScaledValue( value, kIOHIDValueScaleTypeCalibrated ); - const double filteredValue = Joy_RemoveDeadZone( scaledValue, axis.deadZone, NULL ); - - axis.value = static_cast< float >( filteredValue * m_sensitivity * axis.sensitivity ); - + + AnalogAxis& axis = m_axes[i]; + + static const double scaledMin = -1; + static const double scaledMax = 1; + + const double scaledValue = scaledMin + + (event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue); + const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL); + + axis.value = static_cast(filteredValue * m_sensitivity * axis.sensitivity); + return true; } - + return false; } -bool IOKitJoystick::ProcessButton( const IOHIDValueRef value ) +bool IOKitJoystick::ProcessButton(const IOHIDEventStruct& event) { - const IOHIDElementRef element = IOHIDValueGetElement( value ); - - if ( NULL == element ) + for (size_t i = 0, count = m_buttons.Size(); i < count; ++i) { - return false; - } - - for ( size_t i = 0, count = m_buttons.Size(); i < count; ++i ) - { - if ( element != m_buttons[i] ) + if (event.elementCookie != m_buttons[i].cookie) { continue; } - - const int newButton = IOHIDValueGetIntegerValue( value ) & 1; - const int oldButton = ~newButton; - - Joy_GenerateButtonEvents( oldButton, newButton, 1, - static_cast< int >( KEY_FIRSTJOYBUTTON + i ) ); - + + int32_t& current = m_buttons[i].value; + const int32_t previous = current; + current = event.value; + + Joy_GenerateButtonEvents(previous, current, 1, static_cast(KEY_FIRSTJOYBUTTON + i)); + return true; } - + return false; } -bool IOKitJoystick::ProcessPOV( const IOHIDValueRef value ) +bool IOKitJoystick::ProcessPOV(const IOHIDEventStruct& event) { - const IOHIDElementRef element = IOHIDValueGetElement( value ); - - if ( NULL == element ) + for (size_t i = 0, count = m_POVs.Size(); i ( baseButton + i * 4 ) ); + return true; } - + return false; } +void IOKitJoystick::GatherDeviceInfo(const io_object_t device, const CFDictionaryRef properties) +{ + assert(NULL != properties); + + CFStringRef vendorRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDManufacturerKey))); + CFStringRef productRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDProductKey))); + CFNumberRef vendorIDRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDVendorIDKey))); + CFNumberRef productIDRef = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDProductIDKey))); + + CFMutableDictionaryRef usbProperties = NULL; + + if ( NULL == vendorRef || NULL == productRef + || NULL == vendorIDRef || NULL == productIDRef) + { + // OS X is not mirroring all USB properties to HID page, so need to look at USB device page also + // Step up two levels and get dictionary of USB properties + + io_registry_entry_t parent1; + kern_return_t ioResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent1); + + if (KERN_SUCCESS == ioResult) + { + io_registry_entry_t parent2; + ioResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent2); + + if (KERN_SUCCESS == ioResult) + { + ioResult = IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions); + + if (KERN_SUCCESS != ioResult) + { + Printf(TEXTCOLOR_RED "IORegistryEntryCreateCFProperties() failed with code %i\n", ioResult); + } + + IOObjectRelease(parent2); + } + else + { + Printf(TEXTCOLOR_RED "IORegistryEntryGetParentEntry(2) failed with code %i\n", ioResult); + } + + IOObjectRelease(parent1); + } + else + { + Printf(TEXTCOLOR_RED "IORegistryEntryGetParentEntry(1) failed with code %i\n", ioResult); + } + } + + if (NULL != usbProperties) + { + if (NULL == vendorRef) + { + vendorRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("USB Vendor Name"))); + } + + if (NULL == productRef) + { + productRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("USB Product Name"))); + } + + if (NULL == vendorIDRef) + { + vendorIDRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("idVendor"))); + } + + if (NULL == productIDRef) + { + productIDRef = static_cast( + CFDictionaryGetValue(usbProperties, CFSTR("idProduct"))); + } + } + + m_name += ToFString(vendorRef); + m_name += " "; + m_name += ToFString(productRef); + + int vendorID = 0, productID = 0; + + if (NULL != vendorIDRef) + { + CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); + } + + if (NULL != productIDRef) + { + CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); + } + + m_identifier.AppendFormat("VID_%04x_PID_%04x", vendorID, productID); + + if (NULL != usbProperties) + { + CFRelease(usbProperties); + } +} + + +long GetElementValue(const CFDictionaryRef element, const CFStringRef key) +{ + const CFNumberRef number = + static_cast(CFDictionaryGetValue(element, key)); + long result = 0; + + if (NULL != number && CFGetTypeID(number) == CFNumberGetTypeID()) + { + CFNumberGetValue(number, kCFNumberLongType, &result); + } + + return result; +} + +void IOKitJoystick::GatherElementsHandler(const void* value, void* parameter) +{ + assert(NULL != value); + assert(NULL != parameter); + + const CFDictionaryRef element = static_cast(value); + IOKitJoystick* thisPtr = static_cast(parameter); + + if (CFGetTypeID(element) != CFDictionaryGetTypeID()) + { + Printf(TEXTCOLOR_RED "IOKitJoystick: Encountered wrong element type\n"); + return; + } + + const long type = GetElementValue(element, CFSTR(kIOHIDElementTypeKey)); + + if (kIOHIDElementTypeCollection == type) + { + thisPtr->GatherCollectionElements(element); + } + else if (0 != type) + { + const long usagePage = GetElementValue(element, CFSTR(kIOHIDElementUsagePageKey)); + + if (kHIDPage_GenericDesktop == usagePage) + { + const long usage = GetElementValue(element, CFSTR(kIOHIDElementUsageKey)); + + if ( kHIDUsage_GD_Slider == usage + || kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage + || kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage) + { + thisPtr->AddAxis(element); + } + else if (kHIDUsage_GD_Hatswitch == usage && thisPtr->m_POVs.Size() < 4) + { + thisPtr->AddPOV(element); + } + } + else if (kHIDPage_Button == usagePage) + { + thisPtr->AddButton(element); + } + } +} + +void IOKitJoystick::GatherCollectionElements(const CFDictionaryRef properties) +{ + const CFArrayRef topElement = static_cast( + CFDictionaryGetValue(properties, CFSTR(kIOHIDElementKey))); + + if (NULL == topElement || CFGetTypeID(topElement) != CFArrayGetTypeID()) + { + Printf(TEXTCOLOR_RED "GatherCollectionElements: invalid properties dictionary\n"); + return; + } + + const CFRange range = { 0, CFArrayGetCount(topElement) }; + + CFArrayApplyFunction(topElement, range, GatherElementsHandler, this); +} + + +IOHIDElementCookie GetElementCookie(const CFDictionaryRef element) +{ + // Use C-style cast to avoid 32/64-bit IOHIDElementCookie type issue + return (IOHIDElementCookie)GetElementValue(element, CFSTR(kIOHIDElementCookieKey)); +} + +void IOKitJoystick::AddAxis(const CFDictionaryRef element) +{ + AnalogAxis axis; + + axis.cookie = GetElementCookie(element); + axis.minValue = GetElementValue(element, CFSTR(kIOHIDElementMinKey)); + axis.maxValue = GetElementValue(element, CFSTR(kIOHIDElementMaxKey)); + + const CFStringRef nameRef = static_cast( + CFDictionaryGetValue(element, CFSTR(kIOHIDElementNameKey))); + + if (NULL != nameRef && CFStringGetTypeID() == CFGetTypeID(nameRef)) + { + CFStringGetCString(nameRef, axis.name, sizeof(axis.name) - 1, kCFStringEncodingUTF8); + } + else + { + snprintf(axis.name, sizeof(axis.name), "Axis %i", m_axes.Size() + 1); + } + + m_axes.Push(axis); + + AddToQueue(axis.cookie); +} + +void IOKitJoystick::AddButton(CFDictionaryRef element) +{ + const DigitalButton button(GetElementCookie(element)); + + m_buttons.Push(button); + + AddToQueue(button.cookie); +} + +void IOKitJoystick::AddPOV(CFDictionaryRef element) +{ + const DigitalButton pov(GetElementCookie(element)); + + m_POVs.Push(pov); + + AddToQueue(pov.cookie); +} + +void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) +{ + assert(NULL != m_queue); + + if (!(*m_queue)->hasElement(m_queue, cookie)) + { + (*m_queue)->addElement(m_queue, cookie, 0); + } +} + + // --------------------------------------------------------------------------- @@ -571,27 +868,23 @@ class IOKitJoystickManager public: IOKitJoystickManager(); ~IOKitJoystickManager(); - - void GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const; - - void AddAxes( float axes[ NUM_JOYAXIS ] ) const; - + + void GetJoysticks(TArray& joysticks) const; + + void AddAxes(float axes[NUM_JOYAXIS]) const; + // Updates axes/buttons states void Update(); - + // Rebuilds device list void Rescan(); - + private: - TArray< IOKitJoystick* > m_joysticks; - - static void OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device ); - + TArray m_joysticks; + + void Rescan(int usagePage, int usage); + void ReleaseJoysticks(); - - void EnableCallbacks(); - void DisableCallbacks(); - }; @@ -603,38 +896,35 @@ IOKitJoystickManager::IOKitJoystickManager() IOKitJoystickManager::~IOKitJoystickManager() { ReleaseJoysticks(); - DisableCallbacks(); - - HIDReleaseDeviceList(); } -void IOKitJoystickManager::GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const +void IOKitJoystickManager::GetJoysticks(TArray& joysticks) const { const size_t joystickCount = m_joysticks.Size(); - - joysticks.Resize( joystickCount ); - - for ( size_t i = 0; i < joystickCount; ++i ) + + joysticks.Resize(joystickCount); + + for (size_t i = 0; i < joystickCount; ++i) { - M_LoadJoystickConfig( m_joysticks[i] ); - + M_LoadJoystickConfig(m_joysticks[i]); + joysticks[i] = m_joysticks[i]; } } -void IOKitJoystickManager::AddAxes( float axes[ NUM_JOYAXIS ] ) const +void IOKitJoystickManager::AddAxes(float axes[NUM_JOYAXIS]) const { - for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) { - m_joysticks[i]->AddAxes( axes ); + m_joysticks[i]->AddAxes(axes); } } void IOKitJoystickManager::Update() { - for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) { m_joysticks[i]->Update(); } @@ -644,148 +934,126 @@ void IOKitJoystickManager::Update() void IOKitJoystickManager::Rescan() { ReleaseJoysticks(); - DisableCallbacks(); - - const int usageCount = 2; - - const UInt32 usagePages[ usageCount ] = - { - kHIDPage_GenericDesktop, - kHIDPage_GenericDesktop - }; - - const UInt32 usages[ usageCount ] = - { - kHIDUsage_GD_Joystick, - kHIDUsage_GD_GamePad - }; - - if ( HIDUpdateDeviceList( usagePages, usages, usageCount ) ) - { - IOHIDDeviceRef device = HIDGetFirstDevice(); - - while ( NULL != device ) - { - IOKitJoystick* joystick = new IOKitJoystick( device ); - m_joysticks.Push( joystick ); - - device = HIDGetNextDevice( device ); - } - } - else - { - Printf( "IOKitJoystickManager: Failed to build gamepad/joystick device list.\n" ); - } - - EnableCallbacks(); + + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); } - -void IOKitJoystickManager::OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device ) +void IOKitJoystickManager::Rescan(const int usagePage, const int usage) { - event_t event; - - memset( &event, 0, sizeof( event ) ); - event.type = EV_DeviceChange; - - D_PostEvent( &event ); + CFMutableDictionaryRef deviceMatching = IOServiceMatching(kIOHIDDeviceKey); + + if (NULL == deviceMatching) + { + Printf(TEXTCOLOR_RED "IOServiceMatching() returned NULL\n"); + return; + } + + const CFNumberRef usagePageRef = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage); + CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsagePageKey), usagePageRef); + + const CFNumberRef usageRef = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsageKey), usageRef); + + io_iterator_t iterator = 0; + const kern_return_t matchResult = + IOServiceGetMatchingServices(kIOMasterPortDefault, deviceMatching, &iterator); + + CFRelease(usageRef); + CFRelease(usagePageRef); + + if (KERN_SUCCESS != matchResult) + { + Printf(TEXTCOLOR_RED "IOServiceGetMatchingServices() failed with code %i\n", matchResult); + return; + } + + while (io_object_t device = IOIteratorNext(iterator)) + { + IOKitJoystick* joystick = new IOKitJoystick(device); + m_joysticks.Push(joystick); + + IOObjectRelease(device); + } + + IOObjectRelease(iterator); } void IOKitJoystickManager::ReleaseJoysticks() { - for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + for (size_t i = 0, count = m_joysticks.Size(); i = 9) - { - s_joystickManager = new IOKitJoystickManager; - } -} void I_ShutdownJoysticks() +{ + // Needed in order to support existing interface + // Left empty intentionally +} + +static void ShutdownJoysticks() { delete s_joystickManager; s_joystickManager = NULL; } -void I_GetJoysticks( TArray< IJoystickConfig* >& sticks ) +void I_GetJoysticks(TArray& sticks) { - if ( NULL != s_joystickManager ) + // Instances of IOKitJoystick depend on GameConfig object. + // M_SaveDefaultsFinal() must be called after destruction of IOKitJoystickManager. + // To ensure this, its initialization is moved here. + // As M_LoadDefaults() was already called at this moment, + // the order of atterm's functions will be correct + + if (NULL == s_joystickManager && !Args->CheckParm("-nojoy")) { - s_joystickManager->GetJoysticks( sticks ); + s_joystickManager = new IOKitJoystickManager; + atterm(ShutdownJoysticks); + } + + if (NULL != s_joystickManager) + { + s_joystickManager->GetJoysticks(sticks); } } -void I_GetAxes( float axes[ NUM_JOYAXIS ] ) +void I_GetAxes(float axes[NUM_JOYAXIS]) { - for ( size_t i = 0; i < NUM_JOYAXIS; ++i ) + for (size_t i = 0; i AddAxes( axes ); + s_joystickManager->AddAxes(axes); } } IJoystickConfig* I_UpdateDeviceList() { - if ( use_joystick && NULL != s_joystickManager ) + if (use_joystick && NULL != s_joystickManager) { s_joystickManager->Rescan(); } - + return NULL; } @@ -795,42 +1063,8 @@ IJoystickConfig* I_UpdateDeviceList() void I_ProcessJoysticks() { - if ( use_joystick && NULL != s_joystickManager ) + if (NULL != s_joystickManager) { s_joystickManager->Update(); } } - -#else // prior to 10.5 - -void I_StartupJoysticks() -{ -} - -void I_ShutdownJoysticks() -{ -} - -void I_GetJoysticks(TArray& sticks) -{ - sticks.Clear(); -} - -void I_GetAxes(float axes[NUM_JOYAXIS]) -{ - for (size_t i = 0; i < NUM_JOYAXIS; ++i) - { - axes[i] = 0.0f; - } -} - -IJoystickConfig *I_UpdateDeviceList() -{ - return NULL; -} - -void I_ProcessJoysticks() -{ -} - -#endif // 10.5 or higher diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index 323efa8b7..b76ec0984 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -2,7 +2,7 @@ ** i_main.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -214,9 +214,6 @@ int OriginalMain(int argc, char** argv) progdir = [[exePath stringByDeletingLastPathComponent] UTF8String]; progdir += "/"; - I_StartupJoysticks(); - atterm(I_ShutdownJoysticks); - C_InitConsole(80 * 8, 25 * 8, false); D_DoomMain(); } From 2b12db153bd95518aa1f4533402c1f54280805fe Mon Sep 17 00:00:00 2001 From: nashmuhandes Date: Mon, 5 Jan 2015 17:51:32 +0800 Subject: [PATCH 078/117] New functions to manipulate an actor's roll. - DECORATE functions: A_SetRoll code pointer. - DECORATE expressions: "roll" variable. - ACS functions: SetActorRoll, GetActorRoll. --- src/actor.h | 1 + src/p_acs.cpp | 44 ++++++++++++++++++++++++++++ src/p_mobj.cpp | 12 ++++++++ src/thingdef/thingdef_codeptr.cpp | 16 ++++++++++ src/thingdef/thingdef_expression.cpp | 1 + wadsrc/static/actors/actor.txt | 2 ++ wadsrc/static/actors/constants.txt | 2 +- 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/actor.h b/src/actor.h index bc6e830f0..0ab589107 100644 --- a/src/actor.h +++ b/src/actor.h @@ -785,6 +785,7 @@ public: // These also set CF_INTERPVIEW for players. void SetPitch(int p, bool interpolate); void SetAngle(angle_t ang, bool interpolate); + void SetRoll(angle_t roll, bool interpolate); const PClass *GetBloodType(int type = 0) const { diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2ea64f26e..4b9a8bbab 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4440,6 +4440,9 @@ enum EACSFunctions ACSF_CanRaiseActor, ACSF_SetActorTeleFog, // 86 ACSF_SwapActorTeleFog, + ACSF_SetActorRoll, + ACSF_ChangeActorRoll, + ACSF_GetActorRoll, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -4751,6 +4754,27 @@ static void SetActorPitch(AActor *activator, int tid, int angle, bool interpolat } } +static void SetActorRoll(AActor *activator, int tid, int angle, bool interpolate) +{ + if (tid == 0) + { + if (activator != NULL) + { + activator->SetRoll(angle << 16, interpolate); + } + } + else + { + FActorIterator iterator(tid); + AActor *actor; + + while ((actor = iterator.Next())) + { + actor->SetRoll(angle << 16, interpolate); + } + } +} + static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName telefogdest) { //Simply put, if it doesn't exist, it won't change. One can use "" in this scenario. @@ -5832,6 +5856,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + // [Nash] Actor roll functions. Let's roll! + case ACSF_SetActorRoll: + actor = SingleActorFromTID(args[0], activator); + if (actor != NULL) + { + actor->SetRoll(args[1] << 16, false); + } + return 0; + + case ACSF_ChangeActorRoll: + if (argCount >= 2) + { + SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); + } + break; + + case ACSF_GetActorRoll: + actor = SingleActorFromTID(args[0], activator); + return actor != NULL? actor->roll >> 16 : 0; + default: break; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6a035933d..09f84663a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3077,6 +3077,18 @@ void AActor::SetAngle(angle_t ang, bool interpolate) } } +void AActor::SetRoll(angle_t r, bool interpolate) +{ + if (r != roll) + { + roll = r; + if (player != NULL && interpolate) + { + player->cheats |= CF_INTERPVIEW; + } + } +} + // // P_MobjThinker // diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 9554a3c25..980ff88a9 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3973,6 +3973,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); } +//=========================================================================== +// +// [Nash] A_SetRoll +// +// Set actor's roll (in degrees). +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_ANGLE(roll, 0); + ACTION_PARAM_INT(flags, 1); + self->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); +} + //=========================================================================== // // A_ScaleVelocity diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index d1c2eba9e..08b261bbf 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -89,6 +89,7 @@ DEFINE_MEMBER_VARIABLE(radius, AActor) DEFINE_MEMBER_VARIABLE(reactiontime, AActor) DEFINE_MEMBER_VARIABLE(meleerange, AActor) DEFINE_MEMBER_VARIABLE(Speed, AActor) +DEFINE_MEMBER_VARIABLE(roll, AActor) //========================================================================== diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 8b1cf6a65..a1e6b275b 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -68,6 +68,7 @@ ACTOR Actor native //: Thinker native int reactiontime; native fixed_t meleerange; native fixed_t speed; + native angle_t roll; // Meh, MBF redundant functions. Only for DeHackEd support. action native A_Turn(float angle = 0); @@ -290,6 +291,7 @@ ACTOR Actor native //: Thinker action native A_MonsterRefire(int chance, state label); action native A_SetAngle(float angle = 0, int flags = 0); action native A_SetPitch(float pitch, int flags = 0); + action native A_SetRoll(float roll, int flags = 0); action native A_ScaleVelocity(float scale); action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0); action native A_SetArg(int pos, int value); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index e8ba15d46..9b872df52 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -346,7 +346,7 @@ Const Int WARPF_TESTONLY = 0x200; Const Int WAPRF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_ABSOLUTEPOSITION = 0x400; -// flags for A_SetPitch/SetAngle +// flags for A_SetPitch/SetAngle/SetRoll const int SPF_FORCECLAMP = 1; const int SPF_INTERPOLATE = 2; From 31d232e886a3739b4f21140f0a35fe114bab712b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 13:22:53 +0200 Subject: [PATCH 079/117] Added polling of analog axes to IOKit gaming controllers handling This feature helps a lot with buggy gamepads that constantly generate events from "sticky" hats/sticks Polling is enabled by default, use joy_axespolling CVAR to turn it on/off --- src/posix/cocoa/i_joystick.cpp | 85 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index b102cf5a0..0471b43cd 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -45,6 +45,9 @@ #include "v_text.h" +EXTERN_CVAR(Bool, joy_axespolling) + + namespace { @@ -105,6 +108,8 @@ public: void Update(); + void UseAxesPolling(bool axesPolling); + private: IOHIDDeviceInterface** m_interface; IOHIDQueueInterface** m_queue; @@ -154,6 +159,8 @@ private: TArray m_buttons; TArray m_POVs; + bool m_useAxesPolling; + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; @@ -171,7 +178,9 @@ private: void AddAxis(CFDictionaryRef element); void AddButton(CFDictionaryRef element); void AddPOV(CFDictionaryRef element); + void AddToQueue(IOHIDElementCookie cookie); + void RemoveFromQueue(IOHIDElementCookie cookie); }; @@ -261,6 +270,7 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) : m_interface(CreateDeviceInterface(device)) , m_queue(CreateDeviceQueue(m_interface)) , m_sensitivity(DEFAULT_SENSITIVITY) +, m_useAxesPolling(true) { if (NULL == m_interface || NULL == m_queue) { @@ -282,6 +292,8 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) CFRelease(properties); + UseAxesPolling(joy_axespolling); + (*m_queue)->start(m_queue); SetDefaultConfig(); @@ -486,6 +498,26 @@ void IOKitJoystick::AddAxes(float axes[NUM_JOYAXIS]) const } +void IOKitJoystick::UseAxesPolling(const bool axesPolling) +{ + m_useAxesPolling = axesPolling; + + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + AnalogAxis& axis = m_axes[i]; + + if (m_useAxesPolling) + { + RemoveFromQueue(axis.cookie); + } + else + { + AddToQueue(axis.cookie); + } + } +} + + void IOKitJoystick::Update() { if (NULL == m_queue) @@ -509,12 +541,14 @@ void IOKitJoystick::Update() { Printf(TEXTCOLOR_RED "IOHIDQueueInterface::getNextEvent() failed with code 0x%08X\n", eventResult); } + + ProcessAxes(); } void IOKitJoystick::ProcessAxes() { - if (NULL == m_interface) + if (NULL == m_interface || !m_useAxesPolling) { return; } @@ -546,6 +580,11 @@ void IOKitJoystick::ProcessAxes() bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event) { + if (m_useAxesPolling) + { + return false; + } + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) { if (event.elementCookie != m_axes[i].cookie) @@ -827,8 +866,6 @@ void IOKitJoystick::AddAxis(const CFDictionaryRef element) } m_axes.Push(axis); - - AddToQueue(axis.cookie); } void IOKitJoystick::AddButton(CFDictionaryRef element) @@ -849,9 +886,13 @@ void IOKitJoystick::AddPOV(CFDictionaryRef element) AddToQueue(pov.cookie); } + void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) { - assert(NULL != m_queue); + if (NULL == m_queue) + { + return; + } if (!(*m_queue)->hasElement(m_queue, cookie)) { @@ -859,6 +900,19 @@ void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) } } +void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie) +{ + if (NULL == m_queue) + { + return; + } + + if ((*m_queue)->hasElement(m_queue, cookie)) + { + (*m_queue)->removeElement(m_queue, cookie); + } +} + // --------------------------------------------------------------------------- @@ -879,6 +933,8 @@ public: // Rebuilds device list void Rescan(); + void UseAxesPolling(bool axesPolling); + private: TArray m_joysticks; @@ -993,6 +1049,15 @@ void IOKitJoystickManager::ReleaseJoysticks() } +void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) +{ + for (size_t i = 0, count = m_joysticks.Size(); i UseAxesPolling(axesPolling); + } +} + + IOKitJoystickManager* s_joystickManager; @@ -1068,3 +1133,15 @@ void I_ProcessJoysticks() s_joystickManager->Update(); } } + + +// --------------------------------------------------------------------------- + + +CUSTOM_CVAR(Bool, joy_axespolling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (NULL != s_joystickManager) + { + s_joystickManager->UseAxesPolling(self); + } +} From d418648e5902c27884f71822ac5e7b6c27d70464 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 15:46:57 +0200 Subject: [PATCH 080/117] Fixed compiler warning with format string parameter --- src/posix/cocoa/i_joystick.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index 0471b43cd..3e3b0e527 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -221,7 +221,8 @@ IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device) } else { - Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", queryResult); + Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", + static_cast(queryResult)); return NULL; } } From cb681aad2d1cb8e1220074da2e1e438a8038012f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 17:24:54 +0200 Subject: [PATCH 081/117] Rearranged header files and #include's Removed unused OS version check Reduced number of headers Fixed build with SDK 10.4 --- src/posix/cocoa/i_common.h | 21 +++++++++++--- src/posix/cocoa/i_input.mm | 9 ++---- src/posix/cocoa/i_main.mm | 32 ++------------------- src/posix/cocoa/i_osversion.h | 43 ----------------------------- src/posix/cocoa/i_rbopts.h | 52 ----------------------------------- src/posix/cocoa/i_video.mm | 12 ++------ 6 files changed, 25 insertions(+), 144 deletions(-) delete mode 100644 src/posix/cocoa/i_osversion.h delete mode 100644 src/posix/cocoa/i_rbopts.h diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index ea51e1c88..3f26e7940 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -31,10 +31,23 @@ ** */ -#import -#import -#import -#import +#import + + +struct RenderBufferOptions +{ + float pixelScale; + + float shiftX; + float shiftY; + + float width; + float height; + + bool dirty; +}; + +extern RenderBufferOptions rbOpts; inline bool I_IsHiDPISupported() diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 46f5e4450..5e0c5f1c8 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -2,7 +2,7 @@ ** i_input.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,8 +31,8 @@ ** */ -#import -#import +#include "i_common.h" + #import // Avoid collision between DObject class and Objective-C @@ -48,9 +48,6 @@ #include "doomstat.h" #include "v_video.h" -#include "i_common.h" -#include "i_rbopts.h" - #undef Class diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index b76ec0984..bb1309109 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -31,10 +31,9 @@ ** */ -#include +#include "i_common.h" -#import -#import +#include // Avoid collision between DObject class and Objective-C #define Class ObjectClass @@ -49,9 +48,6 @@ #include "s_sound.h" #include "version.h" -#include "i_common.h" -#include "i_osversion.h" - #undef Class @@ -492,27 +488,6 @@ void CreateMenu() [NSApp setMainMenu:menuBar]; } -DarwinVersion GetDarwinVersion() -{ - DarwinVersion result = {}; - - int mib[2] = { CTL_KERN, KERN_OSRELEASE }; - size_t size = 0; - - if (0 == sysctl(mib, 2, NULL, &size, NULL, 0)) - { - char* version = static_cast(alloca(size)); - - if (0 == sysctl(mib, 2, version, &size, NULL, 0)) - { - sscanf(version, "%hu.%hu.%hu", - &result.major, &result.minor, &result.bugfix); - } - } - - return result; -} - void ReleaseApplicationController() { if (NULL != appCtrl) @@ -528,9 +503,6 @@ void ReleaseApplicationController() } // unnamed namespace -const DarwinVersion darwinVersion = GetDarwinVersion(); - - int main(int argc, char** argv) { for (int i = 0; i <= argc; ++i) diff --git a/src/posix/cocoa/i_osversion.h b/src/posix/cocoa/i_osversion.h deleted file mode 100644 index 5e6ac6d20..000000000 --- a/src/posix/cocoa/i_osversion.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - ** i_osversion.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#include - -struct DarwinVersion -{ - uint16_t major; - uint16_t minor; - uint16_t bugfix; -}; - -extern const DarwinVersion darwinVersion; diff --git a/src/posix/cocoa/i_rbopts.h b/src/posix/cocoa/i_rbopts.h deleted file mode 100644 index 40a9ff17a..000000000 --- a/src/posix/cocoa/i_rbopts.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - ** i_rbopts.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#ifndef SRC_COCOA_I_RBOPTS_H_INCLUDED -#define SRC_COCOA_I_RBOPTS_H_INCLUDED - -struct RenderBufferOptions -{ - float pixelScale; - - float shiftX; - float shiftY; - - float width; - float height; - - bool dirty; -}; - -extern RenderBufferOptions rbOpts; - -#endif // SRC_COCOA_I_RBOPTS_H_INCLUDED diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index cb988002c..b5f54c256 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -2,7 +2,7 @@ ** i_video.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,11 +31,8 @@ ** */ -#import -#import -#import -#import -#import +#include "i_common.h" + #import #import @@ -58,9 +55,6 @@ #include "v_video.h" #include "version.h" -#include "i_common.h" -#include "i_rbopts.h" - #undef Class From b59fc595393e00f437937a39f12ec0b2fe8d5d66 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 17:26:06 +0200 Subject: [PATCH 082/117] Added missing header comment --- src/posix/cocoa/i_timer.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/posix/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp index c63561077..319cdd2b8 100644 --- a/src/posix/cocoa/i_timer.cpp +++ b/src/posix/cocoa/i_timer.cpp @@ -1,3 +1,35 @@ +/* + ** i_timer.cpp + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ #include #include From 04d38029605c1d5b5554207975e55eccf34438a1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 18:12:07 +0200 Subject: [PATCH 083/117] Fixed build issue with OS X SDK 10.4 --- src/posix/cocoa/i_common.h | 11 ----------- src/posix/cocoa/i_main.mm | 1 + 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index 3f26e7940..081466e87 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -128,17 +128,6 @@ enum kVK_UpArrow = 0x7E }; -@interface NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint; -@end - -@implementation NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint -{ - return [self convertPoint:aPoint fromView:nil]; -} -@end - #endif // prior to 10.5 diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index bb1309109..05a8081f6 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -34,6 +34,7 @@ #include "i_common.h" #include +#include // Avoid collision between DObject class and Objective-C #define Class ObjectClass From 4aac586dd8c4b5782a8cfa76e8e94d9082c1ccd8 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Mon, 5 Jan 2015 23:31:02 +0100 Subject: [PATCH 084/117] - Fixed another erroneous FString comparison. A comparison between an FString object and 'NULL' doesn't check for the emptiness of the examined string. --- src/g_strife/strife_sbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp index 89659e61d..b69222d36 100644 --- a/src/g_strife/strife_sbar.cpp +++ b/src/g_strife/strife_sbar.cpp @@ -601,7 +601,7 @@ private: screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff, DTA_CleanNoMove, true, TAG_DONE); - if (CPlayer->LogText != NULL) + if (CPlayer->LogText.IsNotEmpty()) { FBrokenLines *lines = V_BreakLines(SmallFont2, 272, CPlayer->LogText); for (i = 0; lines[i].Width >= 0; ++i) From 324a1a7b77522139eeb7796de5a7c75c1e28ad04 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 6 Jan 2015 11:21:11 +0200 Subject: [PATCH 085/117] Added support for dynamic device attachment and removal to IOKit gaming controllers handling --- src/posix/cocoa/i_joystick.cpp | 186 +++++++++++++++++++++++++-------- 1 file changed, 145 insertions(+), 41 deletions(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index 3e3b0e527..9b9487cf6 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -76,6 +77,9 @@ FString ToFString(const CFStringRef string) } +// --------------------------------------------------------------------------- + + class IOKitJoystick : public IJoystickConfig { public: @@ -110,6 +114,8 @@ public: void UseAxesPolling(bool axesPolling); + io_object_t* GetNotificationPtr(); + private: IOHIDDeviceInterface** m_interface; IOHIDQueueInterface** m_queue; @@ -161,6 +167,8 @@ private: bool m_useAxesPolling; + io_object_t m_notification; + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; @@ -272,6 +280,7 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) , m_queue(CreateDeviceQueue(m_interface)) , m_sensitivity(DEFAULT_SENSITIVITY) , m_useAxesPolling(true) +, m_notification(0) { if (NULL == m_interface || NULL == m_queue) { @@ -304,6 +313,11 @@ IOKitJoystick::~IOKitJoystick() { M_SaveJoystickConfig(this); + if (0 != m_notification) + { + IOObjectRelease(m_notification); + } + if (NULL != m_queue) { (*m_queue)->stop(m_queue); @@ -915,6 +929,12 @@ void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie) } +io_object_t* IOKitJoystick::GetNotificationPtr() +{ + return &m_notification; +} + + // --------------------------------------------------------------------------- @@ -931,28 +951,75 @@ public: // Updates axes/buttons states void Update(); - // Rebuilds device list - void Rescan(); - void UseAxesPolling(bool axesPolling); private: - TArray m_joysticks; + typedef TDeletingArray JoystickList; + JoystickList m_joysticks; - void Rescan(int usagePage, int usage); + static const size_t NOTIFICATION_PORT_COUNT = 2; - void ReleaseJoysticks(); + IONotificationPortRef m_notificationPorts[NOTIFICATION_PORT_COUNT]; + io_iterator_t m_notifications [NOTIFICATION_PORT_COUNT]; + + // Rebuilds device list + void Rescan(int usagePage, int usage, size_t notificationPortIndex); + void AddDevices(IONotificationPortRef notificationPort, const io_iterator_t iterator); + + static void OnDeviceAttached(void* refcon, io_iterator_t iterator); + static void OnDeviceRemoved(void* refcon, io_service_t service, + natural_t messageType, void* messageArgument); }; +IOKitJoystickManager* s_joystickManager; + + IOKitJoystickManager::IOKitJoystickManager() { - Rescan(); + memset(m_notifications, 0, sizeof m_notifications); + + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + m_notificationPorts[i] = IONotificationPortCreate(kIOMasterPortDefault); + + if (NULL == m_notificationPorts[i]) + { + Printf(TEXTCOLOR_RED "IONotificationPortCreate(%zu) failed\n", i); + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(m_notificationPorts[i]), kCFRunLoopDefaultMode); + } + + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, 0); + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, 1); } IOKitJoystickManager::~IOKitJoystickManager() { - ReleaseJoysticks(); + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + IONotificationPortRef& port = m_notificationPorts[i]; + + if (NULL != port) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(port), kCFRunLoopDefaultMode); + + IONotificationPortDestroy(port); + port = NULL; + } + + io_iterator_t& notification = m_notifications[i]; + + if (0 != notification) + { + IOObjectRelease(notification); + notification = NULL; + } + } } @@ -988,15 +1055,23 @@ void IOKitJoystickManager::Update() } -void IOKitJoystickManager::Rescan() +void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) { - ReleaseJoysticks(); - - Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); - Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) + { + m_joysticks[i]->UseAxesPolling(axesPolling); + } } -void IOKitJoystickManager::Rescan(const int usagePage, const int usage) + +void PostDeviceChangeEvent() +{ + const event_t event = { EV_DeviceChange }; + D_PostEvent(&event); +} + + +void IOKitJoystickManager::Rescan(const int usagePage, const int usage, const size_t notificationPortIndex) { CFMutableDictionaryRef deviceMatching = IOServiceMatching(kIOHIDDeviceKey); @@ -1014,54 +1089,86 @@ void IOKitJoystickManager::Rescan(const int usagePage, const int usage) CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsageKey), usageRef); - io_iterator_t iterator = 0; - const kern_return_t matchResult = - IOServiceGetMatchingServices(kIOMasterPortDefault, deviceMatching, &iterator); + assert(notificationPortIndex < NOTIFICATION_PORT_COUNT); + io_iterator_t* iteratorPtr = &m_notifications[notificationPortIndex]; + + const IONotificationPortRef notificationPort = m_notificationPorts[notificationPortIndex]; + assert(NULL != notificationPort); + + const kern_return_t notificationResult = IOServiceAddMatchingNotification(notificationPort, + kIOFirstMatchNotification, deviceMatching, OnDeviceAttached, notificationPort, iteratorPtr); + + // IOServiceAddMatchingNotification() consumes one reference of matching dictionary + // Thus CFRelease(deviceMatching) is not needed CFRelease(usageRef); CFRelease(usagePageRef); - if (KERN_SUCCESS != matchResult) + if (KERN_SUCCESS != notificationResult) { - Printf(TEXTCOLOR_RED "IOServiceGetMatchingServices() failed with code %i\n", matchResult); - return; + Printf(TEXTCOLOR_RED "IOServiceAddMatchingNotification() failed with code %i\n", notificationResult); } + AddDevices(notificationPort, *iteratorPtr); +} + +void IOKitJoystickManager::AddDevices(const IONotificationPortRef notificationPort, const io_iterator_t iterator) +{ while (io_object_t device = IOIteratorNext(iterator)) { IOKitJoystick* joystick = new IOKitJoystick(device); m_joysticks.Push(joystick); + const kern_return_t notificationResult = IOServiceAddInterestNotification(notificationPort, + device, kIOGeneralInterest, OnDeviceRemoved, joystick, joystick->GetNotificationPtr()); + if (KERN_SUCCESS != notificationResult) + { + Printf(TEXTCOLOR_RED "IOServiceAddInterestNotification() failed with code %i\n", notificationResult); + } + IOObjectRelease(device); + + PostDeviceChangeEvent(); } - - IOObjectRelease(iterator); } -void IOKitJoystickManager::ReleaseJoysticks() +void IOKitJoystickManager::OnDeviceAttached(void* const refcon, const io_iterator_t iterator) { - for (size_t i = 0, count = m_joysticks.Size(); i (refcon); - m_joysticks.Clear(); + assert(NULL != s_joystickManager); + s_joystickManager->AddDevices(notificationPort, iterator); } - -void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) +void IOKitJoystickManager::OnDeviceRemoved(void* const refcon, io_service_t, const natural_t messageType, void*) { - for (size_t i = 0, count = m_joysticks.Size(); i UseAxesPolling(axesPolling); + return; } + + assert(NULL != refcon); + IOKitJoystick* const joystick = static_cast(refcon); + + assert(NULL != s_joystickManager); + JoystickList& joysticks = s_joystickManager->m_joysticks; + + for (unsigned int i = 0, count = joysticks.Size(); i < count; ++i) + { + if (joystick == joysticks[i]) + { + joysticks.Delete(i); + break; + } + } + + delete joystick; + + PostDeviceChangeEvent(); } - -IOKitJoystickManager* s_joystickManager; - - } // unnamed namespace @@ -1102,7 +1209,7 @@ void I_GetJoysticks(TArray& sticks) void I_GetAxes(float axes[NUM_JOYAXIS]) { - for (size_t i = 0; i Rescan(); - } + // Does nothing, device list is always kept up-to-date return NULL; } From f374cf514d46dfbfe3b1200fdfefaec34d072944 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 6 Jan 2015 08:20:13 -0600 Subject: [PATCH 086/117] - Fixed: Lore Shot didn't take DONTTHRUST into account. --- src/g_strife/a_loremaster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_strife/a_loremaster.cpp b/src/g_strife/a_loremaster.cpp index 3c42ea181..d720b7b9b 100644 --- a/src/g_strife/a_loremaster.cpp +++ b/src/g_strife/a_loremaster.cpp @@ -23,7 +23,7 @@ int ALoreShot::DoSpecialDamage (AActor *target, int damage, FName damagetype) { FVector3 thrust; - if (this->target != NULL) + if (this->target != NULL && !(this->target->flags7 & MF7_DONTTHRUST)) { thrust.X = float(this->target->x - target->x); thrust.Y = float(this->target->y - target->y); From 1a69221f80593cf85a2bf82d81d9d16cf0a846f1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 6 Jan 2015 16:19:59 +0200 Subject: [PATCH 087/117] Removed trailing tab characters in Cocoa IWAD picker --- src/posix/osx/iwadpicker_cocoa.mm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/posix/osx/iwadpicker_cocoa.mm b/src/posix/osx/iwadpicker_cocoa.mm index d1364fa81..e5050f801 100644 --- a/src/posix/osx/iwadpicker_cocoa.mm +++ b/src/posix/osx/iwadpicker_cocoa.mm @@ -139,7 +139,7 @@ static NSDictionary* GetKnownFileTypes() @"-bex" , @"bex", @"-exec" , @"cfg", @"-playdemo", @"lmp", - nil]; + nil]; } static NSArray* GetKnownExtensions() @@ -156,7 +156,7 @@ static NSArray* GetKnownExtensions() { NSString* extension = [[filePath pathExtension] lowercaseString]; NSString* parameter = [GetKnownFileTypes() objectForKey:extension]; - + if (nil == parameter) { return; @@ -211,7 +211,7 @@ static NSArray* GetKnownExtensions() { NSArray* files = [openPanel URLs]; NSMutableString* parameters = [NSMutableString string]; - + for (NSUInteger i = 0, ei = [files count]; i < ei; ++i) { NSString* filePath = [[files objectAtIndex:i] path]; @@ -236,9 +236,9 @@ static NSArray* GetKnownExtensions() { newParameters = [newParameters stringByAppendingString:@" "]; } - + newParameters = [newParameters stringByAppendingString:parameters]; - + [parametersTextField setStringValue: newParameters]; } } @@ -337,7 +337,7 @@ static NSArray* GetKnownExtensions() [cancelButton setTarget:self]; [cancelButton setKeyEquivalent:@"\033"]; [[window contentView] addSubview:cancelButton]; - + browseButton = [[NSButton alloc] initWithFrame:NSMakeRect(14, 8, 96, 32)]; [browseButton setTitle:@"Browse..."]; [browseButton setBezelStyle:NSRoundedBezelStyle]; @@ -398,22 +398,22 @@ static NSString* GetArchitectureString() static void RestartWithParameters(const char* iwadPath, NSString* parameters) { assert(nil != parameters); - + defaultiwad = ExtractFileBase(iwadPath); - + GameConfig->DoGameSetup("Doom"); M_SaveDefaults(NULL); - + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @try { const int commandLineParametersCount = Args->NumArgs(); assert(commandLineParametersCount > 0); - + NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)]; NSString* architecture = GetArchitectureString(); - + NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 6]; [arguments addObject:@"-arch"]; [arguments addObject:architecture]; @@ -450,7 +450,7 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) { NSLog(@"Cannot restart: %@", [e reason]); } - + [pool release]; } From 86b2a8530bb7bc6fc83123bcd39f09fa4d5391de Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 6 Jan 2015 17:06:29 +0200 Subject: [PATCH 088/117] Fixed launching with additional arguments from IWAD picker on OS X 10.4 --- src/posix/osx/iwadpicker_cocoa.mm | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/posix/osx/iwadpicker_cocoa.mm b/src/posix/osx/iwadpicker_cocoa.mm index e5050f801..d2bc1ff0c 100644 --- a/src/posix/osx/iwadpicker_cocoa.mm +++ b/src/posix/osx/iwadpicker_cocoa.mm @@ -408,21 +408,28 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) @try { - const int commandLineParametersCount = Args->NumArgs(); - assert(commandLineParametersCount > 0); - NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)]; - NSString* architecture = GetArchitectureString(); - NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 6]; - [arguments addObject:@"-arch"]; - [arguments addObject:architecture]; - [arguments addObject:executablePath]; + NSMutableArray* const arguments = [[NSMutableArray alloc] init]; + + // The following value shoud be equal to NSAppKitVersionNumber10_5 + // It's hard-coded in order to build with earlier SDKs + const bool canSelectArchitecture = NSAppKitVersionNumber >= 949; + + if (canSelectArchitecture) + { + [arguments addObject:@"-arch"]; + [arguments addObject:GetArchitectureString()]; + [arguments addObject:executablePath]; + + executablePath = @"/usr/bin/arch"; + } + [arguments addObject:@"-wad_picker_restart"]; [arguments addObject:@"-iwad"]; [arguments addObject:[NSString stringWithUTF8String:iwadPath]]; - for (int i = 1; i < commandLineParametersCount; ++i) + for (int i = 1, count = Args->NumArgs(); i < count; ++i) { NSString* currentParameter = [NSString stringWithUTF8String:Args->GetArg(i)]; [arguments addObject:currentParameter]; @@ -442,7 +449,8 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) wordfree(&expansion); } - [NSTask launchedTaskWithLaunchPath:@"/usr/bin/arch" arguments:arguments]; + [NSTask launchedTaskWithLaunchPath:executablePath + arguments:arguments]; _exit(0); // to avoid atexit()'s functions } From 643d37ab7c3994573dfc86da91f4312fcbf1219f Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 6 Jan 2015 11:55:41 -0600 Subject: [PATCH 089/117] - Added A_SetFloatBobPhase(int). Takes a number between 0 and 63. If it's outside that range, it does nothing. - Added A_Warp flags: - WARPF_BOB: Gets the bob offset of actor pointer with the FLOATBOB flag. - WARPF_MOVEPTR: The function is inversed to move the pointed actor with applied flags, but only the original caller will make the success/jump. --- src/thingdef/thingdef_codeptr.cpp | 93 ++++++++++++++++++++---------- wadsrc/static/actors/actor.txt | 1 + wadsrc/static/actors/constants.txt | 2 + 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 980ff88a9..300ee7556 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4563,6 +4563,8 @@ enum WARPF WARPF_TOFLOOR = 0x100, WARPF_TESTONLY = 0x200, WARPF_ABSOLUTEPOSITION = 0x400, + WARPF_BOB = 0x800, + WARPF_MOVEPTR = 0x1000, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) @@ -4579,19 +4581,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) AActor *reference = COPY_AAPTR(self, destination_selector); + //If there is no actor to warp to, fail. if (!reference) { ACTION_SET_RESULT(false); return; } - fixed_t oldx = self->x; - fixed_t oldy = self->y; - fixed_t oldz = self->z; + AActor *caller = self; + + if (flags & WARPF_MOVEPTR) + { + AActor *temp = reference; + reference = caller; + caller = temp; + } + + fixed_t oldx = caller->x; + fixed_t oldy = caller->y; + fixed_t oldz = caller->z; if (!(flags & WARPF_ABSOLUTEANGLE)) { - angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; + angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; } if (!(flags & WARPF_ABSOLUTEPOSITION)) { @@ -4612,7 +4624,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { // set correct xy - self->SetOrigin( + caller->SetOrigin( reference->x + xofs, reference->y + yofs, reference->z); @@ -4623,10 +4635,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) if (zofs) { // extra unlink, link and environment calculation - self->SetOrigin( - self->x, - self->y, - self->floorz + zofs); + caller->SetOrigin( + caller->x, + caller->y, + caller->floorz + zofs); } else { @@ -4634,12 +4646,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) // already identified floor // A_Teleport does the same thing anyway - self->z = self->floorz; + caller->z = caller->floorz; } } else { - self->SetOrigin( + caller->SetOrigin( reference->x + xofs, reference->y + yofs, reference->z + zofs); @@ -4649,51 +4661,57 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { if (flags & WARPF_TOFLOOR) { - self->SetOrigin(xofs, yofs, self->floorz + zofs); + caller->SetOrigin(xofs, yofs, caller->floorz + zofs); } else { - self->SetOrigin(xofs, yofs, zofs); + caller->SetOrigin(xofs, yofs, zofs); } } - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) { if (flags & WARPF_TESTONLY) { - self->SetOrigin(oldx, oldy, oldz); + caller->SetOrigin(oldx, oldy, oldz); } else { - self->angle = angle; + caller->angle = angle; if (flags & WARPF_STOP) { - self->velx = 0; - self->vely = 0; - self->velz = 0; + caller->velx = 0; + caller->vely = 0; + caller->velz = 0; } if (flags & WARPF_WARPINTERPOLATION) { - self->PrevX += self->x - oldx; - self->PrevY += self->y - oldy; - self->PrevZ += self->z - oldz; + caller->PrevX += caller->x - oldx; + caller->PrevY += caller->y - oldy; + caller->PrevZ += caller->z - oldz; } else if (flags & WARPF_COPYINTERPOLATION) { - self->PrevX = self->x + reference->PrevX - reference->x; - self->PrevY = self->y + reference->PrevY - reference->y; - self->PrevZ = self->z + reference->PrevZ - reference->z; + caller->PrevX = caller->x + reference->PrevX - reference->x; + caller->PrevY = caller->y + reference->PrevY - reference->y; + caller->PrevZ = caller->z + reference->PrevZ - reference->z; } else if (!(flags & WARPF_INTERPOLATE)) { - self->PrevX = self->x; - self->PrevY = self->y; - self->PrevZ = self->z; + caller->PrevX = caller->x; + caller->PrevY = caller->y; + caller->PrevZ = caller->z; + } + + if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) + { + caller->z += reference->GetBobOffset(); } } + if (success_state) { ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! @@ -4706,7 +4724,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) } else { - self->SetOrigin(oldx, oldy, oldz); + caller->SetOrigin(oldx, oldy, oldz); ACTION_SET_RESULT(false); } @@ -5634,6 +5652,23 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) } } +//=========================================================================== +// +// A_SetFloatBobPhase +// +// Changes the FloatBobPhase of the +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(bob, 0); + + //Respect float bob phase limits. + if (self && (bob >= 0 && bob <= 63)) + self->FloatBobPhase = bob; +} + //=========================================================================== // // A_SetRipperLevel(int level) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index a1e6b275b..b2fa80c30 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -326,6 +326,7 @@ ACTOR Actor native //: Thinker action native A_TakeFromSiblings(class itemtype, int amount = 0); action native A_SetTeleFog(name oldpos, name newpos); action native A_SwapTeleFog(); + action native A_SetFloatBobPhase(int bob); action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 9b872df52..074953afe 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -345,6 +345,8 @@ Const Int WARPF_TOFLOOR = 0x100; Const Int WARPF_TESTONLY = 0x200; Const Int WAPRF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_ABSOLUTEPOSITION = 0x400; +Const Int WARPF_BOB = 0x800; +Const Int WARPF_MOVEPTR = 0x1000; // flags for A_SetPitch/SetAngle/SetRoll const int SPF_FORCECLAMP = 1; From 4ea918ab3b06ea5271e1ad0776441404c483f217 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 6 Jan 2015 23:16:25 +0100 Subject: [PATCH 090/117] - Fixed bad characters in the info command string. --- src/c_cmds.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 3dd97434e..9290c36c3 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -900,8 +900,8 @@ CCMD(info) linetarget->SpawnHealth()); PrintMiscActorInfo(linetarget); } - else Printf("No target found. Info cannot find actors that have\ - the NOBLOCKMAP flag or have height/radius of 0.\n"); + else Printf("No target found. Info cannot find actors that have " + "the NOBLOCKMAP flag or have height/radius of 0.\n"); } //----------------------------------------------------------------------------- From b2abf224b5a135d93eaf10e80a6469a4d00e20b1 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 6 Jan 2015 19:31:00 -0600 Subject: [PATCH 091/117] Save function name for error message in ParseStates() - The error "You cannot pass parameters to..." used the most recent token, which was always ( and not the function name. (Note that this was already fixed in the scripting branch, so this is probably going to be a conflict. Meh.) --- src/thingdef/thingdef_states.cpp | 5 +++-- wadsrc/static/actors/doom/demon.txt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 6a28e54cd..eb740aad2 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -332,7 +332,8 @@ do_stop: goto endofstate; } - PSymbol *sym = bag.Info->Class->Symbols.FindSymbol (FName(sc.String, true), true); + FName funcname = FName(sc.String, true); + PSymbol *sym = bag.Info->Class->Symbols.FindSymbol (funcname, true); if (sym != NULL && sym->SymbolType == SYM_ActionFunction) { PSymbolActionFunction *afd = static_cast(sym); @@ -434,7 +435,7 @@ do_stop: sc.MustGetString(); if (sc.Compare("(")) { - sc.ScriptError("You cannot pass parameters to '%s'\n",sc.String); + sc.ScriptError("You cannot pass parameters to '%s'\n", funcname.GetChars()); } sc.UnGet(); } diff --git a/wadsrc/static/actors/doom/demon.txt b/wadsrc/static/actors/doom/demon.txt index ecd6fa8a6..03dc5d86f 100644 --- a/wadsrc/static/actors/doom/demon.txt +++ b/wadsrc/static/actors/doom/demon.txt @@ -39,7 +39,7 @@ ACTOR Demon 3002 Goto See Death: SARG I 8 - SARG J 8 A_Scream + SARG J 8 A_Scream() SARG K 4 SARG L 4 A_NoBlocking SARG M 4 From f71b1a89835763f799386e5eba323accb3a671b9 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 6 Jan 2015 19:34:11 -0600 Subject: [PATCH 092/117] Whoops. --- wadsrc/static/actors/doom/demon.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/actors/doom/demon.txt b/wadsrc/static/actors/doom/demon.txt index 03dc5d86f..ecd6fa8a6 100644 --- a/wadsrc/static/actors/doom/demon.txt +++ b/wadsrc/static/actors/doom/demon.txt @@ -39,7 +39,7 @@ ACTOR Demon 3002 Goto See Death: SARG I 8 - SARG J 8 A_Scream() + SARG J 8 A_Scream SARG K 4 SARG L 4 A_NoBlocking SARG M 4 From 83c6798651bb4b691802a7167d29b6aab651730d Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 7 Jan 2015 14:49:07 +0200 Subject: [PATCH 093/117] Fixed a very slim chance that window will be white and empty after startup There are a few quite specific steps to reproduce this issue: * 640x480 video resolution * -iwad ... -warp ... command line parameters * OS X 10.4 or 10.5 PowerPC, maybe performance related When all these requirements are met, content view doesn't show up sometimes The simplest solution for this issue is to set initial window size to non-existent video resolution --- src/posix/cocoa/i_video.mm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index b5f54c256..57f7c16fd 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -355,10 +355,14 @@ cycle_t FlipCycles; CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) { - CocoaWindow* window = [[CocoaWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; + static const CGFloat TEMP_WIDTH = VideoModes[0].width - 1; + static const CGFloat TEMP_HEIGHT = VideoModes[0].height - 1; + + CocoaWindow* const window = [CocoaWindow alloc]; + [window initWithContentRect:NSMakeRect(0, 0, TEMP_WIDTH, TEMP_HEIGHT) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; [window setOpaque:YES]; [window makeFirstResponder:appCtrl]; [window setAcceptsMouseMovedEvents:YES]; From f3cc872677f49be11398b0315049a88918372a8f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Thu, 8 Jan 2015 12:50:23 +0200 Subject: [PATCH 094/117] Added Retina/HiDPI option to video mode menu (OS X only) --- wadsrc/static/menudef.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 4e33029b4..978480e4d 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1586,6 +1586,10 @@ OptionMenu VideoModeMenu Title "VIDEO MODE" Option "Fullscreen", "fullscreen", "YesNo" + IfOption(Mac) + { + Option "Retina/HiDPI support", "vid_hidpi", "YesNo" + } Option "Aspect ratio", "menu_screenratios", "Ratios" Option "Force aspect ratio", "vid_aspect", "ForceRatios" Option "Enable 5:4 aspect ratio","vid_tft", "YesNo" From c3c22315d98f57b5e5a10dd73bd6ac8a933800d8 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Thu, 8 Jan 2015 19:52:23 +0100 Subject: [PATCH 095/117] - Added zero tag handling for Pillar actions. Pillar actions will refer to the back sector if the tag is set to 0. --- src/p_lnspec.cpp | 6 +++--- src/p_pillar.cpp | 22 +++++++++++++++++----- src/p_spec.h | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 67215cf41..c170cacd3 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -519,19 +519,19 @@ FUNC(LS_Generic_Stairs) FUNC(LS_Pillar_Build) // Pillar_Build (tag, speed, height) { - return EV_DoPillar (DPillar::pillarBuild, arg0, SPEED(arg1), arg2*FRACUNIT, 0, -1, false); + return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, -1, false); } FUNC(LS_Pillar_BuildAndCrush) // Pillar_BuildAndCrush (tag, speed, height, crush, crushtype) { - return EV_DoPillar (DPillar::pillarBuild, arg0, SPEED(arg1), arg2*FRACUNIT, 0, arg3, CRUSHTYPE(arg4)); + return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, arg3, CRUSHTYPE(arg4)); } FUNC(LS_Pillar_Open) // Pillar_Open (tag, speed, f_height, c_height) { - return EV_DoPillar (DPillar::pillarOpen, arg0, SPEED(arg1), arg2*FRACUNIT, arg3*FRACUNIT, -1, false); + return EV_DoPillar (DPillar::pillarOpen, ln, arg0, SPEED(arg1), arg2*FRACUNIT, arg3*FRACUNIT, -1, false); } FUNC(LS_Ceiling_LowerByValue) diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index 1f77c7c7b..809542949 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -212,16 +212,28 @@ DPillar::DPillar (sector_t *sector, EPillar type, fixed_t speed, } } -bool EV_DoPillar (DPillar::EPillar type, int tag, fixed_t speed, fixed_t height, - fixed_t height2, int crush, bool hexencrush) +bool EV_DoPillar (DPillar::EPillar type, line_t *line, int tag, + fixed_t speed, fixed_t height, fixed_t height2, int crush, bool hexencrush) { + int secnum; + sector_t *sec; bool rtn = false; - int secnum = -1; - while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0) + // check if a manual trigger; if so do just the sector on the backside + if (tag == 0) { - sector_t *sec = §ors[secnum]; + if (!line || !(sec = line->backsector)) + return rtn; + secnum = (int)(sec-sectors); + goto manual_pillar; + } + secnum = -1; + while (tag && (secnum = P_FindSectorFromTag (tag, secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_pillar: if (sec->PlaneMoving(sector_t::floor) || sec->PlaneMoving(sector_t::ceiling)) continue; diff --git a/src/p_spec.h b/src/p_spec.h index a225d2379..0d7ef4cff 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -515,8 +515,8 @@ private: DPillar (); }; -bool EV_DoPillar (DPillar::EPillar type, int tag, fixed_t speed, fixed_t height, - fixed_t height2, int crush, bool hexencrush); +bool EV_DoPillar (DPillar::EPillar type, line_t *line, int tag, + fixed_t speed, fixed_t height, fixed_t height2, int crush, bool hexencrush); // // P_DOORS From 5336f1085c327ca06f5f549448c5cc594a4fe7b2 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 11 Jan 2015 10:30:30 +0200 Subject: [PATCH 096/117] Fixed incorrect value that I_FPSTime() may return when OS X thread-based timer implementation is used --- src/posix/cocoa/i_timer.cpp | 42 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/posix/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp index 319cdd2b8..b08a43139 100644 --- a/src/posix/cocoa/i_timer.cpp +++ b/src/posix/cocoa/i_timer.cpp @@ -32,6 +32,7 @@ */ #include +#include #include #include #include @@ -43,34 +44,23 @@ #include "templates.h" -static timeval s_startTicks; - - -unsigned int I_MSTime() +namespace { - timeval now; - gettimeofday(&now, NULL); - const uint32_t ticks = - (now.tv_sec - s_startTicks.tv_sec ) * 1000 - + (now.tv_usec - s_startTicks.tv_usec) / 1000; +timeval s_gameStartTicks; +timeval s_systemBootTicks; - return ticks; -} - -unsigned int I_FPSTime() +unsigned int GetMillisecondsSince(const timeval& time) { timeval now; gettimeofday(&now, NULL); return static_cast( - (now.tv_sec) * 1000 + (now.tv_usec) / 1000); + (now.tv_sec - time.tv_sec ) * 1000 + + (now.tv_usec - time.tv_usec) / 1000); } -namespace -{ - bool s_isTicFrozen; timespec GetNextTickTime() @@ -185,6 +175,17 @@ void FreezeTimeThreaded(bool frozen) } // unnamed namespace +unsigned int I_MSTime() +{ + return GetMillisecondsSince(s_gameStartTicks); +} + +unsigned int I_FPSTime() +{ + return GetMillisecondsSince(s_systemBootTicks); +} + + fixed_t I_GetTimeFrac(uint32* ms) { const uint32_t now = I_MSTime(); @@ -205,7 +206,12 @@ void I_InitTimer() assert(!s_timerInitialized); s_timerInitialized = true; - gettimeofday(&s_startTicks, NULL); + gettimeofday(&s_gameStartTicks, NULL); + + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t len = sizeof s_systemBootTicks; + + sysctl(mib, 2, &s_systemBootTicks, &len, NULL, 0); pthread_cond_init (&s_timerEvent, NULL); pthread_mutex_init(&s_timerMutex, NULL); From 7ae3678abc322f6a951916b8ccb5cf18bae33a00 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 11 Jan 2015 11:43:43 +0200 Subject: [PATCH 097/117] Extended check for embedded WADs with one special case Added loading of embedded WAD file if `myfile.wad` is placed in `myfile` directory inside `myfile.zip` This helps with an unpleasant and very annoying fashion to zip a folder instead of just .wad and .txt files. Recent examples include Monster Hunter Ltd.: http://www.doomworld.com/idgames/?id=17601 and http://www.doomworld.com/idgames/?id=17625 Hell Awakened 2 Episode 1: http://www.doomworld.com/idgames/?id=17795 Bauhaus: http://www.doomworld.com/idgames/?id=17954 --- src/resourcefiles/resourcefile.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 8a4f07fd7..24b3ad3d6 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -150,11 +150,22 @@ void FResourceLump::LumpNameSetup(const char *iname) // //========================================================================== +static bool IsWadInFolder(const char* const fullName) +{ + // Checks a special case when was put in + // directory inside + + const FString baseName = ExtractFileBase(fullName); + const FString fileName = baseName + '/' + baseName + ".wad"; + + return 0 == fileName.CompareNoCase(fullName); +} + void FResourceLump::CheckEmbedded() { // Checks for embedded archives const char *c = strstr(FullName, ".wad"); - if (c && strlen(c) == 4 && !strchr(FullName, '/')) + if (c && strlen(c) == 4 && (!strchr(FullName, '/') || IsWadInFolder(FullName))) { // Mark all embedded WADs Flags |= LUMPF_EMBEDDED; From 9df56216b32617509d79c212ddfce8df290d74c7 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 12 Jan 2015 11:28:40 +0200 Subject: [PATCH 098/117] Reworked check for embedded WADs put inside directory within archive Any WAD from directory named the same as archive are treated as embedded Fixed the issue with archive filename that wasn't taken into account --- src/resourcefiles/resourcefile.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 24b3ad3d6..15a4337b1 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -150,22 +150,28 @@ void FResourceLump::LumpNameSetup(const char *iname) // //========================================================================== -static bool IsWadInFolder(const char* const fullName) +static bool IsWadInFolder(const FResourceFile* const archive, const char* const resPath) { - // Checks a special case when was put in - // directory inside + // Checks a special case when was put in + // directory inside - const FString baseName = ExtractFileBase(fullName); - const FString fileName = baseName + '/' + baseName + ".wad"; + if (NULL == archive) + { + return false; + } - return 0 == fileName.CompareNoCase(fullName); + const FString dirName = ExtractFileBase(archive->Filename); + const FString fileName = ExtractFileBase(resPath, true); + const FString filePath = dirName + '/' + fileName; + + return 0 == filePath.CompareNoCase(resPath); } void FResourceLump::CheckEmbedded() { // Checks for embedded archives const char *c = strstr(FullName, ".wad"); - if (c && strlen(c) == 4 && (!strchr(FullName, '/') || IsWadInFolder(FullName))) + if (c && strlen(c) == 4 && (!strchr(FullName, '/') || IsWadInFolder(Owner, FullName))) { // Mark all embedded WADs Flags |= LUMPF_EMBEDDED; From db25322b4c479a4b729918c06caa26bf7008aaf8 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 13 Jan 2015 21:01:00 +1300 Subject: [PATCH 099/117] P_SpawnPuff MF4_RANDOMIZE behaviour - Doom's BulletPuff random spawn tics behaviour was lost at some point. --- src/p_mobj.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 09f84663a..fea80f868 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4959,6 +4959,13 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE); if (puff == NULL) return NULL; + if ((puff->flags4 & MF4_RANDOMIZE) && puff->tics > 0) + { + puff->tics -= pr_spawnpuff() & 3; + if (puff->tics < 1) + puff->tics = 1; + } + //Moved puff creation and target/master/tracer setting to here. if (puff && vict) { From 1c2a6e8457237bdbe355ad402c2fa11b6a470927 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 16 Jan 2015 13:12:18 -0500 Subject: [PATCH 100/117] - Fixed: fullscreenoffset coordinates were determined by translated (offsets) coordinates instead of input coordinates. --- src/g_shared/sbarinfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 7a11a2865..8ffedb5a4 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1280,8 +1280,8 @@ public: // We can't use DTA_HUDRules since it forces a width and height. // Translation: No high res. - bool xright = rx < 0; - bool ybot = ry < 0; + bool xright = *x < 0; + bool ybot = *y < 0; w = (forceWidth < 0 ? texture->GetScaledWidthDouble() : forceWidth); h = (forceHeight < 0 ? texture->GetScaledHeightDouble() : forceHeight); From 4bae3f99762ac7c1d602fdcc95142e10809d28ea Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 16 Jan 2015 18:58:23 -0500 Subject: [PATCH 101/117] - Fixed: SDLVideo::CreateFrameBuffer cleared "retry" too soon resulting in infinite recursion if the code was used. --- src/posix/sdl/sdlvideo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posix/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp index 7a04ce901..309002456 100644 --- a/src/posix/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -286,7 +286,6 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscree } SDLFB *fb = new SDLFB (width, height, fullscreen); - retry = 0; // If we could not create the framebuffer, try again with slightly // different parameters in this order: @@ -327,6 +326,7 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscree ++retry; fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); } + retry = 0; fb->SetFlash (flashColor, flashAmount); From 845bcdf14c0beda51d6bfd0c9db062c287e35d8b Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 16 Jan 2015 19:13:51 -0500 Subject: [PATCH 102/117] - Attempt to disable all comparison operators on FString since unless we decide otherwise, it's a programming error to use them (caused implicit conversion to const char* and then the built in comparision was called). --- src/zstring.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/zstring.h b/src/zstring.h index 0104020fa..82c38142c 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -296,6 +296,16 @@ protected: static FNullStringData NullString; friend struct FStringData; + +private: + // Prevent these from being called as current practices are to use Compare. + // Without this FStrings will be accidentally compared against char* ptrs. + bool operator == (const FString &illegal) const; + bool operator != (const FString &illegal) const; + bool operator < (const FString &illegal) const; + bool operator > (const FString &illegal) const; + bool operator <= (const FString &illegal) const; + bool operator >= (const FString &illegal) const; }; namespace StringFormat From 105a62cc20f878e86ad3b348888d327aea330d5b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 19 Jan 2015 14:54:20 -0600 Subject: [PATCH 103/117] - Added pointer field to A_ChangeVelocity. - Defaults to AAPTR_DEFAULT, otherwise known as the calling actor. --- src/thingdef/thingdef_codeptr.cpp | 27 ++++++++++++++++++--------- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 300ee7556..d522f284a 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4029,12 +4029,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) ACTION_PARAM_FIXED(y, 1); ACTION_PARAM_FIXED(z, 2); ACTION_PARAM_INT(flags, 3); + ACTION_PARAM_INT(ptr, 4); - INTBOOL was_moving = self->velx | self->vely | self->velz; + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + INTBOOL was_moving = ref->velx | ref->vely | ref->velz; fixed_t vx = x, vy = y, vz = z; - fixed_t sina = finesine[self->angle >> ANGLETOFINESHIFT]; - fixed_t cosa = finecosine[self->angle >> ANGLETOFINESHIFT]; + fixed_t sina = finesine[ref->angle >> ANGLETOFINESHIFT]; + fixed_t cosa = finecosine[ref->angle >> ANGLETOFINESHIFT]; if (flags & 1) // relative axes - make x, y relative to actor's current angle { @@ -4043,15 +4052,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) } if (flags & 2) // discard old velocity - replace old velocity with new velocity { - self->velx = vx; - self->vely = vy; - self->velz = vz; + ref->velx = vx; + ref->vely = vy; + ref->velz = vz; } else // add new velocity to old velocity { - self->velx += vx; - self->vely += vy; - self->velz += vz; + ref->velx += vx; + ref->vely += vy; + ref->velz += vz; } if (was_moving) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index b2fa80c30..44ba7f9b6 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -293,7 +293,7 @@ ACTOR Actor native //: Thinker action native A_SetPitch(float pitch, int flags = 0); action native A_SetRoll(float roll, int flags = 0); action native A_ScaleVelocity(float scale); - action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0); + action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_SetArg(int pos, int value); action native A_SetUserVar(name varname, int value); action native A_SetUserArray(name varname, int index, int value); From 67312b907b22bee6abc794fc19e954820171a649 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Jan 2015 10:25:58 +0100 Subject: [PATCH 104/117] - fixed: AInventory::AbsorbDamage may only be called for positive damage values, otherwise it ends up adding armor points. --- src/p_interaction.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 36a68281e..7bf19a03b 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1278,7 +1278,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL) { int newdam = damage; - player->mo->Inventory->AbsorbDamage(damage, mod, newdam); + if (damage > 0) + { + player->mo->Inventory->AbsorbDamage(damage, mod, newdam); + } if (damage < TELEFRAG_DAMAGE) { // if we are telefragging don't let the damage value go below that magic value. Some further checks would fail otherwise. From fa411af1daab62980d2b77cc73c3fd0723962e6d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 18 Jan 2015 09:21:59 -0600 Subject: [PATCH 105/117] - Fixed: Reflected projectiles were always changing their angles to 0. --- src/p_mobj.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index fea80f868..bb33a88c4 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1970,6 +1970,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) { //int dir; //angle_t delta; + angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle)); // Change angle for deflection/reflection @@ -1989,7 +1990,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) //dest->x - source->x FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z); velocity.Resize(speed); - angle = mo->angle >> ANGLETOFINESHIFT; + angle = mo->angle >>= ANGLETOFINESHIFT; mo->velx = (fixed_t)(velocity.X); mo->vely = (fixed_t)(velocity.Y); mo->velz = (fixed_t)(velocity.Z); @@ -2001,6 +2002,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) } else { + mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); From 4fd87a1ce911065f7d82405843318de99457784c Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 20 Jan 2015 22:54:30 -0600 Subject: [PATCH 106/117] - Redid A_SetHealth. - This function will not take the actor's health below 1, and any attempts to do so will simply set it to 1 in its place. --- src/thingdef/thingdef_codeptr.cpp | 39 ++++++++++++++++++++++++++++++- wadsrc/static/actors/actor.txt | 1 + 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 300ee7556..53b5f7f05 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5656,7 +5656,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) // // A_SetFloatBobPhase // -// Changes the FloatBobPhase of the +// Changes the FloatBobPhase of the actor. //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) @@ -5669,6 +5669,43 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) self->FloatBobPhase = bob; } +//=========================================================================== +// A_SetHealth +// +// Changes the health of the actor. +// Takes a pointer as well. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player) + { + if (health <= 0) + player->mo->health = mobj->health = player->health = 1; //Copied from the buddha cheat. + else + player->mo->health = mobj->health = player->health = health; + } + else if (mobj) + { + if (health <= 0) + mobj->health = 1; + else + mobj->health = health; + } +} + //=========================================================================== // // A_SetRipperLevel(int level) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index b2fa80c30..5fda5c590 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -327,6 +327,7 @@ ACTOR Actor native //: Thinker action native A_SetTeleFog(name oldpos, name newpos); action native A_SwapTeleFog(); action native A_SetFloatBobPhase(int bob); + action native A_SetHealth(int health, int ptr = AAPTR_DEFAULT); action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); From 73bdd06cebd094c8b4329830132ec5a036a2869f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Jan 2015 14:10:55 +0100 Subject: [PATCH 107/117] - removed bogus angle modification from reflection code. --- src/p_mobj.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index bb33a88c4..ef23ca0ba 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1990,15 +1990,9 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) //dest->x - source->x FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z); velocity.Resize(speed); - angle = mo->angle >>= ANGLETOFINESHIFT; mo->velx = (fixed_t)(velocity.X); mo->vely = (fixed_t)(velocity.Y); mo->velz = (fixed_t)(velocity.Z); - /* - mo->velx = FixedMul(mo->Speed, finecosine[angle]); - mo->vely = FixedMul(mo->Speed, finesine[angle]); - mo->velz = -mo->velz; - */ } else { From 7157db89b73db5f6edd2e42ad68a52964e48243c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Jan 2015 14:13:25 +0100 Subject: [PATCH 108/117] - fixed: A_VileAttack didn't check for MF7_DONTTHRUST. This is a fixed version of Major Cookes pull request. --- src/g_doom/a_archvile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_doom/a_archvile.cpp b/src/g_doom/a_archvile.cpp index 8fce33324..f24ff146e 100644 --- a/src/g_doom/a_archvile.cpp +++ b/src/g_doom/a_archvile.cpp @@ -151,5 +151,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack) P_RadiusAttack (fire, self, blastdmg, blastrad, dmgtype, 0); } - target->velz = Scale(thrust, 1000, target->Mass); + if (!(target->flags7 & MF7_DONTTHRUST)) + target->velz = Scale(thrust, 1000, target->Mass); } From 19b43d47528536c97542f6ae277617b43bf234d8 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 23 Jan 2015 09:26:34 -0600 Subject: [PATCH 109/117] - Added A_ResetHealth(ptr). Defaults to AAPTR_DEFAULT (the action caller). - Added pointers to the following functions, all of them set to AAPTR_DEFAULT: - A_SetAngle - A_SetPitch - A_SetScale - A_SetSpeed - A_ScaleVelocity --- src/thingdef/thingdef_codeptr.cpp | 113 +++++++++++++++++++++++++----- wadsrc/static/actors/actor.txt | 11 +-- 2 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 926799b7a..32bc9d315 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2504,12 +2504,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_FIXED(scalex, 0); ACTION_PARAM_FIXED(scaley, 1); + ACTION_PARAM_INT(ptr, 2); - self->scaleX = scalex; - self->scaleY = scaley ? scaley : scalex; + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->scaleX = scalex; + ref->scaleY = scaley ? scaley : scalex; } //=========================================================================== @@ -3934,10 +3943,19 @@ enum DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_ANGLE(angle, 0); - ACTION_PARAM_INT(flags, 1) - self->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); } //=========================================================================== @@ -3950,18 +3968,27 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_ANGLE(pitch, 0); ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); - if (self->player != NULL || (flags & SPF_FORCECLAMP)) + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + if (ref->player != NULL || (flags & SPF_FORCECLAMP)) { // clamp the pitch we set int min, max; - if (self->player != NULL) + if (ref->player != NULL) { - min = self->player->MinPitch; - max = self->player->MaxPitch; + min = ref->player->MinPitch; + max = ref->player->MaxPitch; } else { @@ -3970,7 +3997,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) } pitch = clamp(pitch, min, max); } - self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); + ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); } //=========================================================================== @@ -3999,20 +4026,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_FIXED(scale, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } INTBOOL was_moving = self->velx | self->vely | self->velz; - self->velx = FixedMul(self->velx, scale); - self->vely = FixedMul(self->vely, scale); - self->velz = FixedMul(self->velz, scale); + ref->velx = FixedMul(ref->velx, scale); + ref->vely = FixedMul(ref->vely, scale); + ref->velz = FixedMul(ref->velz, scale); // If the actor was previously moving but now is not, and is a player, // update its player variables. (See A_Stop.) if (was_moving) { - CheckStopped(self); + CheckStopped(ref); } } @@ -5073,10 +5109,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_FIXED(speed, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } - self->Speed = speed; + ref->Speed = speed; } static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) @@ -5715,6 +5760,36 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) } } +//=========================================================================== +// A_ResetHealth +// +// Resets the health of the actor to default, except if their dead. +// Takes a pointer. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr, 0); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player && (player->mo->health > 0)) + { + player->health = player->mo->health = player->mo->GetDefault()->health; //Copied from the resurrect cheat. + } + else if (mobj && (mobj->health > 0)) + { + mobj->health = mobj->GetDefault()->health; + } +} + //=========================================================================== // // A_SetRipperLevel(int level) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index ded3a0b93..f3c567c71 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -231,7 +231,7 @@ ACTOR Actor native //: Thinker action native A_FadeIn(float reduce = 0.1, int flags = 0); action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true action native A_FadeTo(float target, float amount = 0.1, int flags = 0); - action native A_SetScale(float scalex, float scaley = 0); + action native A_SetScale(float scalex, float scaley = 0, int ptr = AAPTR_DEFAULT); action native A_SetMass(int mass); action native A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); action native A_CheckSight(state label); @@ -289,10 +289,10 @@ ACTOR Actor native //: Thinker action native A_DropWeaponPieces(class p1, class p2, class p3); action native A_PigPain (); action native A_MonsterRefire(int chance, state label); - action native A_SetAngle(float angle = 0, int flags = 0); - action native A_SetPitch(float pitch, int flags = 0); + action native A_SetAngle(float angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + action native A_SetPitch(float pitch, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_SetRoll(float roll, int flags = 0); - action native A_ScaleVelocity(float scale); + action native A_ScaleVelocity(float scale, int ptr = AAPTR_DEFAULT); action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_SetArg(int pos, int value); action native A_SetUserVar(name varname, int value); @@ -302,7 +302,7 @@ ACTOR Actor native //: Thinker action native A_SetTics(int tics); action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); - action native A_SetSpeed(float speed); + action native A_SetSpeed(float speed, int ptr = AAPTR_DEFAULT); action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); @@ -328,6 +328,7 @@ ACTOR Actor native //: Thinker action native A_SwapTeleFog(); action native A_SetFloatBobPhase(int bob); action native A_SetHealth(int health, int ptr = AAPTR_DEFAULT); + action native A_ResetHealth(int ptr = AAPTR_DEFAULT); action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); From 301c061ec3520ba592fe9b3c75b5f6c10f0a5145 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 23 Jan 2015 10:24:23 -0600 Subject: [PATCH 110/117] - Added pointer for A_SetRoll. --- src/thingdef/thingdef_codeptr.cpp | 13 +++++++++++-- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 32bc9d315..324500f7c 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4010,10 +4010,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_ANGLE(roll, 0); ACTION_PARAM_INT(flags, 1); - self->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + ref->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); } //=========================================================================== diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index f3c567c71..f0e68d9a5 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -291,7 +291,7 @@ ACTOR Actor native //: Thinker action native A_MonsterRefire(int chance, state label); action native A_SetAngle(float angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_SetPitch(float pitch, int flags = 0, int ptr = AAPTR_DEFAULT); - action native A_SetRoll(float roll, int flags = 0); + action native A_SetRoll(float roll, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_ScaleVelocity(float scale, int ptr = AAPTR_DEFAULT); action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_SetArg(int pos, int value); From d1cca79c3178afb4dc4ef087cb60e4c1a8ec79a8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Jan 2015 20:29:38 +0100 Subject: [PATCH 111/117] - Use SpawnHealth() when calling A_ResetHealth. --- src/thingdef/thingdef_codeptr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 324500f7c..be3921877 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5795,7 +5795,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) } else if (mobj && (mobj->health > 0)) { - mobj->health = mobj->GetDefault()->health; + mobj->health = mobj->SpawnHealth(); } } From 92fbe47ade6129585768d75ceb7b32c94b603e52 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Jan 2015 14:04:06 +0100 Subject: [PATCH 112/117] - fixed and cleaned up ALoreShot::DoSpecialDamage. --- src/g_strife/a_loremaster.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/g_strife/a_loremaster.cpp b/src/g_strife/a_loremaster.cpp index d720b7b9b..38cadab34 100644 --- a/src/g_strife/a_loremaster.cpp +++ b/src/g_strife/a_loremaster.cpp @@ -14,27 +14,27 @@ class ALoreShot : public AActor { DECLARE_CLASS (ALoreShot, AActor) public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); + int DoSpecialDamage (AActor *victim, int damage, FName damagetype); }; IMPLEMENT_CLASS (ALoreShot) -int ALoreShot::DoSpecialDamage (AActor *target, int damage, FName damagetype) +int ALoreShot::DoSpecialDamage (AActor *victim, int damage, FName damagetype) { FVector3 thrust; - if (this->target != NULL && !(this->target->flags7 & MF7_DONTTHRUST)) + if (victim != NULL && target != NULL && !(victim->flags7 & MF7_DONTTHRUST)) { - thrust.X = float(this->target->x - target->x); - thrust.Y = float(this->target->y - target->y); - thrust.Z = float(this->target->z - target->z); + thrust.X = float(target->x - victim->x); + thrust.Y = float(target->y - victim->y); + thrust.Z = float(target->z - victim->z); thrust.MakeUnit(); - thrust *= float((255*50*FRACUNIT) / (target->Mass ? target->Mass : 1)); + thrust *= float((255*50*FRACUNIT) / (victim->Mass ? target->Mass : 1)); - target->velx += fixed_t(thrust.X); - target->vely += fixed_t(thrust.Y); - target->velz += fixed_t(thrust.Z); + victim->velx += fixed_t(thrust.X); + victim->vely += fixed_t(thrust.Y); + victim->velz += fixed_t(thrust.Z); } return damage; } From 04c6c5ca55989ed07181ebdfbdb70bcde4546267 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sat, 24 Jan 2015 14:17:47 +0100 Subject: [PATCH 113/117] Fix missing 'victim' replace in ALoreShot code. --- src/g_strife/a_loremaster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_strife/a_loremaster.cpp b/src/g_strife/a_loremaster.cpp index 38cadab34..d984bbdd3 100644 --- a/src/g_strife/a_loremaster.cpp +++ b/src/g_strife/a_loremaster.cpp @@ -30,7 +30,7 @@ int ALoreShot::DoSpecialDamage (AActor *victim, int damage, FName damagetype) thrust.Z = float(target->z - victim->z); thrust.MakeUnit(); - thrust *= float((255*50*FRACUNIT) / (victim->Mass ? target->Mass : 1)); + thrust *= float((255*50*FRACUNIT) / (victim->Mass ? victim->Mass : 1)); victim->velx += fixed_t(thrust.X); victim->vely += fixed_t(thrust.Y); From a4c07a9ee3a445f095d9618f928b1cb83dc54b0f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Jan 2015 20:15:58 +0100 Subject: [PATCH 114/117] - fixed: Since A_Give/TaketoChildren/siblings use the same code as A_Give/TakeInventory but do not use the third parameter this must be ignored by the code. This is merely a band-aid fix, because this problem doesn't exist in the same form in the scripting branch. --- src/thingdef/thingdef_codeptr.cpp | 11665 ++++++++++++++-------------- 1 file changed, 5840 insertions(+), 5825 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index be3921877..63c5d7408 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1,4005 +1,4020 @@ -/* -** thingdef.cpp -** -** Code pointers for Actor definitions -** -**--------------------------------------------------------------------------- -** Copyright 2002-2006 Christoph Oelckers -** Copyright 2004-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be -** covered by 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 SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "gi.h" -#include "g_level.h" -#include "actor.h" -#include "info.h" -#include "sc_man.h" -#include "tarray.h" -#include "w_wad.h" -#include "templates.h" -#include "r_defs.h" -#include "a_pickups.h" -#include "s_sound.h" -#include "cmdlib.h" -#include "p_lnspec.h" -#include "p_enemy.h" -#include "a_action.h" -#include "decallib.h" -#include "m_random.h" -#include "i_system.h" -#include "p_local.h" -#include "c_console.h" -#include "doomerrors.h" -#include "a_sharedglobal.h" -#include "thingdef/thingdef.h" -#include "v_video.h" -#include "v_font.h" -#include "doomstat.h" -#include "v_palette.h" -#include "g_shared/a_specialspot.h" -#include "actorptrselect.h" -#include "m_bbox.h" -#include "r_data/r_translate.h" -#include "p_trace.h" -#include "gstrings.h" - - -static FRandom pr_camissile ("CustomActorfire"); -static FRandom pr_camelee ("CustomMelee"); -static FRandom pr_cabullet ("CustomBullet"); -static FRandom pr_cajump ("CustomJump"); -static FRandom pr_cwbullet ("CustomWpBullet"); -static FRandom pr_cwjump ("CustomWpJump"); -static FRandom pr_cwpunch ("CustomWpPunch"); -static FRandom pr_grenade ("ThrowGrenade"); -static FRandom pr_crailgun ("CustomRailgun"); -static FRandom pr_spawndebris ("SpawnDebris"); -static FRandom pr_spawnitemex ("SpawnItemEx"); -static FRandom pr_burst ("Burst"); -static FRandom pr_monsterrefire ("MonsterRefire"); -static FRandom pr_teleport("A_Teleport"); - -//========================================================================== -// -// ACustomInventory :: CallStateChain -// -// Executes the code pointers in a chain of states -// until there is no next state -// -//========================================================================== - -bool ACustomInventory::CallStateChain (AActor *actor, FState * State) -{ - StateCallData StateCall; - bool result = false; - int counter = 0; - - while (State != NULL) - { - // Assume success. The code pointer will set this to false if necessary - StateCall.State = State; - StateCall.Result = true; - if (State->CallAction(actor, this, &StateCall)) - { - // collect all the results. Even one successful call signifies overall success. - result |= StateCall.Result; - } - - - // Since there are no delays it is a good idea to check for infinite loops here! - counter++; - if (counter >= 10000) break; - - if (StateCall.State == State) - { - // Abort immediately if the state jumps to itself! - if (State == State->GetNextState()) - { - return false; - } - - // If both variables are still the same there was no jump - // so we must advance to the next state. - State = State->GetNextState(); - } - else - { - State = StateCall.State; - } - } - return result; -} - -//========================================================================== -// -// A_RearrangePointers -// -// Allow an actor to change its relationship to other actors by -// copying pointers freely between TARGET MASTER and TRACER. -// Can also assign null value, but does not duplicate A_ClearTarget. -// -//========================================================================== - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(ptr_target, 0); - ACTION_PARAM_INT(ptr_master, 1); - ACTION_PARAM_INT(ptr_tracer, 2); - ACTION_PARAM_INT(flags, 3); - - // Rearrange pointers internally - - // Fetch all values before modification, so that all fields can get original values - AActor - *gettarget = self->target, - *getmaster = self->master, - *gettracer = self->tracer; - - switch (ptr_target) // pick the new target - { - case AAPTR_MASTER: - self->target = getmaster; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_TRACER: - self->target = gettracer; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_NULL: - self->target = NULL; - // THIS IS NOT "A_ClearTarget", so no other targeting info is removed - break; - } - - // presently permitting non-monsters to set master - switch (ptr_master) // pick the new master - { - case AAPTR_TARGET: - self->master = gettarget; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_TRACER: - self->master = gettracer; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_NULL: - self->master = NULL; - break; - } - - switch (ptr_tracer) // pick the new tracer - { - case AAPTR_TARGET: - self->tracer = gettarget; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_MASTER: - self->tracer = getmaster; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_NULL: - self->tracer = NULL; - break; - } -} - -//========================================================================== -// -// A_TransferPointer -// -// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), -// or from this actor's MASTER, TARGET or TRACER. -// -// You can copy any one of that actor's pointers -// -// Assign the copied pointer to any one pointer in SELF, -// MASTER, TARGET or TRACER. -// -// Any attempt to make an actor point to itself will replace the pointer -// with a null value. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ptr_source, 0); - ACTION_PARAM_INT(ptr_recepient, 1); - ACTION_PARAM_INT(ptr_sourcefield, 2); - ACTION_PARAM_INT(ptr_recepientfield, 3); - ACTION_PARAM_INT(flags, 4); - - AActor *source, *recepient; - - // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) - - source = COPY_AAPTR(self, ptr_source); - COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value - - // convert source from dataprovider to data - - source = COPY_AAPTR(source, ptr_sourcefield); - - if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL - - if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from - - ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); -} - -//========================================================================== -// -// A_CopyFriendliness -// -// Join forces with one of the actors you are pointing to (MASTER by default) -// -// Normal CopyFriendliness reassigns health. This function will not. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(ptr_source, 0); - - if (self->player) return; - - AActor *source; - COPY_AAPTR_NOT_NULL(self, source, ptr_source); - self->CopyFriendliness(source, false, false); // No change in current target or health -} - -//========================================================================== -// -// Simple flag changers -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) -{ - self->flags |= MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) -{ - self->flags &= ~MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) -{ - self->flags |= MF_FLOAT; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) -{ - self->flags &= ~(MF_FLOAT|MF_INFLOAT); -} - -//========================================================================== -// -// Customizable attack functions which use actor parameters. -// -//========================================================================== -static void DoAttack (AActor *self, bool domelee, bool domissile, - int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) -{ - if (self->target == NULL) return; - - A_FaceTarget (self); - if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) - { - int damage = pr_camelee.HitDice(MeleeDamage); - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (domissile && MissileType != NULL) - { - // This seemingly senseless code is needed for proper aiming. - self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); - self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); -} - -DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) -{ - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(MeleeDamage, 0); - ACTION_PARAM_SOUND(MeleeSound, 1); - ACTION_PARAM_CLASS(MissileType, 2); - ACTION_PARAM_FIXED(MissileHeight, 3); - - if (MissileType == NULL) return; - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -//========================================================================== -// -// Custom sound functions. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_INT(channel, 1); - ACTION_PARAM_FLOAT(volume, 2); - ACTION_PARAM_BOOL(looping, 3); - ACTION_PARAM_FLOAT(attenuation, 4); - - if (!looping) - { - S_Sound (self, channel, soundid, volume, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, channel&7, soundid)) - { - S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(slot, 0); - - S_StopSound(self, slot); -} - -//========================================================================== -// -// These come from a time when DECORATE constants did not exist yet and -// the sound interface was less flexible. As a result the parameters are -// not optimal and these functions have been deprecated in favor of extending -// A_PlaySound and A_StopSound. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_SOUND(soundid, 0); - - S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_NAME(channel, 1); - ACTION_PARAM_BOOL(looping, 2); - ACTION_PARAM_INT(attenuation_raw, 3); - - float attenuation; - switch (attenuation_raw) - { - case -1: attenuation = ATTN_STATIC; break; // drop off rapidly - default: - case 0: attenuation = ATTN_NORM; break; // normal - case 1: - case 2: attenuation = ATTN_NONE; break; // full volume - } - - if (channel < NAME_Auto || channel > NAME_SoundSlot7) - { - channel = NAME_Auto; - } - - if (!looping) - { - S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) - { - S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(channel, 0); - - if (channel > NAME_Auto && channel <= NAME_SoundSlot7) - { - S_StopSound (self, int(channel) - NAME_Auto); - } -} - -//========================================================================== -// -// Generic seeker missile function -// -//========================================================================== -static FRandom pr_seekermissile ("SeekerMissile"); -enum -{ - SMF_LOOK = 1, - SMF_PRECISE = 2, - SMF_CURSPEED = 4, -}; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ang1, 0); - ACTION_PARAM_INT(ang2, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(chance, 3); - ACTION_PARAM_INT(distance, 4); - - if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); - } - if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) - { - if (flags & SMF_LOOK) - { // This monster is no longer seekable, so let us look for another one next time. - self->tracer = NULL; - } - } -} - -//========================================================================== -// -// Hitscan attack with a customizable amount of bullets (specified in damage) -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) -{ - int i; - int bangle; - int slope; - - if (!self->target) return; - - A_FaceTarget (self); - bangle = self->angle; - - slope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i = self->GetMissileDamage (0, 1); i > 0; --i) - { - int angle = bangle + (pr_cabullet.Random2() << 20); - int damage = ((pr_cabullet()%5)+1)*3; - P_LineAttack(self, angle, MISSILERANGE, slope, damage, - NAME_Hitscan, NAME_BulletPuff); - } -} - - -//========================================================================== -// -// Do the state jump -// -//========================================================================== -static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) -{ - if (jumpto == NULL) return; - - if (statecall != NULL) - { - statecall->State = jumpto; - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) - { - P_SetPsprite(self->player, ps_weapon, jumpto); - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) - { - P_SetPsprite(self->player, ps_flash, jumpto); - } - else if (CallingState == self->state) - { - self->SetState (jumpto); - } - else - { - // something went very wrong. This should never happen. - assert(false); - } -} - -// This is just to avoid having to directly reference the internally defined -// CallingState and statecall parameters in the code below. -#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_INT(maxchance, 1); - - if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) - { - int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); - ACTION_PARAM_STATE(jumpto, jumps); - ACTION_JUMP(jumpto); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(health, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_INT(ptr_selector, 2); - - AActor *measured; - - measured = COPY_AAPTR(self, ptr_selector); - - if (measured && measured->health < health) - { - ACTION_JUMP(jump); - } - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (!self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(dist, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - // No target - no jump - if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && - ( (self->z > target->z && self->z - (target->z + target->height) < dist) || - (self->z <=target->z && target->z - (self->z + self->height) < dist) - ) - ) - { - ACTION_JUMP(jump); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) -{ - AActor *target; - - if (!self->player) - { - target = self->target; - } - else - { - // Does the player aim at something that can be shot? - P_BulletSlope(self, &target); - } - DoJumpIfCloser(target, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) -{ - DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) -{ - DoJumpIfCloser(self->master, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(Type, 0); - ACTION_PARAM_INT(ItemAmount, 1); - ACTION_PARAM_STATE(JumpOffset, 2); - ACTION_PARAM_INT(setowner, 3); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (!Type) return; - COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL - - AInventory *Item = owner->FindInventory(Type); - - if (Item) - { - if (ItemAmount > 0) - { - if (Item->Amount >= ItemAmount) - ACTION_JUMP(JumpOffset); - } - else if (Item->Amount >= Item->MaxAmount) - { - ACTION_JUMP(JumpOffset); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) -{ - DoJumpIfInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) -{ - DoJumpIfInventory(self->target, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(Type, 0); - ACTION_PARAM_STATE(JumpOffset, 1); - ACTION_PARAM_INT(amount, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); - - if (armor && armor->ArmorType == Type && armor->Amount >= amount) - ACTION_JUMP(JumpOffset); -} - -//========================================================================== -// -// Parameterized version of A_Explode -// -//========================================================================== - -enum -{ - XF_HURTSOURCE = 1, - XF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_INT(damage, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_BOOL(alert, 3); - ACTION_PARAM_INT(fulldmgdistance, 4); - ACTION_PARAM_INT(nails, 5); - ACTION_PARAM_INT(naildamage, 6); - ACTION_PARAM_CLASS(pufftype, 7); - - if (damage < 0) // get parameters from metadata - { - damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); - distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); - flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); - alert = false; - } - else - { - if (distance <= 0) distance = damage; - } - // NailBomb effect, from SMMU but not from its source code: instead it was implemented and - // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html - - if (nails) - { - angle_t ang; - for (int i = 0; i < nails; i++) - { - ang = i*(ANGLE_MAX/nails); - // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim - P_LineAttack (self, ang, MISSILERANGE, 0, - //P_AimLineAttack (self, ang, MISSILERANGE), - naildamage, NAME_Hitscan, pufftype); - } - } - - P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); - P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) - { - validcount++; - P_RecursiveSound (self->Sector, self->target, false, 0); - } -} - -//========================================================================== -// -// A_RadiusThrust -// -//========================================================================== - -enum -{ - RTF_AFFECTSOURCE = 1, - RTF_NOIMPACTDAMAGE = 2, - RTF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(force, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(fullthrustdistance, 3); - - bool sourcenothrust = false; - - if (force == 0) force = 128; - if (distance <= 0) distance = abs(force); - - // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. - if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) - { - sourcenothrust = true; - self->target->flags2 &= ~MF2_NODMGTHRUST; - } - - P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); - P_CheckSplash(self, distance << FRACBITS); - - if (sourcenothrust) - { - self->target->flags2 |= MF2_NODMGTHRUST; - } -} - -//========================================================================== -// -// Execute a line special / script -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - ACTION_PARAM_INT(arg5, 5); - - bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); - - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// The ultimate code pointer: Fully customizable missiles! -// -//========================================================================== -enum CM_Flags -{ - CMF_AIMMODE = 3, - CMF_TRACKOWNER = 4, - CMF_CHECKTARGETDEAD = 8, - - CMF_ABSOLUTEPITCH = 16, - CMF_OFFSETPITCH = 32, - CMF_SAVEPITCH = 64, - - CMF_ABSOLUTEANGLE = 128 -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(Spawnofs_XY, 2); - ACTION_PARAM_ANGLE(Angle, 3); - ACTION_PARAM_INT(flags, 4); - ACTION_PARAM_ANGLE(pitch, 5); - ACTION_PARAM_INT(ptr, 6); - - AActor *ref = COPY_AAPTR(self, ptr); - - int aimmode = flags & CMF_AIMMODE; - - AActor * targ; - AActor * missile; - - if (ref != NULL || aimmode==2) - { - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = Spawnofs_XY * finecosine[ang]; - fixed_t y = Spawnofs_XY * finesine[ang]; - fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); - - switch (aimmode) - { - case 0: - default: - // same adjustment as above (in all 3 directions this time) - for better aiming! - self->x += x; - self->y += y; - self->z += z; - missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, ref, ti, false); - self->x -= x; - self->y -= y; - self->z -= z; - break; - - case 1: - missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, ref, ti, false); - break; - - case 2: - self->x += x; - self->y += y; - missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); - self->x -= x; - self->y -= y; - - flags |= CMF_ABSOLUTEPITCH; - - break; - } - - if (missile) - { - // Use the actual velocity instead of the missile's Speed property - // so that this can handle missiles with a high vertical velocity - // component properly. - - fixed_t missilespeed; - - if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) - { - if (CMF_OFFSETPITCH & flags) - { - FVector2 velocity (missile->velx, missile->vely); - pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); - } - ang = pitch >> ANGLETOFINESHIFT; - missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); - missile->velz = FixedMul(finesine[ang], missile->Speed); - } - else - { - FVector2 velocity (missile->velx, missile->vely); - missilespeed = (fixed_t)velocity.Length(); - } - - if (CMF_SAVEPITCH & flags) - { - missile->pitch = pitch; - // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter - // contains the unapplied parameter. In that case, it is set as pitch without - // otherwise affecting the spawned actor. - } - - missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; - - ang = missile->angle >> ANGLETOFINESHIFT; - missile->velx = FixedMul (missilespeed, finecosine[ang]); - missile->vely = FixedMul (missilespeed, finesine[ang]); - - // handle projectile shooting projectiles - track the - // links back to a real owner - if (self->isMissile(!!(flags & CMF_TRACKOWNER))) - { - AActor * owner=self ;//->target; - while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; - targ=owner; - missile->target=owner; - // automatic handling of seeker missiles - if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) - { - missile->tracer=self->tracer; - } - } - else if (missile->flags2&MF2_SEEKERMISSILE) - { - // automatic handling of seeker missiles - missile->tracer=self->target; - } - // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong - if (missile->flags4 & MF4_SPECTRAL) - { - if (missile->target != NULL) - { - missile->SetFriendPlayer(missile->target->player); - } - else - { - missile->FriendPlayer = 0; - } - } - P_CheckMissileSpawn(missile, self->radius); - } - } - } - else if (flags & CMF_CHECKTARGETDEAD) - { - // Target is dead and the attack shall be aborted. - if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); - } -} - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum CBA_Flags -{ - CBAF_AIMFACING = 1, - CBAF_NORANDOM = 2, - CBAF_EXPLICITANGLE = 4, - CBAF_NOPITCH = 8, - CBAF_NORANDOMPUFFZ = 16, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(pufftype, 4); - ACTION_PARAM_FIXED(Range, 5); - ACTION_PARAM_INT(Flags, 6); - ACTION_PARAM_INT(ptr, 7); - - AActor *ref = COPY_AAPTR(self, ptr); - - if(Range==0) Range=MISSILERANGE; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if (ref || (Flags & CBAF_AIMFACING)) - { - if (!(Flags & CBAF_AIMFACING)) - { - A_Face(self, ref); - } - bangle = self->angle; - - if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); - - if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i=0 ; itarget) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else - { - if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); - } -} - -//========================================================================== -// -// A fully customizable combo attack -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(damage, 2); - ACTION_PARAM_SOUND(MeleeSound, 3); - ACTION_PARAM_NAME(DamageType, 4); - ACTION_PARAM_BOOL(bleed, 5); - - if (!self->target) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (ti) - { - // This seemingly senseless code is needed for proper aiming. - self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); - self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (!ACTION_CALL_FROM_WEAPON()) return; - - if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) - { - ACTION_JUMP(jump); - } - -} - - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum FB_Flags -{ - FBF_USEAMMO = 1, - FBF_NORANDOM = 2, - FBF_EXPLICITANGLE = 4, - FBF_NOPITCH = 8, - FBF_NOFLASH = 16, - FBF_NORANDOMPUFFZ = 32, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumberOfBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(PuffType, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FIXED(Range, 6); - - if (!self->player) return; - - player_t * player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if ((Flags & FBF_USEAMMO) && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (Range == 0) Range = PLAYERMISSILERANGE; - - if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); - - if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); - bangle = self->angle; - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) - { - int damage = DamagePerBullet; - - if (!(Flags & FBF_NORANDOM)) - damage *= ((pr_cwbullet()%3)+1); - - P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); - } - else - { - if (NumberOfBullets == -1) NumberOfBullets = 1; - for (i=0 ; iplayer) return; - - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - AActor *linetarget; - - if (UseAmmo && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = SpawnOfs_XY * finecosine[ang]; - fixed_t y = SpawnOfs_XY * finesine[ang]; - fixed_t z = SpawnHeight; - fixed_t shootangle = self->angle; - - if (Flags & FPF_AIMATANGLE) shootangle += Angle; - - // Temporarily adjusts the pitch - fixed_t SavedPlayerPitch = self->pitch; - self->pitch -= pitch; - AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); - self->pitch = SavedPlayerPitch; - - // automatic handling of seeker missiles - if (misl) - { - if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; - if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; - if (!(Flags & FPF_AIMATANGLE)) - { - // This original implementation is to aim straight ahead and then offset - // the angle from the resulting direction. - FVector3 velocity(misl->velx, misl->vely, 0); - fixed_t missilespeed = (fixed_t)velocity.Length(); - misl->angle += Angle; - angle_t an = misl->angle >> ANGLETOFINESHIFT; - misl->velx = FixedMul (missilespeed, finecosine[an]); - misl->vely = FixedMul (missilespeed, finesine[an]); - } - } - } -} - - -//========================================================================== -// -// A_CustomPunch -// -// Berserk is not handled here. That can be done with A_CheckIfInventory -// -//========================================================================== - -enum -{ - CPF_USEAMMO = 1, - CPF_DAGGER = 2, - CPF_PULLIN = 4, - CPF_NORANDOMPUFFZ = 8, - CPF_NOTURN = 16, - CPF_STEALARMOR = 32, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_BOOL(norandom, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(PuffType, 3); - ACTION_PARAM_FIXED(Range, 4); - ACTION_PARAM_FIXED(LifeSteal, 5); - ACTION_PARAM_INT(lifestealmax, 6); - ACTION_PARAM_CLASS(armorbonustype, 7); - - if (!self->player) return; - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - - angle_t angle; - int pitch; - AActor * linetarget; - int actualdamage; - - if (!norandom) Damage *= (pr_cwpunch()%8+1); - - angle = self->angle + (pr_cwpunch.Random2() << 18); - if (Range == 0) Range = MELEERANGE; - pitch = P_AimLineAttack (self, angle, Range, &linetarget); - - // only use ammo when actually hitting something! - if ((flags & CPF_USEAMMO) && linetarget && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); - - P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); - - if (linetarget) - { - if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - { - if (flags & CPF_STEALARMOR) - { - if (!armorbonustype) armorbonustype = PClass::FindClass("ArmorBonus"); - - if (armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))) - { - ABasicArmorBonus *armorbonus = static_cast(Spawn (armorbonustype, 0,0,0, NO_REPLACE)); - armorbonus->SaveAmount *= (actualdamage * LifeSteal) >> FRACBITS; - armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax; - armorbonus->flags |= MF_DROPPED; - armorbonus->ClearCounters(); - - if (!armorbonus->CallTryPickup (self)) - { - armorbonus->Destroy (); - } - } - } - - else - { - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS, lifestealmax); - } - } - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if (!(flags & CPF_NOTURN)) - { - // turn to face target - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); - } - - if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; - if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); - } -} - - -//========================================================================== -// -// customizable railgun attack function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_BOOL(UseAmmo, 2); - ACTION_PARAM_COLOR(Color1, 3); - ACTION_PARAM_COLOR(Color2, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - if (!self->player) return; - - AWeapon * weapon=self->player->ReadyWeapon; - - // only use ammo when actually hitting something! - if (UseAmmo) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - angle_t angle; - angle_t slope; - - if (Flags & RAF_EXPLICITANGLE) - { - angle = Spread_XY; - slope = Spread_Z; - } - else - { - angle = pr_crailgun.Random2() * (Spread_XY / 255); - slope = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); -} - -//========================================================================== -// -// also for monsters -// -//========================================================================== -enum -{ - CRF_DONTAIM = 0, - CRF_AIMPARALLEL = 1, - CRF_AIMDIRECT = 2, - CRF_EXPLICITANGLE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_COLOR(Color1, 2); - ACTION_PARAM_COLOR(Color2, 3); - ACTION_PARAM_INT(Flags, 4); - ACTION_PARAM_INT(aim, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - AActor *linetarget; - - fixed_t saved_x = self->x; - fixed_t saved_y = self->y; - angle_t saved_angle = self->angle; - fixed_t saved_pitch = self->pitch; - - if (aim && self->target == NULL) - { - return; - } - // [RH] Andy Baker's stealth monsters - if (self->flags & MF_STEALTH) - { - self->visdir = 1; - } - - self->flags &= ~MF_AMBUSH; - - - if (aim) - { - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x, - self->target->y); - } - self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); - if (linetarget == NULL && aim) - { - // We probably won't hit the target, but aim at it anyway so we don't look stupid. - FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); - double zdiff = (self->target->z + (self->target->height>>1)) - - (self->z + (self->height>>1) - self->floorclip); - self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); - } - // Let the aim trail behind the player - if (aim) - { - saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - - if (aim == CRF_AIMDIRECT) - { - // Tricky: We must offset to the angle of the current position - // but then change the angle again to ensure proper aim. - self->x += Spawnofs_XY * finecosine[self->angle]; - self->y += Spawnofs_XY * finesine[self->angle]; - Spawnofs_XY = 0; - self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - } - - if (self->target->flags & MF_SHADOW) - { - angle_t rnd = pr_crailgun.Random2() << 21; - self->angle += rnd; - saved_angle = rnd; - } - } - - angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; - - angle_t angleoffset; - angle_t slopeoffset; - - if (Flags & CRF_EXPLICITANGLE) - { - angleoffset = Spread_XY; - slopeoffset = Spread_Z; - } - else - { - angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); - slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); - - self->x = saved_x; - self->y = saved_y; - self->angle = saved_angle; - self->pitch = saved_pitch; -} - -//=========================================================================== -// -// DoGiveInventory -// -//=========================================================================== - -static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_CLASS(mi, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(setreceiver, 2); - - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - - bool res=true; - - if (amount==0) amount=1; - if (mi) - { - AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); - if (item->IsKindOf(RUNTIME_CLASS(AHealth))) - { - item->Amount *= amount; - } - else - { - item->Amount = amount; - } - item->flags |= MF_DROPPED; - item->ClearCounters(); - if (!item->CallTryPickup (receiver)) - { - item->Destroy (); - res = false; - } - else res = true; - } - else res = false; - ACTION_SET_RESULT(res); - -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) -{ - DoGiveInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) -{ - DoGiveInventory(self->target, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) -{ - TThinkerIterator it; - AActor * mo; - - while ((mo = it.Next())) - { - if (mo->master == self) DoGiveInventory(mo, PUSH_PARAMINFO); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) -{ - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) DoGiveInventory(mo, PUSH_PARAMINFO); - } - } -} - -//=========================================================================== -// -// A_TakeInventory -// -//=========================================================================== - -enum -{ - TIF_NOTAKEINFINITE = 1, -}; - -void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(setreceiver, 3); - - if (!item) return; - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - - bool res = false; - - AInventory * inv = receiver->FindInventory(item); - - if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) - { - if (inv->Amount > 0) - { - res = true; - } - // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set - // and infinite ammo is on - if (flags & TIF_NOTAKEINFINITE && - ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && - inv->IsKindOf(RUNTIME_CLASS(AAmmo))) - { - // Nothing to do here, except maybe res = false;? Would it make sense? - } - else if (!amount || amount>=inv->Amount) - { - if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; - else inv->Destroy(); - } - else inv->Amount-=amount; - } - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) -{ - DoTakeInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) -{ - DoTakeInventory(self->target, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) -{ - TThinkerIterator it; - AActor * mo; - - while ((mo = it.Next())) - { - if (mo->master == self) DoTakeInventory(mo, PUSH_PARAMINFO); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) -{ - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) DoTakeInventory(mo, PUSH_PARAMINFO); - } - } -} - -//=========================================================================== -// -// Common code for A_SpawnItem and A_SpawnItemEx -// -//=========================================================================== - -enum SIX_Flags -{ - SIXF_TRANSFERTRANSLATION = 1 << 0, - SIXF_ABSOLUTEPOSITION = 1 << 1, - SIXF_ABSOLUTEANGLE = 1 << 2, - SIXF_ABSOLUTEVELOCITY = 1 << 3, - SIXF_SETMASTER = 1 << 4, - SIXF_NOCHECKPOSITION = 1 << 5, - SIXF_TELEFRAG = 1 << 6, - SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum - SIXF_TRANSFERAMBUSHFLAG = 1 << 8, - SIXF_TRANSFERPITCH = 1 << 9, - SIXF_TRANSFERPOINTERS = 1 << 10, - SIXF_USEBLOODCOLOR = 1 << 11, - SIXF_CLEARCALLERTID = 1 << 12, - SIXF_MULTIPLYSPEED = 1 << 13, - SIXF_TRANSFERSCALE = 1 << 14, - SIXF_TRANSFERSPECIAL = 1 << 15, - SIXF_CLEARCALLERSPECIAL = 1 << 16, - SIXF_TRANSFERSTENCILCOL = 1 << 17, - SIXF_TRANSFERALPHA = 1 << 18, - SIXF_TRANSFERRENDERSTYLE = 1 << 19, - SIXF_SETTARGET = 1 << 20, - SIXF_SETTRACER = 1 << 21, - SIXF_NOPOINTERS = 1 << 22, - SIXF_ORIGINATOR = 1 << 23, -}; - -static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) -{ - if (mo == NULL) - { - return false; - } - AActor *originator = self; - - if (!(mo->flags2 & MF2_DONTTRANSLATE)) - { - if (flags & SIXF_TRANSFERTRANSLATION) - { - mo->Translation = self->Translation; - } - else if (flags & SIXF_USEBLOODCOLOR) - { - // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. - PalEntry bloodcolor = self->GetBloodColor(); - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); - } - } - if (flags & SIXF_TRANSFERPOINTERS) - { - mo->target = self->target; - mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set - mo->tracer = self->tracer; - } - - mo->angle = self->angle; - if (flags & SIXF_TRANSFERPITCH) - { - mo->pitch = self->pitch; - } - if (!(flags & SIXF_ORIGINATOR)) - { - while (originator && originator->isMissile()) - { - originator = originator->target; - } - } - if (flags & SIXF_TELEFRAG) - { - P_TeleportMove(mo, mo->x, mo->y, mo->z, true); - // This is needed to ensure consistent behavior. - // Otherwise it will only spawn if nothing gets telefragged - flags |= SIXF_NOCHECKPOSITION; - } - if (mo->flags3 & MF3_ISMONSTER) - { - if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) - { - // The monster is blocked so don't spawn it at all! - mo->ClearCounters(); - mo->Destroy(); - return false; - } - else if (originator && !(flags & SIXF_NOPOINTERS)) - { - if (originator->flags3 & MF3_ISMONSTER) - { - // If this is a monster transfer all friendliness information - mo->CopyFriendliness(originator, true); - } - else if (originator->player) - { - // A player always spawns a monster friendly to him - mo->flags |= MF_FRIENDLY; - mo->SetFriendPlayer(originator->player); - - AActor * attacker=originator->player->attacker; - if (attacker) - { - if (!(attacker->flags&MF_FRIENDLY) || - (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) - { - // Target the monster which last attacked the player - mo->LastHeard = mo->target = attacker; - } - } - } - } - } - else if (!(flags & SIXF_TRANSFERPOINTERS)) - { - // If this is a missile or something else set the target to the originator - mo->target = originator ? originator : self; - } - if (flags & SIXF_NOPOINTERS) - { - //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. - mo->LastHeard = NULL; //Sanity check. - mo->target = NULL; - mo->master = NULL; - mo->tracer = NULL; - } - if (flags & SIXF_SETMASTER) - { - mo->master = originator; - } - if (flags & SIXF_SETTARGET) - { - mo->target = originator; - } - if (flags & SIXF_SETTRACER) - { - mo->tracer = originator; - } - if (flags & SIXF_TRANSFERSCALE) - { - mo->scaleX = self->scaleX; - mo->scaleY = self->scaleY; - } - if (flags & SIXF_TRANSFERAMBUSHFLAG) - { - mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); - } - if (flags & SIXF_CLEARCALLERTID) - { - self->RemoveFromHash(); - self->tid = 0; - } - if (flags & SIXF_TRANSFERSPECIAL) - { - mo->special = self->special; - memcpy(mo->args, self->args, sizeof(self->args)); - } - if (flags & SIXF_CLEARCALLERSPECIAL) - { - self->special = 0; - memset(self->args, 0, sizeof(self->args)); - } - if (flags & SIXF_TRANSFERSTENCILCOL) - { - mo->fillcolor = self->fillcolor; - } - if (flags & SIXF_TRANSFERALPHA) - { - mo->alpha = self->alpha; - } - if (flags & SIXF_TRANSFERRENDERSTYLE) - { - mo->RenderStyle = self->RenderStyle; - } - - return true; -} - -//=========================================================================== -// -// A_SpawnItem -// -// Spawns an item in front of the caller like Heretic's time bomb -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_FIXED(zheight, 2); - ACTION_PARAM_BOOL(useammo, 3); - ACTION_PARAM_BOOL(transfer_translation, 4); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - // Don't spawn monsters if this actor has been massacred - if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - if (distance==0) - { - // use the minimum distance that does not result in an overlap - distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; - } - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon so use some ammo - AWeapon * weapon=self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - AActor * mo = Spawn( missile, - self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), - self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), - self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); - - int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state -} - -//=========================================================================== -// -// A_SpawnItemEx -// -// Enhanced spawning function -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) -{ - ACTION_PARAM_START(11); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_FIXED(xvel, 4); - ACTION_PARAM_FIXED(yvel, 5); - ACTION_PARAM_FIXED(zvel, 6); - ACTION_PARAM_ANGLE(Angle, 7); - ACTION_PARAM_INT(flags, 8); - ACTION_PARAM_INT(chance, 9); - ACTION_PARAM_INT(tid, 10); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - fixed_t x,y; - - if (!(flags & SIXF_ABSOLUTEANGLE)) - { - Angle += self->angle; - } - - angle_t ang = Angle >> ANGLETOFINESHIFT; - - if (flags & SIXF_ABSOLUTEPOSITION) - { - x = self->x + xofs; - y = self->y + yofs; - } - else - { - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); - y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); - } - - if (!(flags & SIXF_ABSOLUTEVELOCITY)) - { - // Same orientation issue here! - fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); - yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); - xvel = newxvel; - } - - AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state - if (res) - { - if (tid != 0) - { - assert(mo->tid == 0); - mo->tid = tid; - mo->AddToHash(); - } - if (flags & SIXF_MULTIPLYSPEED) - { - mo->velx = FixedMul(xvel, mo->Speed); - mo->vely = FixedMul(yvel, mo->Speed); - mo->velz = FixedMul(zvel, mo->Speed); - } - else - { - mo->velx = xvel; - mo->vely = yvel; - mo->velz = zvel; - } - mo->angle = Angle; - } -} - -//=========================================================================== -// -// A_ThrowGrenade -// -// Throws a grenade (like Hexen's fighter flechette) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(zheight, 1); - ACTION_PARAM_FIXED(xyvel, 2); - ACTION_PARAM_FIXED(zvel, 3); - ACTION_PARAM_BOOL(useammo, 4); - - if (missile == NULL) return; - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon, so use some ammo - AWeapon *weapon = self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - - AActor * bo; - - bo = Spawn(missile, self->x, self->y, - self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), - ALLOW_REPLACE); - if (bo) - { - P_PlaySpawnSound(bo, self); - if (xyvel != 0) - bo->Speed = xyvel; - bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); - - angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; - angle_t angle = bo->angle >> ANGLETOFINESHIFT; - - // There are two vectors we are concerned about here: xy and z. We rotate - // them separately according to the shooter's pitch and then sum them to - // get the final velocity vector to shoot with. - - fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); - fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); - fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); - fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); - - pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; - fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); - fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); - fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); - fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); - - bo->velx = xy_velx + z_velx + (self->velx >> 1); - bo->vely = xy_vely + z_vely + (self->vely >> 1); - bo->velz = xy_velz + z_velz; - - bo->target = self; - P_CheckMissileSpawn (bo, self->radius); - } - else ACTION_SET_RESULT(false); -} - - -//=========================================================================== -// -// A_Recoil -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(xyvel, 0); - - angle_t angle = self->angle + ANG180; - angle >>= ANGLETOFINESHIFT; - self->velx += FixedMul (xyvel, finecosine[angle]); - self->vely += FixedMul (xyvel, finesine[angle]); -} - - -//=========================================================================== -// -// A_SelectWeapon -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(cls, 0); - - if (cls == NULL || self->player == NULL) - { - ACTION_SET_RESULT(false); - return; - } - - AWeapon * weaponitem = static_cast(self->FindInventory(cls)); - - if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) - { - if (self->player->ReadyWeapon != weaponitem) - { - self->player->PendingWeapon = weaponitem; - } - } - else ACTION_SET_RESULT(false); - -} - - -//=========================================================================== -// -// A_Print -// -//=========================================================================== -EXTERN_CVAR(Float, con_midtime) - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - if (text[0] == '$') text = GStrings(text+1); - if (self->CheckLocalView (consoleplayer) || - (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) - { - float saved = con_midtime; - FFont *font = NULL; - - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - } - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_PrintBold -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - float saved = con_midtime; - FFont *font = NULL; - - if (text[0] == '$') text = GStrings(text+1); - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_Log -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STRING(text, 0); - - if (text[0] == '$') text = GStrings(text+1); - FString formatted = strbin1(text); - Printf("%s\n", formatted.GetChars()); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//========================================================================= -// -// A_LogInt -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(num, 0); - Printf("%d\n", num); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_SetTranslucent -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(alpha, 0); - ACTION_PARAM_INT(mode, 1); - - mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha = clamp(alpha, 0, FRACUNIT); - self->RenderStyle = ERenderStyle(mode); -} - -//=========================================================================== -// -// A_FadeIn -// -// Fades the actor in -// -//=========================================================================== - -enum FadeFlags -{ - FTF_REMOVE = 1 << 0, - FTF_CLAMP = 1 << 1, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_INT(flags, 1); - - if (reduce == 0) - { - reduce = FRACUNIT / 10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha += reduce; - - if (self->alpha >= (FRACUNIT * 1)) - { - if (flags & FTF_CLAMP) - self->alpha = (FRACUNIT * 1); - if (flags & FTF_REMOVE) - self->Destroy(); - } -} - -//=========================================================================== -// -// A_FadeOut -// -// fades the actor out and destroys it when done -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_INT(flags, 1); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha -= reduce; - if (self->alpha <= 0) - { - if (flags & FTF_CLAMP) - self->alpha = 0; - if (flags & FTF_REMOVE) - self->Destroy(); - } -} - -//=========================================================================== -// -// A_FadeTo -// -// fades the actor to a specified transparency by a specified amount and -// destroys it if so desired -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_FIXED(target, 0); - ACTION_PARAM_FIXED(amount, 1); - ACTION_PARAM_INT(flags, 2); - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - - if (self->alpha > target) - { - self->alpha -= amount; - - if (self->alpha < target) - { - self->alpha = target; - } - } - else if (self->alpha < target) - { - self->alpha += amount; - - if (self->alpha > target) - { - self->alpha = target; - } - } - if (flags & FTF_CLAMP) - { - if (self->alpha > (FRACUNIT * 1)) - self->alpha = (FRACUNIT * 1); - else if (self->alpha < 0) - self->alpha = 0; - } - if (self->alpha == target && (flags & FTF_REMOVE)) - { - self->Destroy(); - } -} - -//=========================================================================== -// -// A_Scale(float scalex, optional float scaley) -// -// Scales the actor's graphics. If scaley is 0, use scalex. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_FIXED(scalex, 0); - ACTION_PARAM_FIXED(scaley, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - ref->scaleX = scalex; - ref->scaleY = scaley ? scaley : scalex; -} - -//=========================================================================== -// -// A_SetMass(int mass) -// -// Sets the actor's mass. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(mass, 0); - - self->Mass = mass; -} - -//=========================================================================== -// -// A_SpawnDebris -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) -{ - int i; - AActor * mo; - - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(debris, 0); - ACTION_PARAM_BOOL(transfer_translation, 1); - ACTION_PARAM_FIXED(mult_h, 2); - ACTION_PARAM_FIXED(mult_v, 3); - - if (debris == NULL) return; - - // only positive values make sense here - if (mult_v<=0) mult_v=FRACUNIT; - if (mult_h<=0) mult_h=FRACUNIT; - - for (i = 0; i < GetDefaultByType(debris)->health; i++) - { - mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), - self->y + ((pr_spawndebris()-128)<<12), - self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); - if (mo) - { - if (transfer_translation) - { - mo->Translation = self->Translation; - } - if (i < mo->GetClass()->ActorInfo->NumOwnedStates) - { - mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); - } - mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); - mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - } - } -} - - -//=========================================================================== -// -// A_CheckSight -// jumps if no player can see this actor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - // Always check sight from each player. - if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) - { - return; - } - // If a player is viewing from a non-player, then check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) - { - return; - } - } - } - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckSightOrRange -// Jumps if this actor is out of range of all players *and* out of sight. -// Useful for maps with many multi-actor special effects. -// -//=========================================================================== -static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool twodi) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height) - { - dz = self->z + self->height - eyez; - } - else if (eyez < self->z) - { - dz = self->z - eyez; - } - else - { - dz = 0; - } - double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); - if (distance <= range){ - // Within range - return true; - } - - // Now check LOS. - if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) - { // Visible - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) -{ - ACTION_PARAM_START(3); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(twodi, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckSightOrRange(self, players[i].mo, range, twodi)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckSightOrRange(self, players[i].camera, range, twodi)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckRange -// Jumps if this actor is out of range of all players. -// -//=========================================================================== -static bool DoCheckRange(AActor *self, AActor *camera, double range, bool twodi) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height){ - dz = self->z + self->height - eyez; - } - else if (eyez < self->z){ - dz = self->z - eyez; - } - else{ - dz = 0; - } - double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); - if (distance <= range){ - // Within range - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) -{ - ACTION_PARAM_START(3); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(twodi, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckRange(self, players[i].mo, range, twodi)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckRange(self, players[i].camera, range, twodi)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - - -//=========================================================================== -// -// Inventory drop -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(drop, 0); - - if (drop) - { - AInventory * inv = self->FindInventory(drop); - if (inv) - { - self->DropInventory(inv); - } - } -} - - -//=========================================================================== -// -// A_SetBlend -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_COLOR(color, 0); - ACTION_PARAM_FLOAT(alpha, 1); - ACTION_PARAM_INT(tics, 2); - ACTION_PARAM_COLOR(color2, 3); - - if (color == MAKEARGB(255,255,255,255)) color=0; - if (color2 == MAKEARGB(255,255,255,255)) color2=0; - if (!color2.a) - color2 = color; - - new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, - color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, - (float)tics/TICRATE, self); -} - - -//=========================================================================== -// -// A_JumpIf -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_BOOL(expression, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (expression) ACTION_JUMP(jump); - -} - -//=========================================================================== -// -// A_CountdownArg -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(cnt, 0); - ACTION_PARAM_STATE(state, 1); - - if (cnt<0 || cnt>=5) return; - if (!self->args[cnt]--) - { - if (self->flags&MF_MISSILE) - { - P_ExplodeMissile(self, NULL, NULL); - } - else if (self->flags&MF_SHOOTABLE) - { - P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); - } - else - { - // can't use "Death" as default parameter with current DECORATE parser. - if (state == NULL) state = self->FindState(NAME_Death); - self->SetState(state); - } - } - -} - -//============================================================================ -// -// A_Burst -// -//============================================================================ - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(chunk, 0); - - int i, numChunks; - AActor * mo; - - if (chunk == NULL) return; - - self->velx = self->vely = self->velz = 0; - self->height = self->GetDefault()->height; - - // [RH] In Hexen, this creates a random number of shards (range [24,56]) - // with no relation to the size of the self shattering. I think it should - // base the number of shards on the size of the dead thing, so bigger - // things break up into more shards than smaller things. - // An self with radius 20 and height 64 creates ~40 chunks. - numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); - i = (pr_burst.Random2()) % (numChunks/4); - for (i = MAX (24, numChunks + i); i >= 0; i--) - { - mo = Spawn(chunk, - self->x + (((pr_burst()-128)*self->radius)>>7), - self->y + (((pr_burst()-128)*self->radius)>>7), - self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); - - if (mo) - { - mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; - mo->velx = pr_burst.Random2 () << (FRACBITS-7); - mo->vely = pr_burst.Random2 () << (FRACBITS-7); - mo->RenderStyle = self->RenderStyle; - mo->alpha = self->alpha; - mo->CopyFriendliness(self, true); - } - } - - // [RH] Do some stuff to make this more useful outside Hexen - if (self->flags4 & MF4_BOSSDEATH) - { - CALL_ACTION(A_BossDeath, self); - } - A_Unblock(self, true); - - self->Destroy (); -} - -//=========================================================================== -// -// A_CheckFloor -// [GRB] Jumps if actor is standing on floor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->z <= self->floorz) - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_CheckCeiling -// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling -// - -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); - if (self->z+self->height >= self->ceilingz) // Height needs to be counted - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_Stop -// resets all velocity of the actor to 0 -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_Stop) -{ - self->velx = self->vely = self->velz = 0; - if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -static void CheckStopped(AActor *self) -{ - if (self->player != NULL && - self->player->mo == self && - !(self->player->cheats & CF_PREDICTING) && - !(self->velx | self->vely | self->velz)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -//=========================================================================== -// -// A_Respawn -// -//=========================================================================== - -extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); - -enum RS_Flags -{ - RSF_FOG=1, - RSF_KEEPTARGET=2, - RSF_TELEFRAG=4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(flags, 0); - bool oktorespawn = false; - fixed_t oldx = self->x; - fixed_t oldy = self->y; - fixed_t oldz = self->z; - self->flags |= MF_SOLID; - self->height = self->GetDefault()->height; - CALL_ACTION(A_RestoreSpecialPosition, self); - - if (flags & RSF_TELEFRAG) - { - // [KS] DIE DIE DIE DIE erm *ahem* =) - oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); - if (oktorespawn) - { // Need to do this over again, since P_TeleportMove() will redo - // it with the proper point-on-side calculation. - self->UnlinkFromWorld(); - self->LinkToWorld(true); - sector_t *sec = self->Sector; - self->dropoffz = - self->floorz = sec->floorplane.ZatPoint(self->x, self->y); - self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); - } - } - else - { - oktorespawn = P_CheckPosition(self, self->x, self->y, true); - } - - if (oktorespawn) - { - AActor *defs = self->GetDefault(); - self->health = defs->health; - - // [KS] Don't keep target, because it could be self if the monster committed suicide - // ...Actually it's better off an option, so you have better control over monster behavior. - if (!(flags & RSF_KEEPTARGET)) - { - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; - } - else - { - // Don't attack yourself (Re: "Marine targets itself after suicide") - if (self->target == self) self->target = NULL; - if (self->lastenemy == self) self->lastenemy = NULL; - } - - self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); - self->flags2 = defs->flags2; - self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); - self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); - self->flags5 = defs->flags5; - self->flags6 = defs->flags6; - self->flags7 = defs->flags7; - self->SetState (self->SpawnState); - self->renderflags &= ~RF_INVISIBLE; - - if (flags & RSF_FOG) - { - P_SpawnTeleportFog(self, oldx, oldy, oldz, true); - P_SpawnTeleportFog(self, self->x, self->y, self->z, false); - } - if (self->CountsAsKill()) - { - level.total_monsters++; - } - } - else - { - self->flags &= ~MF_SOLID; - } -} - - -//========================================================================== -// -// A_PlayerSkinCheck -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->player != NULL && - skins[self->player->userinfo.GetSkin()].othergame) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetGravity -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(val, 0); - - self->gravity = clamp (val, 0, FRACUNIT*10); -} - - -// [KS] *** Start of my modifications *** - -//=========================================================================== -// -// A_ClearTarget -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) -{ - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; -} - -//========================================================================== -// -// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, -// fixed range = 0, angle angle = 0, angle pitch = 0, -// fixed offsetheight = 32, fixed offsetwidth = 0, -// int ptr_target = AAPTR_DEFAULT (target) ) -// -//========================================================================== - -enum CLOF_flags -{ - CLOFF_NOAIM_VERT = 0x00000001, - CLOFF_NOAIM_HORZ = 0x00000002, - - CLOFF_JUMPENEMY = 0x00000004, - CLOFF_JUMPFRIEND = 0x00000008, - CLOFF_JUMPOBJECT = 0x00000010, - CLOFF_JUMPNONHOSTILE = 0x00000020, - - CLOFF_SKIPENEMY = 0x00000040, - CLOFF_SKIPFRIEND = 0x00000080, - CLOFF_SKIPOBJECT = 0x00000100, - CLOFF_SKIPNONHOSTILE = 0x00000200, - - CLOFF_MUSTBESHOOTABLE = 0x00000400, - - CLOFF_SKIPTARGET = 0x00000800, - CLOFF_ALLOWNULL = 0x00001000, - CLOFF_CHECKPARTIAL = 0x00002000, - - CLOFF_MUSTBEGHOST = 0x00004000, - CLOFF_IGNOREGHOST = 0x00008000, - - CLOFF_MUSTBESOLID = 0x00010000, - CLOFF_BEYONDTARGET = 0x00020000, - - CLOFF_FROMBASE = 0x00040000, - CLOFF_MUL_HEIGHT = 0x00080000, - CLOFF_MUL_WIDTH = 0x00100000, - - CLOFF_JUMP_ON_MISS = 0x00200000, - CLOFF_AIM_VERT_NOOFFSET = 0x00400000, - - CLOFF_SETTARGET = 0x00800000, - CLOFF_SETMASTER = 0x01000000, - CLOFF_SETTRACER = 0x02000000, -}; - -struct LOFData -{ - AActor *Self; - AActor *Target; - int Flags; - bool BadActor; -}; - -ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) -{ - LOFData *data = (LOFData *)userdata; - int flags = data->Flags; - - if (trace.HitType != TRACE_HitActor) - { - return TRACE_Stop; - } - if (trace.Actor == data->Target) - { - if (flags & CLOFF_SKIPTARGET) - { - if (flags & CLOFF_BEYONDTARGET) - { - return TRACE_Skip; - } - return TRACE_Abort; - } - return TRACE_Stop; - } - if (flags & CLOFF_MUSTBESHOOTABLE) - { // all shootability checks go here - if (!(trace.Actor->flags & MF_SHOOTABLE)) - { - return TRACE_Skip; - } - if (trace.Actor->flags2 & MF2_NONSHOOTABLE) - { - return TRACE_Skip; - } - } - if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) - { - return TRACE_Skip; - } - if (flags & CLOFF_MUSTBEGHOST) - { - if (!(trace.Actor->flags3 & MF3_GHOST)) - { - return TRACE_Skip; - } - } - else if (flags & CLOFF_IGNOREGHOST) - { - if (trace.Actor->flags3 & MF3_GHOST) - { - return TRACE_Skip; - } - } - if ( - ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Stop; - } - if ( - ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Skip; - } - data->BadActor = true; - return TRACE_Abort; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) -{ - // Check line of fire - - /* - Not accounted for / I don't know how it works: FLOORCLIP - */ - - AActor *target; - fixed_t - x1, y1, z1, - vx, vy, vz; - - ACTION_PARAM_START(9); - - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_FIXED(range, 2); - ACTION_PARAM_FIXED(minrange, 3); - { - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_ANGLE(pitch, 5); - ACTION_PARAM_FIXED(offsetheight, 6); - ACTION_PARAM_FIXED(offsetwidth, 7); - ACTION_PARAM_INT(ptr_target, 8); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default - - if (flags & CLOFF_MUL_HEIGHT) - { - if (self->player != NULL) - { - // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player - offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); - } - else - { - offsetheight = FixedMul(offsetheight, self->height); - } - } - if (flags & CLOFF_MUL_WIDTH) - { - offsetwidth = FixedMul(self->radius, offsetwidth); - } - - x1 = self->x; - y1 = self->y; - z1 = self->z + offsetheight - self->floorclip; - - if (!(flags & CLOFF_FROMBASE)) - { // default to hitscan origin - - // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player - z1 += (self->height >> 1); - if (self->player != NULL) - { - z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); - } - else - { - z1 += 8*FRACUNIT; - } - } - - if (target) - { - FVector2 xyvec(target->x - x1, target->y - y1); - fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); - - if (range && !(flags & CLOFF_CHECKPARTIAL)) - { - if (distance > range) return; - } - - { - angle_t ang; - - if (flags & CLOFF_NOAIM_HORZ) - { - ang = self->angle; - } - else ang = R_PointToAngle2 (x1, y1, target->x, target->y); - - angle += ang; - - ang >>= ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - - if (flags & CLOFF_NOAIM_VERT) - { - pitch += self->pitch; - } - else if (flags & CLOFF_AIM_VERT_NOOFFSET) - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); - } - else - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); - } - } - else if (flags & CLOFF_ALLOWNULL) - { - angle += self->angle; - pitch += self->pitch; - - angle_t ang = self->angle >> ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - else return; - - angle >>= ANGLETOFINESHIFT; - pitch = (0-pitch)>>ANGLETOFINESHIFT; - - vx = FixedMul (finecosine[pitch], finecosine[angle]); - vy = FixedMul (finecosine[pitch], finesine[angle]); - vz = -finesine[pitch]; - } - - /* Variable set: - - jump, flags, target - x1,y1,z1 (trace point of origin) - vx,vy,vz (trace unit vector) - range - */ - - sector_t *sec = P_PointInSector(x1, y1); - - if (range == 0) - { - range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; - } - - FTraceResults trace; - LOFData lof_data; - - lof_data.Self = self; - lof_data.Target = target; - lof_data.Flags = flags; - lof_data.BadActor = false; - - Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, - CheckLOFTraceFunc, &lof_data); - - if (trace.HitType == TRACE_HitActor || - ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) - { - if (minrange > 0 && trace.Distance < minrange) - { - return; - } - if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor)) - { - if (flags & (CLOFF_SETTARGET)) self->target = trace.Actor; - if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor; - if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor; - } - - ACTION_JUMP(jump); - } -} - -//========================================================================== -// -// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, -// optional fixed dist_max, optional fixed dist_close) -// -// Jumps if the actor can see its target, or if the player has a linetarget. -// ProjectileTarget affects how projectiles are treated. If set, it will use -// the target of the projectile for seekers, and ignore the target for -// normal projectiles. If not set, it will use the missile's owner instead -// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max -// sets the maximum distance that actor can see, 0 means forever. dist_close -// uses special behavior if certain flags are set, 0 means no checks. -// -//========================================================================== - -enum JLOS_flags -{ - JLOSF_PROJECTILE = 1, - JLOSF_NOSIGHT = 1 << 1, - JLOSF_CLOSENOFOV = 1 << 2, - JLOSF_CLOSENOSIGHT = 1 << 3, - JLOSF_CLOSENOJUMP = 1 << 4, - JLOSF_DEADNOJUMP = 1 << 5, - JLOSF_CHECKMASTER = 1 << 6, - JLOSF_TARGETLOS = 1 << 7, - JLOSF_FLIPFOV = 1 << 8, - JLOSF_ALLYNOJUMP = 1 << 9, - JLOSF_COMBATANTONLY = 1 << 10, - JLOSF_NOAUTOAIM = 1 << 11, - JLOSF_CHECKTRACER = 1 << 12, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target, *viewport; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - bool doCheckSight; - - if (!self->player) - { - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) - { - if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - doCheckSight = !(flags & JLOSF_NOSIGHT); - } - else - { - // Does the player aim at something that can be shot? - P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); - - if (!target) return; - - switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) - { - case JLOSF_TARGETLOS|JLOSF_FLIPFOV: - // target makes sight check, player makes fov check; player has verified fov - fov = 0; - // fall-through - case JLOSF_TARGETLOS: - doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov - break; - default: - // player has verified sight and fov - fov = 0; - // fall-through - case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov - doCheckSight = false; - break; - } - } - - // [FDARI] If target is not a combatant, don't jump - if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; - - // [FDARI] If actors share team, don't jump - if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } - else { viewport = self; } - - if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) - return; - - if (flags & JLOSF_FLIPFOV) - { - if (viewport == self) { viewport = target; target = self; } - else { target = viewport; viewport = self; } - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (viewport->x, - viewport->y, - target->x, - target->y) - - viewport->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - - } - - ACTION_JUMP(jump); -} - - -//========================================================================== -// -// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags -// optional fixed dist_max, optional fixed dist_close) -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) - { - if (self->flags2 & MF2_SEEKERMISSILE) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - bool doCheckSight = !(flags & JLOSF_NOSIGHT); - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (target->x, - target->y, - self->x, - self->y) - - target->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - } - - if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) - return; - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// Modified code pointer from Skulltag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - ACTION_PARAM_START(2); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(dontincrement, 2) - - if (count <= 0) return; - - AWeapon *weapon = self->player->ReadyWeapon; - - int ReloadCounter = weapon->ReloadCounter; - if(!dontincrement || ReloadCounter != 0) - ReloadCounter = (weapon->ReloadCounter+1) % count; - else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. - ReloadCounter = 1; - - // If we have not made our last shot... - if (ReloadCounter != 0) - { - // Go back to the refire frames, instead of continuing on to the reload frames. - ACTION_JUMP(jump); - } - else - { - // We need to reload. However, don't reload if we're out of ammo. - weapon->CheckAmmo( false, false ); - } - - if(!dontincrement) - weapon->ReloadCounter = ReloadCounter; -} - -//=========================================================================== -// -// Resets the counter for the above function -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - AWeapon *weapon = self->player->ReadyWeapon; - weapon->ReloadCounter = 0; -} - -//=========================================================================== -// -// A_ChangeFlag -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_BOOL(expression, 1); - - const char *dot = strchr (flagname, '.'); - FFlagDef *fd; - const PClass *cls = self->GetClass(); - - if (dot != NULL) - { - FString part1(flagname, dot-flagname); - fd = FindFlag (cls, part1, dot+1); - } - else - { - fd = FindFlag (cls, flagname, NULL); - } - - if (fd != NULL) - { - bool kill_before, kill_after; - INTBOOL item_before, item_after; - INTBOOL secret_before, secret_after; - - kill_before = self->CountsAsKill(); - item_before = self->flags & MF_COUNTITEM; - secret_before = self->flags5 & MF5_COUNTSECRET; - - if (fd->structoffset == -1) - { - HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); - } - else - { - DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); - - // If these 2 flags get changed we need to update the blockmap and sector links. - bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); - - if (linkchange) self->UnlinkFromWorld(); - ModActorFlag(self, fd, expression); - if (linkchange) self->LinkToWorld(); - } - kill_after = self->CountsAsKill(); - item_after = self->flags & MF_COUNTITEM; - secret_after = self->flags5 & MF5_COUNTSECRET; - // Was this monster previously worth a kill but no longer is? - // Or vice versa? - if (kill_before != kill_after) - { - if (kill_after) - { // It counts as a kill now. - level.total_monsters++; - } - else - { // It no longer counts as a kill. - level.total_monsters--; - } - } - // same for items - if (item_before != item_after) - { - if (item_after) - { // It counts as an item now. - level.total_items++; - } - else - { // It no longer counts as an item - level.total_items--; - } - } - // and secretd - if (secret_before != secret_after) - { - if (secret_after) - { // It counts as an secret now. - level.total_secrets++; - } - else - { // It no longer counts as an secret - level.total_secrets--; - } - } - } - else - { - Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); - } -} - -//=========================================================================== -// -// A_CheckFlag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_STATE(jumpto, 1); - ACTION_PARAM_INT(checkpointer, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - AActor *owner; - - COPY_AAPTR_NOT_NULL(self, owner, checkpointer); - - if (CheckActorFlag(owner, flagname)) - { - ACTION_JUMP(jumpto); - } -} - -//=========================================================================== -// -// A_RaiseMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - - if (self->master != NULL) - { - if (copy) - P_Thing_Raise(self->master, self); - else - P_Thing_Raise(self->master, NULL); - } -} - -//=========================================================================== -// -// A_RaiseChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - TThinkerIterator it; - AActor *mo; - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self) - { - if (copy) - P_Thing_Raise(mo, self); - else - P_Thing_Raise(mo, NULL); - } - } -} - -//=========================================================================== -// -// A_RaiseSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self) - { - if (copy) - P_Thing_Raise(mo, self); - else - P_Thing_Raise(mo, NULL); - } - } - } -} - -//=========================================================================== -// -// [TP] A_FaceConsolePlayer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { - ACTION_PARAM_START (1); - ACTION_PARAM_ANGLE (MaxTurnAngle, 0); - // NOTE: It does nothing for zdoom. -} - -//=========================================================================== -// -// A_MonsterRefire -// -// Keep firing unless target got out of sight -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(prob, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - A_FaceTarget (self); - - if (pr_monsterrefire() < prob) - return; - - if (!self->target - || P_HitFriend (self) - || self->target->health <= 0 - || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetAngle -// -// Set actor's angle (in degrees). -// -//=========================================================================== -enum -{ - SPF_FORCECLAMP = 1, // players always clamp - SPF_INTERPOLATE = 2, -}; - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_ANGLE(angle, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_SetPitch -// -// Set actor's pitch (in degrees). -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_ANGLE(pitch, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - if (ref->player != NULL || (flags & SPF_FORCECLAMP)) - { // clamp the pitch we set - int min, max; - - if (ref->player != NULL) - { - min = ref->player->MinPitch; - max = ref->player->MaxPitch; - } - else - { - min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); - max = ANGLE_90 - (1 << ANGLETOFINESHIFT); - } - pitch = clamp(pitch, min, max); - } - ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); -} - +/* +** thingdef.cpp +** +** Code pointers for Actor definitions +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Christoph Oelckers +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by 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 SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gi.h" +#include "g_level.h" +#include "actor.h" +#include "info.h" +#include "sc_man.h" +#include "tarray.h" +#include "w_wad.h" +#include "templates.h" +#include "r_defs.h" +#include "a_pickups.h" +#include "s_sound.h" +#include "cmdlib.h" +#include "p_lnspec.h" +#include "p_enemy.h" +#include "a_action.h" +#include "decallib.h" +#include "m_random.h" +#include "i_system.h" +#include "p_local.h" +#include "c_console.h" +#include "doomerrors.h" +#include "a_sharedglobal.h" +#include "thingdef/thingdef.h" +#include "v_video.h" +#include "v_font.h" +#include "doomstat.h" +#include "v_palette.h" +#include "g_shared/a_specialspot.h" +#include "actorptrselect.h" +#include "m_bbox.h" +#include "r_data/r_translate.h" +#include "p_trace.h" +#include "gstrings.h" + + +static FRandom pr_camissile ("CustomActorfire"); +static FRandom pr_camelee ("CustomMelee"); +static FRandom pr_cabullet ("CustomBullet"); +static FRandom pr_cajump ("CustomJump"); +static FRandom pr_cwbullet ("CustomWpBullet"); +static FRandom pr_cwjump ("CustomWpJump"); +static FRandom pr_cwpunch ("CustomWpPunch"); +static FRandom pr_grenade ("ThrowGrenade"); +static FRandom pr_crailgun ("CustomRailgun"); +static FRandom pr_spawndebris ("SpawnDebris"); +static FRandom pr_spawnitemex ("SpawnItemEx"); +static FRandom pr_burst ("Burst"); +static FRandom pr_monsterrefire ("MonsterRefire"); +static FRandom pr_teleport("A_Teleport"); + +//========================================================================== +// +// ACustomInventory :: CallStateChain +// +// Executes the code pointers in a chain of states +// until there is no next state +// +//========================================================================== + +bool ACustomInventory::CallStateChain (AActor *actor, FState * State) +{ + StateCallData StateCall; + bool result = false; + int counter = 0; + + while (State != NULL) + { + // Assume success. The code pointer will set this to false if necessary + StateCall.State = State; + StateCall.Result = true; + if (State->CallAction(actor, this, &StateCall)) + { + // collect all the results. Even one successful call signifies overall success. + result |= StateCall.Result; + } + + + // Since there are no delays it is a good idea to check for infinite loops here! + counter++; + if (counter >= 10000) break; + + if (StateCall.State == State) + { + // Abort immediately if the state jumps to itself! + if (State == State->GetNextState()) + { + return false; + } + + // If both variables are still the same there was no jump + // so we must advance to the next state. + State = State->GetNextState(); + } + else + { + State = StateCall.State; + } + } + return result; +} + +//========================================================================== +// +// A_RearrangePointers +// +// Allow an actor to change its relationship to other actors by +// copying pointers freely between TARGET MASTER and TRACER. +// Can also assign null value, but does not duplicate A_ClearTarget. +// +//========================================================================== + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(ptr_target, 0); + ACTION_PARAM_INT(ptr_master, 1); + ACTION_PARAM_INT(ptr_tracer, 2); + ACTION_PARAM_INT(flags, 3); + + // Rearrange pointers internally + + // Fetch all values before modification, so that all fields can get original values + AActor + *gettarget = self->target, + *getmaster = self->master, + *gettracer = self->tracer; + + switch (ptr_target) // pick the new target + { + case AAPTR_MASTER: + self->target = getmaster; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_TRACER: + self->target = gettracer; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_NULL: + self->target = NULL; + // THIS IS NOT "A_ClearTarget", so no other targeting info is removed + break; + } + + // presently permitting non-monsters to set master + switch (ptr_master) // pick the new master + { + case AAPTR_TARGET: + self->master = gettarget; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_TRACER: + self->master = gettracer; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_NULL: + self->master = NULL; + break; + } + + switch (ptr_tracer) // pick the new tracer + { + case AAPTR_TARGET: + self->tracer = gettarget; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_MASTER: + self->tracer = getmaster; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_NULL: + self->tracer = NULL; + break; + } +} + +//========================================================================== +// +// A_TransferPointer +// +// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), +// or from this actor's MASTER, TARGET or TRACER. +// +// You can copy any one of that actor's pointers +// +// Assign the copied pointer to any one pointer in SELF, +// MASTER, TARGET or TRACER. +// +// Any attempt to make an actor point to itself will replace the pointer +// with a null value. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ptr_source, 0); + ACTION_PARAM_INT(ptr_recepient, 1); + ACTION_PARAM_INT(ptr_sourcefield, 2); + ACTION_PARAM_INT(ptr_recepientfield, 3); + ACTION_PARAM_INT(flags, 4); + + AActor *source, *recepient; + + // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) + + source = COPY_AAPTR(self, ptr_source); + COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value + + // convert source from dataprovider to data + + source = COPY_AAPTR(source, ptr_sourcefield); + + if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL + + if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from + + ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); +} + +//========================================================================== +// +// A_CopyFriendliness +// +// Join forces with one of the actors you are pointing to (MASTER by default) +// +// Normal CopyFriendliness reassigns health. This function will not. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr_source, 0); + + if (self->player) return; + + AActor *source; + COPY_AAPTR_NOT_NULL(self, source, ptr_source); + self->CopyFriendliness(source, false, false); // No change in current target or health +} + +//========================================================================== +// +// Simple flag changers +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) +{ + self->flags |= MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) +{ + self->flags &= ~MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) +{ + self->flags |= MF_FLOAT; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) +{ + self->flags &= ~(MF_FLOAT|MF_INFLOAT); +} + +//========================================================================== +// +// Customizable attack functions which use actor parameters. +// +//========================================================================== +static void DoAttack (AActor *self, bool domelee, bool domissile, + int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) +{ + if (self->target == NULL) return; + + A_FaceTarget (self); + if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) + { + int damage = pr_camelee.HitDice(MeleeDamage); + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); + P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (domissile && MissileType != NULL) + { + // This seemingly senseless code is needed for proper aiming. + self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); + self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); +} + +DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) +{ + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(MeleeDamage, 0); + ACTION_PARAM_SOUND(MeleeSound, 1); + ACTION_PARAM_CLASS(MissileType, 2); + ACTION_PARAM_FIXED(MissileHeight, 3); + + if (MissileType == NULL) return; + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +//========================================================================== +// +// Custom sound functions. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_INT(channel, 1); + ACTION_PARAM_FLOAT(volume, 2); + ACTION_PARAM_BOOL(looping, 3); + ACTION_PARAM_FLOAT(attenuation, 4); + + if (!looping) + { + S_Sound (self, channel, soundid, volume, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, channel&7, soundid)) + { + S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(slot, 0); + + S_StopSound(self, slot); +} + +//========================================================================== +// +// These come from a time when DECORATE constants did not exist yet and +// the sound interface was less flexible. As a result the parameters are +// not optimal and these functions have been deprecated in favor of extending +// A_PlaySound and A_StopSound. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_SOUND(soundid, 0); + + S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_NAME(channel, 1); + ACTION_PARAM_BOOL(looping, 2); + ACTION_PARAM_INT(attenuation_raw, 3); + + float attenuation; + switch (attenuation_raw) + { + case -1: attenuation = ATTN_STATIC; break; // drop off rapidly + default: + case 0: attenuation = ATTN_NORM; break; // normal + case 1: + case 2: attenuation = ATTN_NONE; break; // full volume + } + + if (channel < NAME_Auto || channel > NAME_SoundSlot7) + { + channel = NAME_Auto; + } + + if (!looping) + { + S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) + { + S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(channel, 0); + + if (channel > NAME_Auto && channel <= NAME_SoundSlot7) + { + S_StopSound (self, int(channel) - NAME_Auto); + } +} + +//========================================================================== +// +// Generic seeker missile function +// +//========================================================================== +static FRandom pr_seekermissile ("SeekerMissile"); +enum +{ + SMF_LOOK = 1, + SMF_PRECISE = 2, + SMF_CURSPEED = 4, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ang1, 0); + ACTION_PARAM_INT(ang2, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(chance, 3); + ACTION_PARAM_INT(distance, 4); + + if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); + } + if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) + { + if (flags & SMF_LOOK) + { // This monster is no longer seekable, so let us look for another one next time. + self->tracer = NULL; + } + } +} + +//========================================================================== +// +// Hitscan attack with a customizable amount of bullets (specified in damage) +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) +{ + int i; + int bangle; + int slope; + + if (!self->target) return; + + A_FaceTarget (self); + bangle = self->angle; + + slope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i = self->GetMissileDamage (0, 1); i > 0; --i) + { + int angle = bangle + (pr_cabullet.Random2() << 20); + int damage = ((pr_cabullet()%5)+1)*3; + P_LineAttack(self, angle, MISSILERANGE, slope, damage, + NAME_Hitscan, NAME_BulletPuff); + } +} + + +//========================================================================== +// +// Do the state jump +// +//========================================================================== +static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) +{ + if (jumpto == NULL) return; + + if (statecall != NULL) + { + statecall->State = jumpto; + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) + { + P_SetPsprite(self->player, ps_weapon, jumpto); + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) + { + P_SetPsprite(self->player, ps_flash, jumpto); + } + else if (CallingState == self->state) + { + self->SetState (jumpto); + } + else + { + // something went very wrong. This should never happen. + assert(false); + } +} + +// This is just to avoid having to directly reference the internally defined +// CallingState and statecall parameters in the code below. +#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_INT(maxchance, 1); + + if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) + { + int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); + ACTION_PARAM_STATE(jumpto, jumps); + ACTION_JUMP(jumpto); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_INT(ptr_selector, 2); + + AActor *measured; + + measured = COPY_AAPTR(self, ptr_selector); + + if (measured && measured->health < health) + { + ACTION_JUMP(jump); + } + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (!self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(dist, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + // No target - no jump + if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && + ( (self->z > target->z && self->z - (target->z + target->height) < dist) || + (self->z <=target->z && target->z - (self->z + self->height) < dist) + ) + ) + { + ACTION_JUMP(jump); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) +{ + AActor *target; + + if (!self->player) + { + target = self->target; + } + else + { + // Does the player aim at something that can be shot? + P_BulletSlope(self, &target); + } + DoJumpIfCloser(target, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) +{ + DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) +{ + DoJumpIfCloser(self->master, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(Type, 0); + ACTION_PARAM_INT(ItemAmount, 1); + ACTION_PARAM_STATE(JumpOffset, 2); + ACTION_PARAM_INT(setowner, 3); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (!Type) return; + COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL + + AInventory *Item = owner->FindInventory(Type); + + if (Item) + { + if (ItemAmount > 0) + { + if (Item->Amount >= ItemAmount) + ACTION_JUMP(JumpOffset); + } + else if (Item->Amount >= Item->MaxAmount) + { + ACTION_JUMP(JumpOffset); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) +{ + DoJumpIfInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) +{ + DoJumpIfInventory(self->target, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(Type, 0); + ACTION_PARAM_STATE(JumpOffset, 1); + ACTION_PARAM_INT(amount, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); + + if (armor && armor->ArmorType == Type && armor->Amount >= amount) + ACTION_JUMP(JumpOffset); +} + +//========================================================================== +// +// Parameterized version of A_Explode +// +//========================================================================== + +enum +{ + XF_HURTSOURCE = 1, + XF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_INT(damage, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_BOOL(alert, 3); + ACTION_PARAM_INT(fulldmgdistance, 4); + ACTION_PARAM_INT(nails, 5); + ACTION_PARAM_INT(naildamage, 6); + ACTION_PARAM_CLASS(pufftype, 7); + + if (damage < 0) // get parameters from metadata + { + damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); + distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); + flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); + alert = false; + } + else + { + if (distance <= 0) distance = damage; + } + // NailBomb effect, from SMMU but not from its source code: instead it was implemented and + // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html + + if (nails) + { + angle_t ang; + for (int i = 0; i < nails; i++) + { + ang = i*(ANGLE_MAX/nails); + // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim + P_LineAttack (self, ang, MISSILERANGE, 0, + //P_AimLineAttack (self, ang, MISSILERANGE), + naildamage, NAME_Hitscan, pufftype); + } + } + + P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); + P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) + { + validcount++; + P_RecursiveSound (self->Sector, self->target, false, 0); + } +} + +//========================================================================== +// +// A_RadiusThrust +// +//========================================================================== + +enum +{ + RTF_AFFECTSOURCE = 1, + RTF_NOIMPACTDAMAGE = 2, + RTF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(force, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(fullthrustdistance, 3); + + bool sourcenothrust = false; + + if (force == 0) force = 128; + if (distance <= 0) distance = abs(force); + + // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. + if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) + { + sourcenothrust = true; + self->target->flags2 &= ~MF2_NODMGTHRUST; + } + + P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); + P_CheckSplash(self, distance << FRACBITS); + + if (sourcenothrust) + { + self->target->flags2 |= MF2_NODMGTHRUST; + } +} + +//========================================================================== +// +// Execute a line special / script +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + ACTION_PARAM_INT(arg5, 5); + + bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); + + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// The ultimate code pointer: Fully customizable missiles! +// +//========================================================================== +enum CM_Flags +{ + CMF_AIMMODE = 3, + CMF_TRACKOWNER = 4, + CMF_CHECKTARGETDEAD = 8, + + CMF_ABSOLUTEPITCH = 16, + CMF_OFFSETPITCH = 32, + CMF_SAVEPITCH = 64, + + CMF_ABSOLUTEANGLE = 128 +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(Spawnofs_XY, 2); + ACTION_PARAM_ANGLE(Angle, 3); + ACTION_PARAM_INT(flags, 4); + ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_INT(ptr, 6); + + AActor *ref = COPY_AAPTR(self, ptr); + + int aimmode = flags & CMF_AIMMODE; + + AActor * targ; + AActor * missile; + + if (ref != NULL || aimmode==2) + { + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = Spawnofs_XY * finecosine[ang]; + fixed_t y = Spawnofs_XY * finesine[ang]; + fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); + + switch (aimmode) + { + case 0: + default: + // same adjustment as above (in all 3 directions this time) - for better aiming! + self->x += x; + self->y += y; + self->z += z; + missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, ref, ti, false); + self->x -= x; + self->y -= y; + self->z -= z; + break; + + case 1: + missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, ref, ti, false); + break; + + case 2: + self->x += x; + self->y += y; + missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); + self->x -= x; + self->y -= y; + + flags |= CMF_ABSOLUTEPITCH; + + break; + } + + if (missile) + { + // Use the actual velocity instead of the missile's Speed property + // so that this can handle missiles with a high vertical velocity + // component properly. + + fixed_t missilespeed; + + if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) + { + if (CMF_OFFSETPITCH & flags) + { + FVector2 velocity (missile->velx, missile->vely); + pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); + } + ang = pitch >> ANGLETOFINESHIFT; + missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); + missile->velz = FixedMul(finesine[ang], missile->Speed); + } + else + { + FVector2 velocity (missile->velx, missile->vely); + missilespeed = (fixed_t)velocity.Length(); + } + + if (CMF_SAVEPITCH & flags) + { + missile->pitch = pitch; + // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter + // contains the unapplied parameter. In that case, it is set as pitch without + // otherwise affecting the spawned actor. + } + + missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; + + ang = missile->angle >> ANGLETOFINESHIFT; + missile->velx = FixedMul (missilespeed, finecosine[ang]); + missile->vely = FixedMul (missilespeed, finesine[ang]); + + // handle projectile shooting projectiles - track the + // links back to a real owner + if (self->isMissile(!!(flags & CMF_TRACKOWNER))) + { + AActor * owner=self ;//->target; + while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; + targ=owner; + missile->target=owner; + // automatic handling of seeker missiles + if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) + { + missile->tracer=self->tracer; + } + } + else if (missile->flags2&MF2_SEEKERMISSILE) + { + // automatic handling of seeker missiles + missile->tracer=self->target; + } + // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong + if (missile->flags4 & MF4_SPECTRAL) + { + if (missile->target != NULL) + { + missile->SetFriendPlayer(missile->target->player); + } + else + { + missile->FriendPlayer = 0; + } + } + P_CheckMissileSpawn(missile, self->radius); + } + } + } + else if (flags & CMF_CHECKTARGETDEAD) + { + // Target is dead and the attack shall be aborted. + if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); + } +} + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum CBA_Flags +{ + CBAF_AIMFACING = 1, + CBAF_NORANDOM = 2, + CBAF_EXPLICITANGLE = 4, + CBAF_NOPITCH = 8, + CBAF_NORANDOMPUFFZ = 16, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(pufftype, 4); + ACTION_PARAM_FIXED(Range, 5); + ACTION_PARAM_INT(Flags, 6); + ACTION_PARAM_INT(ptr, 7); + + AActor *ref = COPY_AAPTR(self, ptr); + + if(Range==0) Range=MISSILERANGE; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if (ref || (Flags & CBAF_AIMFACING)) + { + if (!(Flags & CBAF_AIMFACING)) + { + A_Face(self, ref); + } + bangle = self->angle; + + if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); + + if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i=0 ; itarget) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else + { + if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); + } +} + +//========================================================================== +// +// A fully customizable combo attack +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(damage, 2); + ACTION_PARAM_SOUND(MeleeSound, 3); + ACTION_PARAM_NAME(DamageType, 4); + ACTION_PARAM_BOOL(bleed, 5); + + if (!self->target) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (ti) + { + // This seemingly senseless code is needed for proper aiming. + self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); + self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (!ACTION_CALL_FROM_WEAPON()) return; + + if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) + { + ACTION_JUMP(jump); + } + +} + + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum FB_Flags +{ + FBF_USEAMMO = 1, + FBF_NORANDOM = 2, + FBF_EXPLICITANGLE = 4, + FBF_NOPITCH = 8, + FBF_NOFLASH = 16, + FBF_NORANDOMPUFFZ = 32, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumberOfBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(PuffType, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FIXED(Range, 6); + + if (!self->player) return; + + player_t * player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if ((Flags & FBF_USEAMMO) && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (Range == 0) Range = PLAYERMISSILERANGE; + + if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); + + if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); + bangle = self->angle; + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) + { + int damage = DamagePerBullet; + + if (!(Flags & FBF_NORANDOM)) + damage *= ((pr_cwbullet()%3)+1); + + P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); + } + else + { + if (NumberOfBullets == -1) NumberOfBullets = 1; + for (i=0 ; iplayer) return; + + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + AActor *linetarget; + + if (UseAmmo && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = SpawnOfs_XY * finecosine[ang]; + fixed_t y = SpawnOfs_XY * finesine[ang]; + fixed_t z = SpawnHeight; + fixed_t shootangle = self->angle; + + if (Flags & FPF_AIMATANGLE) shootangle += Angle; + + // Temporarily adjusts the pitch + fixed_t SavedPlayerPitch = self->pitch; + self->pitch -= pitch; + AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); + self->pitch = SavedPlayerPitch; + + // automatic handling of seeker missiles + if (misl) + { + if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; + if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; + if (!(Flags & FPF_AIMATANGLE)) + { + // This original implementation is to aim straight ahead and then offset + // the angle from the resulting direction. + FVector3 velocity(misl->velx, misl->vely, 0); + fixed_t missilespeed = (fixed_t)velocity.Length(); + misl->angle += Angle; + angle_t an = misl->angle >> ANGLETOFINESHIFT; + misl->velx = FixedMul (missilespeed, finecosine[an]); + misl->vely = FixedMul (missilespeed, finesine[an]); + } + } + } +} + + +//========================================================================== +// +// A_CustomPunch +// +// Berserk is not handled here. That can be done with A_CheckIfInventory +// +//========================================================================== + +enum +{ + CPF_USEAMMO = 1, + CPF_DAGGER = 2, + CPF_PULLIN = 4, + CPF_NORANDOMPUFFZ = 8, + CPF_NOTURN = 16, + CPF_STEALARMOR = 32, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_BOOL(norandom, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(PuffType, 3); + ACTION_PARAM_FIXED(Range, 4); + ACTION_PARAM_FIXED(LifeSteal, 5); + ACTION_PARAM_INT(lifestealmax, 6); + ACTION_PARAM_CLASS(armorbonustype, 7); + + if (!self->player) return; + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + + angle_t angle; + int pitch; + AActor * linetarget; + int actualdamage; + + if (!norandom) Damage *= (pr_cwpunch()%8+1); + + angle = self->angle + (pr_cwpunch.Random2() << 18); + if (Range == 0) Range = MELEERANGE; + pitch = P_AimLineAttack (self, angle, Range, &linetarget); + + // only use ammo when actually hitting something! + if ((flags & CPF_USEAMMO) && linetarget && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); + + P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); + + if (linetarget) + { + if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) + { + if (flags & CPF_STEALARMOR) + { + if (!armorbonustype) armorbonustype = PClass::FindClass("ArmorBonus"); + + if (armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))) + { + ABasicArmorBonus *armorbonus = static_cast(Spawn (armorbonustype, 0,0,0, NO_REPLACE)); + armorbonus->SaveAmount *= (actualdamage * LifeSteal) >> FRACBITS; + armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax; + armorbonus->flags |= MF_DROPPED; + armorbonus->ClearCounters(); + + if (!armorbonus->CallTryPickup (self)) + { + armorbonus->Destroy (); + } + } + } + + else + { + P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS, lifestealmax); + } + } + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + if (!(flags & CPF_NOTURN)) + { + // turn to face target + self->angle = R_PointToAngle2 (self->x, + self->y, + linetarget->x, + linetarget->y); + } + + if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; + if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); + } +} + + +//========================================================================== +// +// customizable railgun attack function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_BOOL(UseAmmo, 2); + ACTION_PARAM_COLOR(Color1, 3); + ACTION_PARAM_COLOR(Color2, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + if (!self->player) return; + + AWeapon * weapon=self->player->ReadyWeapon; + + // only use ammo when actually hitting something! + if (UseAmmo) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + angle_t angle; + angle_t slope; + + if (Flags & RAF_EXPLICITANGLE) + { + angle = Spread_XY; + slope = Spread_Z; + } + else + { + angle = pr_crailgun.Random2() * (Spread_XY / 255); + slope = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); +} + +//========================================================================== +// +// also for monsters +// +//========================================================================== +enum +{ + CRF_DONTAIM = 0, + CRF_AIMPARALLEL = 1, + CRF_AIMDIRECT = 2, + CRF_EXPLICITANGLE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_COLOR(Color1, 2); + ACTION_PARAM_COLOR(Color2, 3); + ACTION_PARAM_INT(Flags, 4); + ACTION_PARAM_INT(aim, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + AActor *linetarget; + + fixed_t saved_x = self->x; + fixed_t saved_y = self->y; + angle_t saved_angle = self->angle; + fixed_t saved_pitch = self->pitch; + + if (aim && self->target == NULL) + { + return; + } + // [RH] Andy Baker's stealth monsters + if (self->flags & MF_STEALTH) + { + self->visdir = 1; + } + + self->flags &= ~MF_AMBUSH; + + + if (aim) + { + self->angle = R_PointToAngle2 (self->x, + self->y, + self->target->x, + self->target->y); + } + self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); + if (linetarget == NULL && aim) + { + // We probably won't hit the target, but aim at it anyway so we don't look stupid. + FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + double zdiff = (self->target->z + (self->target->height>>1)) - + (self->z + (self->height>>1) - self->floorclip); + self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); + } + // Let the aim trail behind the player + if (aim) + { + saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + + if (aim == CRF_AIMDIRECT) + { + // Tricky: We must offset to the angle of the current position + // but then change the angle again to ensure proper aim. + self->x += Spawnofs_XY * finecosine[self->angle]; + self->y += Spawnofs_XY * finesine[self->angle]; + Spawnofs_XY = 0; + self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + } + + if (self->target->flags & MF_SHADOW) + { + angle_t rnd = pr_crailgun.Random2() << 21; + self->angle += rnd; + saved_angle = rnd; + } + } + + angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; + + angle_t angleoffset; + angle_t slopeoffset; + + if (Flags & CRF_EXPLICITANGLE) + { + angleoffset = Spread_XY; + slopeoffset = Spread_Z; + } + else + { + angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); + slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); + + self->x = saved_x; + self->y = saved_y; + self->angle = saved_angle; + self->pitch = saved_pitch; +} + +//=========================================================================== +// +// DoGiveInventory +// +//=========================================================================== + +static void DoGiveInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(2+use_aaptr); + ACTION_PARAM_CLASS(mi, 0); + ACTION_PARAM_INT(amount, 1); + + if (use_aaptr) + { + ACTION_PARAM_INT(setreceiver, 2); + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + } + + bool res=true; + + if (amount==0) amount=1; + if (mi) + { + AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); + if (!item) + { + ACTION_SET_RESULT(false); + return; + } + if (item->IsKindOf(RUNTIME_CLASS(AHealth))) + { + item->Amount *= amount; + } + else + { + item->Amount = amount; + } + item->flags |= MF_DROPPED; + item->ClearCounters(); + if (!item->CallTryPickup (receiver)) + { + item->Destroy (); + res = false; + } + else res = true; + } + else res = false; + ACTION_SET_RESULT(res); + +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) +{ + DoGiveInventory(self, true, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) +{ + DoGiveInventory(self->target, true, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) +{ + TThinkerIterator it; + AActor * mo; + + while ((mo = it.Next())) + { + if (mo->master == self) DoGiveInventory(mo, false, PUSH_PARAMINFO); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) +{ + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) DoGiveInventory(mo, false, PUSH_PARAMINFO); + } + } +} + +//=========================================================================== +// +// A_TakeInventory +// +//=========================================================================== + +enum +{ + TIF_NOTAKEINFINITE = 1, +}; + +void DoTakeInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(3+use_aaptr); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(flags, 2); + + if (!item) + { + ACTION_SET_RESULT(false); + return; + } + if (use_aaptr) + { + ACTION_PARAM_INT(setreceiver, 3); + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + } + + bool res = false; + + AInventory * inv = receiver->FindInventory(item); + + if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) + { + if (inv->Amount > 0) + { + res = true; + } + // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set + // and infinite ammo is on + if (flags & TIF_NOTAKEINFINITE && + ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && + inv->IsKindOf(RUNTIME_CLASS(AAmmo))) + { + // Nothing to do here, except maybe res = false;? Would it make sense? + } + else if (!amount || amount>=inv->Amount) + { + if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; + else inv->Destroy(); + } + else inv->Amount-=amount; + } + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) +{ + DoTakeInventory(self, true, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) +{ + DoTakeInventory(self->target, true, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) +{ + TThinkerIterator it; + AActor * mo; + + while ((mo = it.Next())) + { + if (mo->master == self) DoTakeInventory(mo, false, PUSH_PARAMINFO); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) +{ + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) DoTakeInventory(mo, false, PUSH_PARAMINFO); + } + } +} + +//=========================================================================== +// +// Common code for A_SpawnItem and A_SpawnItemEx +// +//=========================================================================== + +enum SIX_Flags +{ + SIXF_TRANSFERTRANSLATION = 1 << 0, + SIXF_ABSOLUTEPOSITION = 1 << 1, + SIXF_ABSOLUTEANGLE = 1 << 2, + SIXF_ABSOLUTEVELOCITY = 1 << 3, + SIXF_SETMASTER = 1 << 4, + SIXF_NOCHECKPOSITION = 1 << 5, + SIXF_TELEFRAG = 1 << 6, + SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum + SIXF_TRANSFERAMBUSHFLAG = 1 << 8, + SIXF_TRANSFERPITCH = 1 << 9, + SIXF_TRANSFERPOINTERS = 1 << 10, + SIXF_USEBLOODCOLOR = 1 << 11, + SIXF_CLEARCALLERTID = 1 << 12, + SIXF_MULTIPLYSPEED = 1 << 13, + SIXF_TRANSFERSCALE = 1 << 14, + SIXF_TRANSFERSPECIAL = 1 << 15, + SIXF_CLEARCALLERSPECIAL = 1 << 16, + SIXF_TRANSFERSTENCILCOL = 1 << 17, + SIXF_TRANSFERALPHA = 1 << 18, + SIXF_TRANSFERRENDERSTYLE = 1 << 19, + SIXF_SETTARGET = 1 << 20, + SIXF_SETTRACER = 1 << 21, + SIXF_NOPOINTERS = 1 << 22, + SIXF_ORIGINATOR = 1 << 23, +}; + +static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) +{ + if (mo == NULL) + { + return false; + } + AActor *originator = self; + + if (!(mo->flags2 & MF2_DONTTRANSLATE)) + { + if (flags & SIXF_TRANSFERTRANSLATION) + { + mo->Translation = self->Translation; + } + else if (flags & SIXF_USEBLOODCOLOR) + { + // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. + PalEntry bloodcolor = self->GetBloodColor(); + mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + } + } + if (flags & SIXF_TRANSFERPOINTERS) + { + mo->target = self->target; + mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set + mo->tracer = self->tracer; + } + + mo->angle = self->angle; + if (flags & SIXF_TRANSFERPITCH) + { + mo->pitch = self->pitch; + } + if (!(flags & SIXF_ORIGINATOR)) + { + while (originator && originator->isMissile()) + { + originator = originator->target; + } + } + if (flags & SIXF_TELEFRAG) + { + P_TeleportMove(mo, mo->x, mo->y, mo->z, true); + // This is needed to ensure consistent behavior. + // Otherwise it will only spawn if nothing gets telefragged + flags |= SIXF_NOCHECKPOSITION; + } + if (mo->flags3 & MF3_ISMONSTER) + { + if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) + { + // The monster is blocked so don't spawn it at all! + mo->ClearCounters(); + mo->Destroy(); + return false; + } + else if (originator && !(flags & SIXF_NOPOINTERS)) + { + if (originator->flags3 & MF3_ISMONSTER) + { + // If this is a monster transfer all friendliness information + mo->CopyFriendliness(originator, true); + } + else if (originator->player) + { + // A player always spawns a monster friendly to him + mo->flags |= MF_FRIENDLY; + mo->SetFriendPlayer(originator->player); + + AActor * attacker=originator->player->attacker; + if (attacker) + { + if (!(attacker->flags&MF_FRIENDLY) || + (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) + { + // Target the monster which last attacked the player + mo->LastHeard = mo->target = attacker; + } + } + } + } + } + else if (!(flags & SIXF_TRANSFERPOINTERS)) + { + // If this is a missile or something else set the target to the originator + mo->target = originator ? originator : self; + } + if (flags & SIXF_NOPOINTERS) + { + //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. + mo->LastHeard = NULL; //Sanity check. + mo->target = NULL; + mo->master = NULL; + mo->tracer = NULL; + } + if (flags & SIXF_SETMASTER) + { + mo->master = originator; + } + if (flags & SIXF_SETTARGET) + { + mo->target = originator; + } + if (flags & SIXF_SETTRACER) + { + mo->tracer = originator; + } + if (flags & SIXF_TRANSFERSCALE) + { + mo->scaleX = self->scaleX; + mo->scaleY = self->scaleY; + } + if (flags & SIXF_TRANSFERAMBUSHFLAG) + { + mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); + } + if (flags & SIXF_CLEARCALLERTID) + { + self->RemoveFromHash(); + self->tid = 0; + } + if (flags & SIXF_TRANSFERSPECIAL) + { + mo->special = self->special; + memcpy(mo->args, self->args, sizeof(self->args)); + } + if (flags & SIXF_CLEARCALLERSPECIAL) + { + self->special = 0; + memset(self->args, 0, sizeof(self->args)); + } + if (flags & SIXF_TRANSFERSTENCILCOL) + { + mo->fillcolor = self->fillcolor; + } + if (flags & SIXF_TRANSFERALPHA) + { + mo->alpha = self->alpha; + } + if (flags & SIXF_TRANSFERRENDERSTYLE) + { + mo->RenderStyle = self->RenderStyle; + } + + return true; +} + +//=========================================================================== +// +// A_SpawnItem +// +// Spawns an item in front of the caller like Heretic's time bomb +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_FIXED(zheight, 2); + ACTION_PARAM_BOOL(useammo, 3); + ACTION_PARAM_BOOL(transfer_translation, 4); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + // Don't spawn monsters if this actor has been massacred + if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + if (distance==0) + { + // use the minimum distance that does not result in an overlap + distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; + } + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon so use some ammo + AWeapon * weapon=self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + AActor * mo = Spawn( missile, + self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), + self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), + self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); + + int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state +} + +//=========================================================================== +// +// A_SpawnItemEx +// +// Enhanced spawning function +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) +{ + ACTION_PARAM_START(11); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_FIXED(xvel, 4); + ACTION_PARAM_FIXED(yvel, 5); + ACTION_PARAM_FIXED(zvel, 6); + ACTION_PARAM_ANGLE(Angle, 7); + ACTION_PARAM_INT(flags, 8); + ACTION_PARAM_INT(chance, 9); + ACTION_PARAM_INT(tid, 10); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + fixed_t x,y; + + if (!(flags & SIXF_ABSOLUTEANGLE)) + { + Angle += self->angle; + } + + angle_t ang = Angle >> ANGLETOFINESHIFT; + + if (flags & SIXF_ABSOLUTEPOSITION) + { + x = self->x + xofs; + y = self->y + yofs; + } + else + { + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); + y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); + } + + if (!(flags & SIXF_ABSOLUTEVELOCITY)) + { + // Same orientation issue here! + fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); + yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); + xvel = newxvel; + } + + AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state + if (res) + { + if (tid != 0) + { + assert(mo->tid == 0); + mo->tid = tid; + mo->AddToHash(); + } + if (flags & SIXF_MULTIPLYSPEED) + { + mo->velx = FixedMul(xvel, mo->Speed); + mo->vely = FixedMul(yvel, mo->Speed); + mo->velz = FixedMul(zvel, mo->Speed); + } + else + { + mo->velx = xvel; + mo->vely = yvel; + mo->velz = zvel; + } + mo->angle = Angle; + } +} + +//=========================================================================== +// +// A_ThrowGrenade +// +// Throws a grenade (like Hexen's fighter flechette) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(zheight, 1); + ACTION_PARAM_FIXED(xyvel, 2); + ACTION_PARAM_FIXED(zvel, 3); + ACTION_PARAM_BOOL(useammo, 4); + + if (missile == NULL) return; + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon, so use some ammo + AWeapon *weapon = self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + + AActor * bo; + + bo = Spawn(missile, self->x, self->y, + self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), + ALLOW_REPLACE); + if (bo) + { + P_PlaySpawnSound(bo, self); + if (xyvel != 0) + bo->Speed = xyvel; + bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); + + angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; + angle_t angle = bo->angle >> ANGLETOFINESHIFT; + + // There are two vectors we are concerned about here: xy and z. We rotate + // them separately according to the shooter's pitch and then sum them to + // get the final velocity vector to shoot with. + + fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); + fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); + fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); + fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); + + pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; + fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); + fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); + fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); + fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); + + bo->velx = xy_velx + z_velx + (self->velx >> 1); + bo->vely = xy_vely + z_vely + (self->vely >> 1); + bo->velz = xy_velz + z_velz; + + bo->target = self; + P_CheckMissileSpawn (bo, self->radius); + } + else ACTION_SET_RESULT(false); +} + + +//=========================================================================== +// +// A_Recoil +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(xyvel, 0); + + angle_t angle = self->angle + ANG180; + angle >>= ANGLETOFINESHIFT; + self->velx += FixedMul (xyvel, finecosine[angle]); + self->vely += FixedMul (xyvel, finesine[angle]); +} + + +//=========================================================================== +// +// A_SelectWeapon +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(cls, 0); + + if (cls == NULL || self->player == NULL) + { + ACTION_SET_RESULT(false); + return; + } + + AWeapon * weaponitem = static_cast(self->FindInventory(cls)); + + if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) + { + if (self->player->ReadyWeapon != weaponitem) + { + self->player->PendingWeapon = weaponitem; + } + } + else ACTION_SET_RESULT(false); + +} + + +//=========================================================================== +// +// A_Print +// +//=========================================================================== +EXTERN_CVAR(Float, con_midtime) + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + if (text[0] == '$') text = GStrings(text+1); + if (self->CheckLocalView (consoleplayer) || + (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) + { + float saved = con_midtime; + FFont *font = NULL; + + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + } + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_PrintBold +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + float saved = con_midtime; + FFont *font = NULL; + + if (text[0] == '$') text = GStrings(text+1); + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_Log +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STRING(text, 0); + + if (text[0] == '$') text = GStrings(text+1); + FString formatted = strbin1(text); + Printf("%s\n", formatted.GetChars()); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//========================================================================= +// +// A_LogInt +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(num, 0); + Printf("%d\n", num); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_SetTranslucent +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(alpha, 0); + ACTION_PARAM_INT(mode, 1); + + mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha = clamp(alpha, 0, FRACUNIT); + self->RenderStyle = ERenderStyle(mode); +} + +//=========================================================================== +// +// A_FadeIn +// +// Fades the actor in +// +//=========================================================================== + +enum FadeFlags +{ + FTF_REMOVE = 1 << 0, + FTF_CLAMP = 1 << 1, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_INT(flags, 1); + + if (reduce == 0) + { + reduce = FRACUNIT / 10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha += reduce; + + if (self->alpha >= (FRACUNIT * 1)) + { + if (flags & FTF_CLAMP) + self->alpha = (FRACUNIT * 1); + if (flags & FTF_REMOVE) + self->Destroy(); + } +} + +//=========================================================================== +// +// A_FadeOut +// +// fades the actor out and destroys it when done +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_INT(flags, 1); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha -= reduce; + if (self->alpha <= 0) + { + if (flags & FTF_CLAMP) + self->alpha = 0; + if (flags & FTF_REMOVE) + self->Destroy(); + } +} + +//=========================================================================== +// +// A_FadeTo +// +// fades the actor to a specified transparency by a specified amount and +// destroys it if so desired +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_FIXED(target, 0); + ACTION_PARAM_FIXED(amount, 1); + ACTION_PARAM_INT(flags, 2); + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + + if (self->alpha > target) + { + self->alpha -= amount; + + if (self->alpha < target) + { + self->alpha = target; + } + } + else if (self->alpha < target) + { + self->alpha += amount; + + if (self->alpha > target) + { + self->alpha = target; + } + } + if (flags & FTF_CLAMP) + { + if (self->alpha > (FRACUNIT * 1)) + self->alpha = (FRACUNIT * 1); + else if (self->alpha < 0) + self->alpha = 0; + } + if (self->alpha == target && (flags & FTF_REMOVE)) + { + self->Destroy(); + } +} + +//=========================================================================== +// +// A_Scale(float scalex, optional float scaley) +// +// Scales the actor's graphics. If scaley is 0, use scalex. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_FIXED(scalex, 0); + ACTION_PARAM_FIXED(scaley, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->scaleX = scalex; + ref->scaleY = scaley ? scaley : scalex; +} + +//=========================================================================== +// +// A_SetMass(int mass) +// +// Sets the actor's mass. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(mass, 0); + + self->Mass = mass; +} + +//=========================================================================== +// +// A_SpawnDebris +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) +{ + int i; + AActor * mo; + + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(debris, 0); + ACTION_PARAM_BOOL(transfer_translation, 1); + ACTION_PARAM_FIXED(mult_h, 2); + ACTION_PARAM_FIXED(mult_v, 3); + + if (debris == NULL) return; + + // only positive values make sense here + if (mult_v<=0) mult_v=FRACUNIT; + if (mult_h<=0) mult_h=FRACUNIT; + + for (i = 0; i < GetDefaultByType(debris)->health; i++) + { + mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), + self->y + ((pr_spawndebris()-128)<<12), + self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); + if (mo) + { + if (transfer_translation) + { + mo->Translation = self->Translation; + } + if (i < mo->GetClass()->ActorInfo->NumOwnedStates) + { + mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); + } + mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); + mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + } + } +} + + +//=========================================================================== +// +// A_CheckSight +// jumps if no player can see this actor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + for (int i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + // Always check sight from each player. + if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) + { + return; + } + // If a player is viewing from a non-player, then check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) + { + return; + } + } + } + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckSightOrRange +// Jumps if this actor is out of range of all players *and* out of sight. +// Useful for maps with many multi-actor special effects. +// +//=========================================================================== +static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool twodi) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height) + { + dz = self->z + self->height - eyez; + } + else if (eyez < self->z) + { + dz = self->z - eyez; + } + else + { + dz = 0; + } + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ + // Within range + return true; + } + + // Now check LOS. + if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) + { // Visible + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) +{ + ACTION_PARAM_START(3); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckSightOrRange(self, players[i].mo, range, twodi)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckSightOrRange(self, players[i].camera, range, twodi)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckRange +// Jumps if this actor is out of range of all players. +// +//=========================================================================== +static bool DoCheckRange(AActor *self, AActor *camera, double range, bool twodi) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height){ + dz = self->z + self->height - eyez; + } + else if (eyez < self->z){ + dz = self->z - eyez; + } + else{ + dz = 0; + } + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ + // Within range + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) +{ + ACTION_PARAM_START(3); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckRange(self, players[i].mo, range, twodi)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckRange(self, players[i].camera, range, twodi)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + + +//=========================================================================== +// +// Inventory drop +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(drop, 0); + + if (drop) + { + AInventory * inv = self->FindInventory(drop); + if (inv) + { + self->DropInventory(inv); + } + } +} + + +//=========================================================================== +// +// A_SetBlend +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_COLOR(color, 0); + ACTION_PARAM_FLOAT(alpha, 1); + ACTION_PARAM_INT(tics, 2); + ACTION_PARAM_COLOR(color2, 3); + + if (color == MAKEARGB(255,255,255,255)) color=0; + if (color2 == MAKEARGB(255,255,255,255)) color2=0; + if (!color2.a) + color2 = color; + + new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, + color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, + (float)tics/TICRATE, self); +} + + +//=========================================================================== +// +// A_JumpIf +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(expression, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (expression) ACTION_JUMP(jump); + +} + +//=========================================================================== +// +// A_CountdownArg +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(cnt, 0); + ACTION_PARAM_STATE(state, 1); + + if (cnt<0 || cnt>=5) return; + if (!self->args[cnt]--) + { + if (self->flags&MF_MISSILE) + { + P_ExplodeMissile(self, NULL, NULL); + } + else if (self->flags&MF_SHOOTABLE) + { + P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); + } + else + { + // can't use "Death" as default parameter with current DECORATE parser. + if (state == NULL) state = self->FindState(NAME_Death); + self->SetState(state); + } + } + +} + +//============================================================================ +// +// A_Burst +// +//============================================================================ + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(chunk, 0); + + int i, numChunks; + AActor * mo; + + if (chunk == NULL) return; + + self->velx = self->vely = self->velz = 0; + self->height = self->GetDefault()->height; + + // [RH] In Hexen, this creates a random number of shards (range [24,56]) + // with no relation to the size of the self shattering. I think it should + // base the number of shards on the size of the dead thing, so bigger + // things break up into more shards than smaller things. + // An self with radius 20 and height 64 creates ~40 chunks. + numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); + i = (pr_burst.Random2()) % (numChunks/4); + for (i = MAX (24, numChunks + i); i >= 0; i--) + { + mo = Spawn(chunk, + self->x + (((pr_burst()-128)*self->radius)>>7), + self->y + (((pr_burst()-128)*self->radius)>>7), + self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); + + if (mo) + { + mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; + mo->velx = pr_burst.Random2 () << (FRACBITS-7); + mo->vely = pr_burst.Random2 () << (FRACBITS-7); + mo->RenderStyle = self->RenderStyle; + mo->alpha = self->alpha; + mo->CopyFriendliness(self, true); + } + } + + // [RH] Do some stuff to make this more useful outside Hexen + if (self->flags4 & MF4_BOSSDEATH) + { + CALL_ACTION(A_BossDeath, self); + } + A_Unblock(self, true); + + self->Destroy (); +} + +//=========================================================================== +// +// A_CheckFloor +// [GRB] Jumps if actor is standing on floor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->z <= self->floorz) + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_CheckCeiling +// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling +// + +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); + if (self->z+self->height >= self->ceilingz) // Height needs to be counted + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_Stop +// resets all velocity of the actor to 0 +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_Stop) +{ + self->velx = self->vely = self->velz = 0; + if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +static void CheckStopped(AActor *self) +{ + if (self->player != NULL && + self->player->mo == self && + !(self->player->cheats & CF_PREDICTING) && + !(self->velx | self->vely | self->velz)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +//=========================================================================== +// +// A_Respawn +// +//=========================================================================== + +extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); + +enum RS_Flags +{ + RSF_FOG=1, + RSF_KEEPTARGET=2, + RSF_TELEFRAG=4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + bool oktorespawn = false; + fixed_t oldx = self->x; + fixed_t oldy = self->y; + fixed_t oldz = self->z; + self->flags |= MF_SOLID; + self->height = self->GetDefault()->height; + CALL_ACTION(A_RestoreSpecialPosition, self); + + if (flags & RSF_TELEFRAG) + { + // [KS] DIE DIE DIE DIE erm *ahem* =) + oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); + if (oktorespawn) + { // Need to do this over again, since P_TeleportMove() will redo + // it with the proper point-on-side calculation. + self->UnlinkFromWorld(); + self->LinkToWorld(true); + sector_t *sec = self->Sector; + self->dropoffz = + self->floorz = sec->floorplane.ZatPoint(self->x, self->y); + self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); + P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); + } + } + else + { + oktorespawn = P_CheckPosition(self, self->x, self->y, true); + } + + if (oktorespawn) + { + AActor *defs = self->GetDefault(); + self->health = defs->health; + + // [KS] Don't keep target, because it could be self if the monster committed suicide + // ...Actually it's better off an option, so you have better control over monster behavior. + if (!(flags & RSF_KEEPTARGET)) + { + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; + } + else + { + // Don't attack yourself (Re: "Marine targets itself after suicide") + if (self->target == self) self->target = NULL; + if (self->lastenemy == self) self->lastenemy = NULL; + } + + self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); + self->flags2 = defs->flags2; + self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); + self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); + self->flags5 = defs->flags5; + self->flags6 = defs->flags6; + self->flags7 = defs->flags7; + self->SetState (self->SpawnState); + self->renderflags &= ~RF_INVISIBLE; + + if (flags & RSF_FOG) + { + P_SpawnTeleportFog(self, oldx, oldy, oldz, true); + P_SpawnTeleportFog(self, self->x, self->y, self->z, false); + } + if (self->CountsAsKill()) + { + level.total_monsters++; + } + } + else + { + self->flags &= ~MF_SOLID; + } +} + + +//========================================================================== +// +// A_PlayerSkinCheck +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->player != NULL && + skins[self->player->userinfo.GetSkin()].othergame) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetGravity +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(val, 0); + + self->gravity = clamp (val, 0, FRACUNIT*10); +} + + +// [KS] *** Start of my modifications *** + +//=========================================================================== +// +// A_ClearTarget +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) +{ + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; +} + +//========================================================================== +// +// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, +// fixed range = 0, angle angle = 0, angle pitch = 0, +// fixed offsetheight = 32, fixed offsetwidth = 0, +// int ptr_target = AAPTR_DEFAULT (target) ) +// +//========================================================================== + +enum CLOF_flags +{ + CLOFF_NOAIM_VERT = 0x00000001, + CLOFF_NOAIM_HORZ = 0x00000002, + + CLOFF_JUMPENEMY = 0x00000004, + CLOFF_JUMPFRIEND = 0x00000008, + CLOFF_JUMPOBJECT = 0x00000010, + CLOFF_JUMPNONHOSTILE = 0x00000020, + + CLOFF_SKIPENEMY = 0x00000040, + CLOFF_SKIPFRIEND = 0x00000080, + CLOFF_SKIPOBJECT = 0x00000100, + CLOFF_SKIPNONHOSTILE = 0x00000200, + + CLOFF_MUSTBESHOOTABLE = 0x00000400, + + CLOFF_SKIPTARGET = 0x00000800, + CLOFF_ALLOWNULL = 0x00001000, + CLOFF_CHECKPARTIAL = 0x00002000, + + CLOFF_MUSTBEGHOST = 0x00004000, + CLOFF_IGNOREGHOST = 0x00008000, + + CLOFF_MUSTBESOLID = 0x00010000, + CLOFF_BEYONDTARGET = 0x00020000, + + CLOFF_FROMBASE = 0x00040000, + CLOFF_MUL_HEIGHT = 0x00080000, + CLOFF_MUL_WIDTH = 0x00100000, + + CLOFF_JUMP_ON_MISS = 0x00200000, + CLOFF_AIM_VERT_NOOFFSET = 0x00400000, + + CLOFF_SETTARGET = 0x00800000, + CLOFF_SETMASTER = 0x01000000, + CLOFF_SETTRACER = 0x02000000, +}; + +struct LOFData +{ + AActor *Self; + AActor *Target; + int Flags; + bool BadActor; +}; + +ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) +{ + LOFData *data = (LOFData *)userdata; + int flags = data->Flags; + + if (trace.HitType != TRACE_HitActor) + { + return TRACE_Stop; + } + if (trace.Actor == data->Target) + { + if (flags & CLOFF_SKIPTARGET) + { + if (flags & CLOFF_BEYONDTARGET) + { + return TRACE_Skip; + } + return TRACE_Abort; + } + return TRACE_Stop; + } + if (flags & CLOFF_MUSTBESHOOTABLE) + { // all shootability checks go here + if (!(trace.Actor->flags & MF_SHOOTABLE)) + { + return TRACE_Skip; + } + if (trace.Actor->flags2 & MF2_NONSHOOTABLE) + { + return TRACE_Skip; + } + } + if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) + { + return TRACE_Skip; + } + if (flags & CLOFF_MUSTBEGHOST) + { + if (!(trace.Actor->flags3 & MF3_GHOST)) + { + return TRACE_Skip; + } + } + else if (flags & CLOFF_IGNOREGHOST) + { + if (trace.Actor->flags3 & MF3_GHOST) + { + return TRACE_Skip; + } + } + if ( + ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Stop; + } + if ( + ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Skip; + } + data->BadActor = true; + return TRACE_Abort; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) +{ + // Check line of fire + + /* + Not accounted for / I don't know how it works: FLOORCLIP + */ + + AActor *target; + fixed_t + x1, y1, z1, + vx, vy, vz; + + ACTION_PARAM_START(9); + + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_FIXED(range, 2); + ACTION_PARAM_FIXED(minrange, 3); + { + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_FIXED(offsetheight, 6); + ACTION_PARAM_FIXED(offsetwidth, 7); + ACTION_PARAM_INT(ptr_target, 8); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default + + if (flags & CLOFF_MUL_HEIGHT) + { + if (self->player != NULL) + { + // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player + offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); + } + else + { + offsetheight = FixedMul(offsetheight, self->height); + } + } + if (flags & CLOFF_MUL_WIDTH) + { + offsetwidth = FixedMul(self->radius, offsetwidth); + } + + x1 = self->x; + y1 = self->y; + z1 = self->z + offsetheight - self->floorclip; + + if (!(flags & CLOFF_FROMBASE)) + { // default to hitscan origin + + // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player + z1 += (self->height >> 1); + if (self->player != NULL) + { + z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); + } + else + { + z1 += 8*FRACUNIT; + } + } + + if (target) + { + FVector2 xyvec(target->x - x1, target->y - y1); + fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); + + if (range && !(flags & CLOFF_CHECKPARTIAL)) + { + if (distance > range) return; + } + + { + angle_t ang; + + if (flags & CLOFF_NOAIM_HORZ) + { + ang = self->angle; + } + else ang = R_PointToAngle2 (x1, y1, target->x, target->y); + + angle += ang; + + ang >>= ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + + if (flags & CLOFF_NOAIM_VERT) + { + pitch += self->pitch; + } + else if (flags & CLOFF_AIM_VERT_NOOFFSET) + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); + } + else + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); + } + } + else if (flags & CLOFF_ALLOWNULL) + { + angle += self->angle; + pitch += self->pitch; + + angle_t ang = self->angle >> ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + else return; + + angle >>= ANGLETOFINESHIFT; + pitch = (0-pitch)>>ANGLETOFINESHIFT; + + vx = FixedMul (finecosine[pitch], finecosine[angle]); + vy = FixedMul (finecosine[pitch], finesine[angle]); + vz = -finesine[pitch]; + } + + /* Variable set: + + jump, flags, target + x1,y1,z1 (trace point of origin) + vx,vy,vz (trace unit vector) + range + */ + + sector_t *sec = P_PointInSector(x1, y1); + + if (range == 0) + { + range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; + } + + FTraceResults trace; + LOFData lof_data; + + lof_data.Self = self; + lof_data.Target = target; + lof_data.Flags = flags; + lof_data.BadActor = false; + + Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, + CheckLOFTraceFunc, &lof_data); + + if (trace.HitType == TRACE_HitActor || + ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) + { + if (minrange > 0 && trace.Distance < minrange) + { + return; + } + if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor)) + { + if (flags & (CLOFF_SETTARGET)) self->target = trace.Actor; + if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor; + if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor; + } + + ACTION_JUMP(jump); + } +} + +//========================================================================== +// +// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, +// optional fixed dist_max, optional fixed dist_close) +// +// Jumps if the actor can see its target, or if the player has a linetarget. +// ProjectileTarget affects how projectiles are treated. If set, it will use +// the target of the projectile for seekers, and ignore the target for +// normal projectiles. If not set, it will use the missile's owner instead +// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max +// sets the maximum distance that actor can see, 0 means forever. dist_close +// uses special behavior if certain flags are set, 0 means no checks. +// +//========================================================================== + +enum JLOS_flags +{ + JLOSF_PROJECTILE = 1, + JLOSF_NOSIGHT = 1 << 1, + JLOSF_CLOSENOFOV = 1 << 2, + JLOSF_CLOSENOSIGHT = 1 << 3, + JLOSF_CLOSENOJUMP = 1 << 4, + JLOSF_DEADNOJUMP = 1 << 5, + JLOSF_CHECKMASTER = 1 << 6, + JLOSF_TARGETLOS = 1 << 7, + JLOSF_FLIPFOV = 1 << 8, + JLOSF_ALLYNOJUMP = 1 << 9, + JLOSF_COMBATANTONLY = 1 << 10, + JLOSF_NOAUTOAIM = 1 << 11, + JLOSF_CHECKTRACER = 1 << 12, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target, *viewport; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + bool doCheckSight; + + if (!self->player) + { + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) + { + if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + doCheckSight = !(flags & JLOSF_NOSIGHT); + } + else + { + // Does the player aim at something that can be shot? + P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); + + if (!target) return; + + switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) + { + case JLOSF_TARGETLOS|JLOSF_FLIPFOV: + // target makes sight check, player makes fov check; player has verified fov + fov = 0; + // fall-through + case JLOSF_TARGETLOS: + doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov + break; + default: + // player has verified sight and fov + fov = 0; + // fall-through + case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov + doCheckSight = false; + break; + } + } + + // [FDARI] If target is not a combatant, don't jump + if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; + + // [FDARI] If actors share team, don't jump + if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } + else { viewport = self; } + + if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) + return; + + if (flags & JLOSF_FLIPFOV) + { + if (viewport == self) { viewport = target; target = self; } + else { target = viewport; viewport = self; } + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (viewport->x, + viewport->y, + target->x, + target->y) + - viewport->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + + } + + ACTION_JUMP(jump); +} + + +//========================================================================== +// +// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags +// optional fixed dist_max, optional fixed dist_close) +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + { + if (self->flags2 & MF2_SEEKERMISSILE) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + bool doCheckSight = !(flags & JLOSF_NOSIGHT); + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (target->x, + target->y, + self->x, + self->y) + - target->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + } + + if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) + return; + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// Modified code pointer from Skulltag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + ACTION_PARAM_START(2); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(dontincrement, 2) + + if (count <= 0) return; + + AWeapon *weapon = self->player->ReadyWeapon; + + int ReloadCounter = weapon->ReloadCounter; + if(!dontincrement || ReloadCounter != 0) + ReloadCounter = (weapon->ReloadCounter+1) % count; + else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. + ReloadCounter = 1; + + // If we have not made our last shot... + if (ReloadCounter != 0) + { + // Go back to the refire frames, instead of continuing on to the reload frames. + ACTION_JUMP(jump); + } + else + { + // We need to reload. However, don't reload if we're out of ammo. + weapon->CheckAmmo( false, false ); + } + + if(!dontincrement) + weapon->ReloadCounter = ReloadCounter; +} + +//=========================================================================== +// +// Resets the counter for the above function +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + AWeapon *weapon = self->player->ReadyWeapon; + weapon->ReloadCounter = 0; +} + +//=========================================================================== +// +// A_ChangeFlag +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_BOOL(expression, 1); + + const char *dot = strchr (flagname, '.'); + FFlagDef *fd; + const PClass *cls = self->GetClass(); + + if (dot != NULL) + { + FString part1(flagname, dot-flagname); + fd = FindFlag (cls, part1, dot+1); + } + else + { + fd = FindFlag (cls, flagname, NULL); + } + + if (fd != NULL) + { + bool kill_before, kill_after; + INTBOOL item_before, item_after; + INTBOOL secret_before, secret_after; + + kill_before = self->CountsAsKill(); + item_before = self->flags & MF_COUNTITEM; + secret_before = self->flags5 & MF5_COUNTSECRET; + + if (fd->structoffset == -1) + { + HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); + } + else + { + DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); + + // If these 2 flags get changed we need to update the blockmap and sector links. + bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); + + if (linkchange) self->UnlinkFromWorld(); + ModActorFlag(self, fd, expression); + if (linkchange) self->LinkToWorld(); + } + kill_after = self->CountsAsKill(); + item_after = self->flags & MF_COUNTITEM; + secret_after = self->flags5 & MF5_COUNTSECRET; + // Was this monster previously worth a kill but no longer is? + // Or vice versa? + if (kill_before != kill_after) + { + if (kill_after) + { // It counts as a kill now. + level.total_monsters++; + } + else + { // It no longer counts as a kill. + level.total_monsters--; + } + } + // same for items + if (item_before != item_after) + { + if (item_after) + { // It counts as an item now. + level.total_items++; + } + else + { // It no longer counts as an item + level.total_items--; + } + } + // and secretd + if (secret_before != secret_after) + { + if (secret_after) + { // It counts as an secret now. + level.total_secrets++; + } + else + { // It no longer counts as an secret + level.total_secrets--; + } + } + } + else + { + Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); + } +} + +//=========================================================================== +// +// A_CheckFlag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_STATE(jumpto, 1); + ACTION_PARAM_INT(checkpointer, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + AActor *owner; + + COPY_AAPTR_NOT_NULL(self, owner, checkpointer); + + if (CheckActorFlag(owner, flagname)) + { + ACTION_JUMP(jumpto); + } +} + +//=========================================================================== +// +// A_RaiseMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + + if (self->master != NULL) + { + if (copy) + P_Thing_Raise(self->master, self); + else + P_Thing_Raise(self->master, NULL); + } +} + +//=========================================================================== +// +// A_RaiseChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + TThinkerIterator it; + AActor *mo; + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self) + { + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); + } + } +} + +//=========================================================================== +// +// A_RaiseSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self) + { + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); + } + } + } +} + +//=========================================================================== +// +// [TP] A_FaceConsolePlayer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { + ACTION_PARAM_START (1); + ACTION_PARAM_ANGLE (MaxTurnAngle, 0); + // NOTE: It does nothing for zdoom. +} + +//=========================================================================== +// +// A_MonsterRefire +// +// Keep firing unless target got out of sight +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(prob, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + A_FaceTarget (self); + + if (pr_monsterrefire() < prob) + return; + + if (!self->target + || P_HitFriend (self) + || self->target->health <= 0 + || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetAngle +// +// Set actor's angle (in degrees). +// +//=========================================================================== +enum +{ + SPF_FORCECLAMP = 1, // players always clamp + SPF_INTERPOLATE = 2, +}; + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_ANGLE(angle, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_SetPitch +// +// Set actor's pitch (in degrees). +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_ANGLE(pitch, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + if (ref->player != NULL || (flags & SPF_FORCECLAMP)) + { // clamp the pitch we set + int min, max; + + if (ref->player != NULL) + { + min = ref->player->MinPitch; + max = ref->player->MaxPitch; + } + else + { + min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); + max = ANGLE_90 - (1 << ANGLETOFINESHIFT); + } + pitch = clamp(pitch, min, max); + } + ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); +} + //=========================================================================== // // [Nash] A_SetRoll @@ -4013,1830 +4028,1830 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) ACTION_PARAM_START(3); ACTION_PARAM_ANGLE(roll, 0); ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; } ref->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); } - -//=========================================================================== -// -// A_ScaleVelocity -// -// Scale actor's velocity. -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(scale, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - INTBOOL was_moving = self->velx | self->vely | self->velz; - - ref->velx = FixedMul(ref->velx, scale); - ref->vely = FixedMul(ref->vely, scale); - ref->velz = FixedMul(ref->velz, scale); - - // If the actor was previously moving but now is not, and is a player, - // update its player variables. (See A_Stop.) - if (was_moving) - { - CheckStopped(ref); - } -} - -//=========================================================================== -// -// A_ChangeVelocity -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_FIXED(x, 0); - ACTION_PARAM_FIXED(y, 1); - ACTION_PARAM_FIXED(z, 2); - ACTION_PARAM_INT(flags, 3); - ACTION_PARAM_INT(ptr, 4); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - INTBOOL was_moving = ref->velx | ref->vely | ref->velz; - - fixed_t vx = x, vy = y, vz = z; - fixed_t sina = finesine[ref->angle >> ANGLETOFINESHIFT]; - fixed_t cosa = finecosine[ref->angle >> ANGLETOFINESHIFT]; - - if (flags & 1) // relative axes - make x, y relative to actor's current angle - { - vx = DMulScale16(x, cosa, -y, sina); - vy = DMulScale16(x, sina, y, cosa); - } - if (flags & 2) // discard old velocity - replace old velocity with new velocity - { - ref->velx = vx; - ref->vely = vy; - ref->velz = vz; - } - else // add new velocity to old velocity - { - ref->velx += vx; - ref->vely += vy; - ref->velz += vz; - } - - if (was_moving) - { - CheckStopped(self); - } -} - -//=========================================================================== -// -// A_SetArg -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(pos, 0); - ACTION_PARAM_INT(value, 1); - - // Set the value of the specified arg - if ((size_t)pos < countof(self->args)) - { - self->args[pos] = value; - } -} - -//=========================================================================== -// -// A_SetSpecial -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(spec, 0); - ACTION_PARAM_INT(arg0, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - ACTION_PARAM_INT(arg4, 5); - - self->special = spec; - self->args[0] = arg0; - self->args[1] = arg1; - self->args[2] = arg2; - self->args[3] = arg3; - self->args[4] = arg4; -} - -//=========================================================================== -// -// A_SetUserVar -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(value, 1); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Int) - { - Printf("%s is not a user variable in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user variable. - *(int *)(reinterpret_cast(self) + var->offset) = value; -} - -//=========================================================================== -// -// A_SetUserArray -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(pos, 1); - ACTION_PARAM_INT(value, 2); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) - { - Printf("%s is not a user array in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - if (pos < 0 || pos >= var->ValueType.size) - { - Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user array at index pos. - ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; -} - -//=========================================================================== -// -// A_Teleport(optional state teleportstate, optional class targettype, -// optional class fogtype, optional int flags, optional fixed mindist, -// optional fixed maxdist) -// -// Attempts to teleport to a targettype at least mindist away and at most -// maxdist away (0 means unlimited). If successful, spawn a fogtype at old -// location and place calling actor in teleportstate. -// -//=========================================================================== -enum T_Flags -{ - TF_TELEFRAG = 0x00000001, // Allow telefrag in order to teleport. - TF_RANDOMDECIDE = 0x00000002, // Randomly fail based on health. (A_Srcr2Decide) - TF_FORCED = 0x00000004, // Forget what's in the way. TF_Telefrag takes precedence though. - TF_KEEPVELOCITY = 0x00000008, // Preserve velocity. - TF_KEEPANGLE = 0x00000010, // Keep angle. - TF_USESPOTZ = 0x00000020, // Set the z to the spot's z, instead of the floor. - TF_NOSRCFOG = 0x00000040, // Don't leave any fog behind when teleporting. - TF_NODESTFOG = 0x00000080, // Don't spawn any fog at the arrival position. - TF_USEACTORFOG = 0x00000100, // Use the actor's TeleFogSourceType and TeleFogDestType fogs. - TF_NOJUMP = 0x00000200, // Don't jump after teleporting. -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_STATE(TeleportState, 0); - ACTION_PARAM_CLASS(TargetType, 1); - ACTION_PARAM_CLASS(FogType, 2); - ACTION_PARAM_INT(Flags, 3); - ACTION_PARAM_FIXED(MinDist, 4); - ACTION_PARAM_FIXED(MaxDist, 5); - - // Randomly choose not to teleport like A_Srcr2Decide. - if (Flags & TF_RANDOMDECIDE) - { - static const int chance[] = - { - 192, 120, 120, 120, 64, 64, 32, 16, 0 - }; - - unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); - - if (chanceindex >= countof(chance)) - { - chanceindex = countof(chance) - 1; - } - - if (pr_teleport() >= chance[chanceindex]) return; - } - - DSpotState *state = DSpotState::GetSpotState(); - if (state == NULL) return; - - if (!TargetType) TargetType = PClass::FindClass("BossSpot"); - - AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); - if (spot == NULL) return; - - fixed_t prevX = self->x; - fixed_t prevY = self->y; - fixed_t prevZ = self->z; - fixed_t aboveFloor = spot->z - spot->floorz; - fixed_t finalz = spot->floorz + aboveFloor; - - if (spot->z + self->height > spot->ceilingz) - finalz = spot->ceilingz - self->height; - else if (spot->z < spot->floorz) - finalz = spot->floorz; - - - //Take precedence and cooperate with telefragging first. - bool teleResult = P_TeleportMove(self, spot->x, spot->y, finalz, Flags & TF_TELEFRAG); - - if (Flags & TF_FORCED) - { - //If for some reason the original move didn't work, regardless of telefrag, force it to move. - self->SetOrigin(spot->x, spot->y, finalz); - teleResult = true; - } - - if (teleResult) - { - //If a fog type is defined in the parameter, or the user wants to use the actor's predefined fogs, - //and if there's no desire to be fogless, spawn a fog based upon settings. - if (FogType || (Flags & TF_USEACTORFOG)) - { - if (!(Flags & TF_NOSRCFOG)) - { - if (Flags & TF_USEACTORFOG) - P_SpawnTeleportFog(self, prevX, prevY, prevZ, true); - else - Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); - } - if (!(Flags & TF_NODESTFOG)) - { - if (Flags & TF_USEACTORFOG) - P_SpawnTeleportFog(self, self->x, self->y, self->z, false); - else - Spawn(FogType, self->x, self->y, self->z, ALLOW_REPLACE); - } - } - - if (Flags & TF_USESPOTZ) - self->z = spot->z; - else - self->z = self->floorz; - - if (!(Flags & TF_KEEPANGLE)) - self->angle = spot->angle; - - if (!(Flags & TF_KEEPVELOCITY)) - self->velx = self->vely = self->velz = 0; - - if (!(Flags & TF_NOJUMP)) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (TeleportState == NULL) - { - // Default to Teleport. - TeleportState = self->FindState("Teleport"); - // If still nothing, then return. - if (!TeleportState) return; - } - ACTION_JUMP(TeleportState); - return; - } - } - ACTION_SET_RESULT(teleResult); -} - -//=========================================================================== -// -// A_Turn -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_ANGLE(angle, 0); - self->angle += angle; -} - -//=========================================================================== -// -// A_Quake -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(intensity, 0); - ACTION_PARAM_INT(duration, 1); - ACTION_PARAM_INT(damrad, 2); - ACTION_PARAM_INT(tremrad, 3); - ACTION_PARAM_SOUND(sound, 4); - P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); -} - -//=========================================================================== -// -// A_Weave -// -//=========================================================================== - -void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) -{ - fixed_t newX, newY; - int weaveXY, weaveZ; - int angle; - fixed_t dist; - - weaveXY = self->WeaveIndexXY & 63; - weaveZ = self->WeaveIndexZ & 63; - angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; - - if (xydist != 0 && xyspeed != 0) - { - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX = self->x - FixedMul (finecosine[angle], dist); - newY = self->y - FixedMul (finesine[angle], dist); - weaveXY = (weaveXY + xyspeed) & 63; - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX += FixedMul (finecosine[angle], dist); - newY += FixedMul (finesine[angle], dist); - if (!(self->flags5 & MF5_NOINTERACTION)) - { - P_TryMove (self, newX, newY, true); - } - else - { - self->UnlinkFromWorld (); - self->flags |= MF_NOBLOCKMAP; - self->x = newX; - self->y = newY; - self->LinkToWorld (); - } - self->WeaveIndexXY = weaveXY; - } - if (zdist != 0 && zspeed != 0) - { - self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - weaveZ = (weaveZ + zspeed) & 63; - self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - self->WeaveIndexZ = weaveZ; - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(xspeed, 0); - ACTION_PARAM_INT(yspeed, 1); - ACTION_PARAM_FIXED(xdist, 2); - ACTION_PARAM_FIXED(ydist, 3); - A_Weave(self, xspeed, yspeed, xdist, ydist); -} - - - - -//=========================================================================== -// -// A_LineEffect -// -// This allows linedef effects to be activated inside deh frames. -// -//=========================================================================== - - -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(tag, 1); - - line_t junk; maplinedef_t oldjunk; - bool res = false; - if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up - { - if ((oldjunk.special = special)) // Linedef type - { - oldjunk.tag = tag; // Sector tag for linedef - P_TranslateLineDef(&junk, &oldjunk); // Turn into native type - res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], - junk.args[1], junk.args[2], junk.args[3], junk.args[4]); - if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, - self->flags6 |= MF6_LINEDONE; // no more for this thing - } - } - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// A Wolf3D-style attack codepointer -// -//========================================================================== -enum WolfAttackFlags -{ - WAF_NORANDOM = 1, - WAF_USEPUFF = 2, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) -{ - ACTION_PARAM_START(9); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_SOUND(sound, 1); - ACTION_PARAM_FIXED(snipe, 2); - ACTION_PARAM_INT(maxdamage, 3); - ACTION_PARAM_INT(blocksize, 4); - ACTION_PARAM_INT(pointblank, 5); - ACTION_PARAM_INT(longrange, 6); - ACTION_PARAM_FIXED(runspeed, 7); - ACTION_PARAM_CLASS(pufftype, 8); - - if (!self->target) - return; - - // Enemy can't see target - if (!P_CheckSight(self, self->target)) - return; - - A_FaceTarget (self); - - - // Target can dodge if it can see enemy - angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; - angle >>= 24; - bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); - - // Distance check is simplistic - fixed_t dx = abs (self->x - self->target->x); - fixed_t dy = abs (self->y - self->target->y); - fixed_t dz; - fixed_t dist = dx > dy ? dx : dy; - - // Some enemies are more precise - dist = FixedMul(dist, snipe); - - // Convert distance into integer number of blocks - dist >>= FRACBITS; - dist /= blocksize; - - // Now for the speed accuracy thingie - fixed_t speed = FixedMul(self->target->velx, self->target->velx) - + FixedMul(self->target->vely, self->target->vely) - + FixedMul(self->target->velz, self->target->velz); - int hitchance = speed < runspeed ? 256 : 160; - - // Distance accuracy (factoring dodge) - hitchance -= dist * (dodge ? 16 : 8); - - // While we're here, we may as well do something for this: - if (self->target->flags & MF_SHADOW) - { - hitchance >>= 2; - } - - // The attack itself - if (pr_cabullet() < hitchance) - { - // Compute position for spawning blood/puff - dx = self->target->x; - dy = self->target->y; - dz = self->target->z + (self->target->height>>1); - angle = R_PointToAngle2(dx, dy, self->x, self->y); - - dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); - dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); - - int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); - if (dist >= pointblank) - damage >>= 1; - if (dist >= longrange) - damage >>= 1; - FName mod = NAME_None; - bool spawnblood = !((self->target->flags & MF_NOBLOOD) - || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); - if (flags & WAF_USEPUFF && pufftype) - { - AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); - mod = dpuff->DamageType; - - if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) - damage = 0; - - if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) - { - spawnblood = false; - P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); - } - } - else if (self->target->flags3 & MF3_GHOST) - damage >>= 2; - if (damage) - { - int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); - if (spawnblood) - { - P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); - P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); - } - } - } - - // And finally, let's play the sound - S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); -} - - -//========================================================================== -// -// A_Warp -// -//========================================================================== - -enum WARPF -{ - WARPF_ABSOLUTEOFFSET = 0x1, - WARPF_ABSOLUTEANGLE = 0x2, - WARPF_USECALLERANGLE = 0x4, - - WARPF_NOCHECKPOSITION = 0x8, - - WARPF_INTERPOLATE = 0x10, - WARPF_WARPINTERPOLATION = 0x20, - WARPF_COPYINTERPOLATION = 0x40, - - WARPF_STOP = 0x80, - WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200, - WARPF_ABSOLUTEPOSITION = 0x400, - WARPF_BOB = 0x800, - WARPF_MOVEPTR = 0x1000, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) -{ - ACTION_PARAM_START(7); - - ACTION_PARAM_INT(destination_selector, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_INT(flags, 5); - ACTION_PARAM_STATE(success_state, 6); - - AActor *reference = COPY_AAPTR(self, destination_selector); - - //If there is no actor to warp to, fail. - if (!reference) - { - ACTION_SET_RESULT(false); - return; - } - - AActor *caller = self; - - if (flags & WARPF_MOVEPTR) - { - AActor *temp = reference; - reference = caller; - caller = temp; - } - - fixed_t oldx = caller->x; - fixed_t oldy = caller->y; - fixed_t oldz = caller->z; - - if (!(flags & WARPF_ABSOLUTEANGLE)) - { - angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; - } - if (!(flags & WARPF_ABSOLUTEPOSITION)) - { - if (!(flags & WARPF_ABSOLUTEOFFSET)) - { - angle_t fineangle = angle >> ANGLETOFINESHIFT; - fixed_t xofs1 = xofs; - - // (borrowed from A_SpawnItemEx, assumed workable) - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - - xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z); - - // now the caller's floorz should be appropriate for the assigned xy-position - // assigning position again with - - if (zofs) - { - // extra unlink, link and environment calculation - caller->SetOrigin( - caller->x, - caller->y, - caller->floorz + zofs); - } - else - { - // if there is no offset, there should be no ill effect from moving down to the - // already identified floor - - // A_Teleport does the same thing anyway - caller->z = caller->floorz; - } - } - else - { - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); - } - } - else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. - { - if (flags & WARPF_TOFLOOR) - { - caller->SetOrigin(xofs, yofs, caller->floorz + zofs); - } - else - { - caller->SetOrigin(xofs, yofs, zofs); - } - } - - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) - { - if (flags & WARPF_TESTONLY) - { - caller->SetOrigin(oldx, oldy, oldz); - } - else - { - caller->angle = angle; - - if (flags & WARPF_STOP) - { - caller->velx = 0; - caller->vely = 0; - caller->velz = 0; - } - - if (flags & WARPF_WARPINTERPOLATION) - { - caller->PrevX += caller->x - oldx; - caller->PrevY += caller->y - oldy; - caller->PrevZ += caller->z - oldz; - } - else if (flags & WARPF_COPYINTERPOLATION) - { - caller->PrevX = caller->x + reference->PrevX - reference->x; - caller->PrevY = caller->y + reference->PrevY - reference->y; - caller->PrevZ = caller->z + reference->PrevZ - reference->z; - } - else if (!(flags & WARPF_INTERPOLATE)) - { - caller->PrevX = caller->x; - caller->PrevY = caller->y; - caller->PrevZ = caller->z; - } - - if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) - { - caller->z += reference->GetBobOffset(); - } - } - - - if (success_state) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - // in this case, you have the statejump to help you handle all the success anyway. - ACTION_JUMP(success_state); - return; - } - - ACTION_SET_RESULT(true); - } - else - { - caller->SetOrigin(oldx, oldy, oldz); - ACTION_SET_RESULT(false); - } - -} - -//========================================================================== -// -// ACS_Named* stuff - -// -// These are exactly like their un-named line special equivalents, except -// they take strings instead of integers to indicate which script to run. -// Some of these probably aren't very useful, but they are included for -// the sake of completeness. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - - -//========================================================================== -// -// A_RadiusGive -// -// Uses code roughly similar to A_Explode (but without all the compatibility -// baggage and damage computation code to give an item to all eligible mobjs -// in range. -// -//========================================================================== -enum RadiusGiveFlags -{ - RGF_GIVESELF = 1 << 0, - RGF_PLAYERS = 1 << 1, - RGF_MONSTERS = 1 << 2, - RGF_OBJECTS = 1 << 3, - RGF_VOODOO = 1 << 4, - RGF_CORPSES = 1 << 5, - RGF_MASK = 2111, - RGF_NOTARGET = 1 << 6, - RGF_NOTRACER = 1 << 7, - RGF_NOMASTER = 1 << 8, - RGF_CUBE = 1 << 9, - RGF_NOSIGHT = 1 << 10, - RGF_MISSILES = 1 << 11, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(amount, 3); - - // We need a valid item, valid targets, and a valid range - if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) - { - return; - } - if (amount == 0) - { - amount = 1; - } - FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); - double distsquared = double(distance) * double(distance); - - AActor *thing; - while ((thing = it.Next())) - { - // Don't give to inventory items - if (thing->flags & MF_SPECIAL) - { - continue; - } - // Avoid giving to self unless requested - if (thing == self && !(flags & RGF_GIVESELF)) - { - continue; - } - // Avoiding special pointers if requested - if (((thing == self->target) && (flags & RGF_NOTARGET)) || - ((thing == self->tracer) && (flags & RGF_NOTRACER)) || - ((thing == self->master) && (flags & RGF_NOMASTER))) - { - continue; - } - // Don't give to dead thing unless requested - if (thing->flags & MF_CORPSE) - { - if (!(flags & RGF_CORPSES)) - { - continue; - } - } - else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) - { - continue; - } - // Players, monsters, and other shootable objects - if (thing->player) - { - if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) - { - continue; - } - if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) - { - continue; - } - } - else if (thing->flags3 & MF3_ISMONSTER) - { - if (!(flags & RGF_MONSTERS)) - { - continue; - } - } - else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) - { - if (!(flags & RGF_OBJECTS)) - { - continue; - } - } - else if (thing->flags & MF_MISSILE) - { - if (!(flags & RGF_MISSILES)) - { - continue; - } - } - else - { - continue; - } - - if (flags & RGF_CUBE) - { // check if inside a cube - if (abs(thing->x - self->x) > distance || - abs(thing->y - self->y) > distance || - abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) - { - continue; - } - } - else - { // check if inside a sphere - TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); - TVector3 spos(self->x, self->y, self->z + self->height/2); - if ((tpos - spos).LengthSquared() > distsquared) - { - continue; - } - } - fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); - - if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. - AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); - if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) - { - gift->Amount *= amount; - } - else - { - gift->Amount = amount; - } - gift->flags |= MF_DROPPED; - gift->ClearCounters(); - if (!gift->CallTryPickup (thing)) - { - gift->Destroy (); - } - } - } -} - - -//========================================================================== -// -// A_SetTics -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(tics_to_set, 0); - - if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) - { // Is this a weapon? Need to check psp states for a match, then. Blah. - for (int i = 0; i < NUMPSPRITES; ++i) - { - if (self->player->psprites[i].state == CallingState) - { - self->player->psprites[i].tics = tics_to_set; - return; - } - } - } - // Just set tics for self. - self->tics = tics_to_set; -} - -//========================================================================== -// -// A_SetDamageType -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(damagetype, 0); - - self->DamageType = damagetype; -} - -//========================================================================== -// -// A_DropItem -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_CLASS(spawntype, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(chance, 2); - - P_DropItem(self, spawntype, amount, chance); -} - -//========================================================================== -// -// A_SetSpeed -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(speed, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - ref->Speed = speed; -} - -static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) -{ - return (!(species) || mo->Species == NAME_None || (species && ((exclude) ? (mo->Species != species) : (mo->Species == species)))); -} - -static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) -{ - const PClass *c1 = mo->GetClass(); - return (!(filter) || (filter == NULL) || (filter && ((exclude) ? (c1 != filter) : (c1 == filter)))); -} - -//=========================================================================== -// -// Common A_Damage handler -// -// A_Damage* (int amount, str damagetype, int flags, str filter, str species) -// Damages the specified actor by the specified amount. Negative values heal. -// Flags: See below. -// Filter: Specified actor is the only type allowed to be affected. -// Species: Specified species is the only type allowed to be affected. -// -// Examples: -// A_Damage(20,"Normal",DMSS_FOILINVUL,0,"DemonicSpecies") <--Only actors -// with a species "DemonicSpecies" will be affected. Use 0 to not filter by actor. -// -//=========================================================================== - -enum DMSS -{ - DMSS_FOILINVUL = 1, //Foil invulnerability - DMSS_AFFECTARMOR = 2, //Make it affect armor - DMSS_KILL = 4, //Damages them for their current health - DMSS_NOFACTOR = 8, //Ignore DamageFactors - DMSS_FOILBUDDHA = 16, //Can kill actors with Buddha flag, except the player. - DMSS_NOPROTECT = 32, //Ignores PowerProtection entirely - DMSS_EXFILTER = 64, //Changes filter into a blacklisted class instead of whitelisted. - DMSS_EXSPECIES = 128, // ^ but with species instead. - DMSS_EITHER = 256, //Allow either type or species to be affected. -}; - -static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); - if ((flags & DMSS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) - { - int dmgFlags = 0; - if (flags & DMSS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (flags & DMSS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor - dmgFlags += DMG_NO_FACTOR; - if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor - dmgFlags += DMG_NO_ARMOR; - if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. - amount += dmgtarget->health; - if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. - dmgFlags += DMG_NO_PROTECT; - - if (amount > 0) - P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. - - else if (amount < 0) - { - amount = -amount; - P_GiveBody(dmgtarget, amount); - } - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - DoDamage(self, self, amount, DamageType, flags, filter, species); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->target != NULL) - { - DoDamage(self->target, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->tracer != NULL) - { - DoDamage(self->tracer, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->master != NULL) - { - DoDamage(self->master, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - TThinkerIterator it; - AActor * mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - DoDamage(mo, self, amount, DamageType, flags, filter, species); - } - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) - { - DoDamage(mo, self, amount, DamageType, flags, filter, species); - } - } - } -} - - -//=========================================================================== -// -// A_Kill*(damagetype, int flags) -// -//=========================================================================== -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, - KILS_FOILBUDDHA = 1 << 3, - KILS_EXFILTER = 1 << 4, - KILS_EXSPECIES = 1 << 5, - KILS_EITHER = 1 << 6, -}; - -static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); - if ((flags & KILS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) //Check this first. I think it'll save the engine a lot more time this way. - { - int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; - - if (KILS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (KILS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - - - if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && - (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(killtarget, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); - } - } -} - - -//=========================================================================== -// -// A_KillTarget(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->target != NULL) - { - DoKill(self->target, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillTracer(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->tracer != NULL) - { - DoKill(self->tracer, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->master != NULL) - { - DoKill(self->master, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - DoKill(mo, self, damagetype, flags, filter, species); - } - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) - { - DoKill(mo, self, damagetype, flags, filter, species); - } - } - } -} - -//=========================================================================== -// -// DoRemove -// -//=========================================================================== - -enum RMVF_flags -{ - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3, - RMVF_EXFILTER = 1 << 4, - RMVF_EXSPECIES = 1 << 5, - RMVF_EITHER = 1 << 6, -}; - -static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); - if ((flags & RMVF_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) - { - if ((flags & RMVF_EVERYTHING)) - { - P_RemoveThing(removetarget); - } - if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) - { - P_RemoveThing(removetarget); - } - } -} - -//=========================================================================== -// -// A_RemoveTarget -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->target != NULL) - { - DoRemove(self->target, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveTracer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->tracer != NULL) - { - DoRemove(self->tracer, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->master != NULL) - { - DoRemove(self->master, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(4); - ACTION_PARAM_BOOL(removeall, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - DoRemove(mo, flags, filter, species); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(4); - ACTION_PARAM_BOOL(removeall, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - DoRemove(mo, flags, filter, species); - } - } - } -} - -//=========================================================================== -// -// A_Remove -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(removee, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - AActor *reference = COPY_AAPTR(self, removee); - if (reference != NULL) - { - DoRemove(reference, flags, filter, species); - } -} - -//=========================================================================== -// -// A_SetTeleFog -// -// Sets the teleport fog(s) for the calling actor. -// Takes a name of the classes for te source and destination. -// Can set both at the same time. Use "" to retain the previous fog without -// changing it. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(oldpos, 0); - ACTION_PARAM_NAME(newpos, 1); - const PClass *check = PClass::FindClass(oldpos); - if (check == NULL || !stricmp(oldpos, "none") || !stricmp(oldpos, "null")) - self->TeleFogSourceType = NULL; - else if (!stricmp(oldpos, "")) - { //Don't change it if it's just "" - } - else - self->TeleFogSourceType = check; - - check = PClass::FindClass(newpos); - if (check == NULL || !stricmp(newpos, "none") || !stricmp(newpos, "null")) - self->TeleFogDestType = NULL; - else if (!stricmp(newpos, "")) - { //Don't change it if it's just "" - } - else - self->TeleFogDestType = check; -} - -//=========================================================================== -// -// A_SwapTeleFog -// -// Switches the source and dest telefogs around. -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) -{ - if ((self->TeleFogSourceType != self->TeleFogDestType)) //Does nothing if they're the same. - { - const PClass *temp = self->TeleFogSourceType; - self->TeleFogSourceType = self->TeleFogDestType; - self->TeleFogDestType = temp; - } -} - -//=========================================================================== -// -// A_SetFloatBobPhase -// -// Changes the FloatBobPhase of the actor. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(bob, 0); - - //Respect float bob phase limits. - if (self && (bob >= 0 && bob <= 63)) - self->FloatBobPhase = bob; -} - -//=========================================================================== -// A_SetHealth -// -// Changes the health of the actor. -// Takes a pointer as well. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(health, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *mobj = COPY_AAPTR(self, ptr); - - if (!mobj) - { - return; - } - - player_t *player = mobj->player; - if (player) - { - if (health <= 0) - player->mo->health = mobj->health = player->health = 1; //Copied from the buddha cheat. - else - player->mo->health = mobj->health = player->health = health; - } - else if (mobj) - { - if (health <= 0) - mobj->health = 1; - else - mobj->health = health; - } -} - -//=========================================================================== -// A_ResetHealth -// -// Resets the health of the actor to default, except if their dead. -// Takes a pointer. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(ptr, 0); - - AActor *mobj = COPY_AAPTR(self, ptr); - - if (!mobj) - { - return; - } - - player_t *player = mobj->player; - if (player && (player->mo->health > 0)) - { - player->health = player->mo->health = player->mo->GetDefault()->health; //Copied from the resurrect cheat. - } - else if (mobj && (mobj->health > 0)) - { - mobj->health = mobj->SpawnHealth(); - } -} - -//=========================================================================== -// -// A_SetRipperLevel(int level) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(level, 0); - self->RipperLevel = level; -} - -//=========================================================================== -// -// A_SetRipMin(int min) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(min, 1); - self->RipLevelMin = min; -} - -//=========================================================================== -// -// A_SetRipMin(int min) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(max, 1); - self->RipLevelMax = max; + +//=========================================================================== +// +// A_ScaleVelocity +// +// Scale actor's velocity. +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(scale, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + INTBOOL was_moving = self->velx | self->vely | self->velz; + + ref->velx = FixedMul(ref->velx, scale); + ref->vely = FixedMul(ref->vely, scale); + ref->velz = FixedMul(ref->velz, scale); + + // If the actor was previously moving but now is not, and is a player, + // update its player variables. (See A_Stop.) + if (was_moving) + { + CheckStopped(ref); + } +} + +//=========================================================================== +// +// A_ChangeVelocity +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_FIXED(x, 0); + ACTION_PARAM_FIXED(y, 1); + ACTION_PARAM_FIXED(z, 2); + ACTION_PARAM_INT(flags, 3); + ACTION_PARAM_INT(ptr, 4); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + INTBOOL was_moving = ref->velx | ref->vely | ref->velz; + + fixed_t vx = x, vy = y, vz = z; + fixed_t sina = finesine[ref->angle >> ANGLETOFINESHIFT]; + fixed_t cosa = finecosine[ref->angle >> ANGLETOFINESHIFT]; + + if (flags & 1) // relative axes - make x, y relative to actor's current angle + { + vx = DMulScale16(x, cosa, -y, sina); + vy = DMulScale16(x, sina, y, cosa); + } + if (flags & 2) // discard old velocity - replace old velocity with new velocity + { + ref->velx = vx; + ref->vely = vy; + ref->velz = vz; + } + else // add new velocity to old velocity + { + ref->velx += vx; + ref->vely += vy; + ref->velz += vz; + } + + if (was_moving) + { + CheckStopped(self); + } +} + +//=========================================================================== +// +// A_SetArg +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(pos, 0); + ACTION_PARAM_INT(value, 1); + + // Set the value of the specified arg + if ((size_t)pos < countof(self->args)) + { + self->args[pos] = value; + } +} + +//=========================================================================== +// +// A_SetSpecial +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(spec, 0); + ACTION_PARAM_INT(arg0, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + ACTION_PARAM_INT(arg4, 5); + + self->special = spec; + self->args[0] = arg0; + self->args[1] = arg1; + self->args[2] = arg2; + self->args[3] = arg3; + self->args[4] = arg4; +} + +//=========================================================================== +// +// A_SetUserVar +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(value, 1); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Int) + { + Printf("%s is not a user variable in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user variable. + *(int *)(reinterpret_cast(self) + var->offset) = value; +} + +//=========================================================================== +// +// A_SetUserArray +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(pos, 1); + ACTION_PARAM_INT(value, 2); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) + { + Printf("%s is not a user array in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + if (pos < 0 || pos >= var->ValueType.size) + { + Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user array at index pos. + ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; +} + +//=========================================================================== +// +// A_Teleport(optional state teleportstate, optional class targettype, +// optional class fogtype, optional int flags, optional fixed mindist, +// optional fixed maxdist) +// +// Attempts to teleport to a targettype at least mindist away and at most +// maxdist away (0 means unlimited). If successful, spawn a fogtype at old +// location and place calling actor in teleportstate. +// +//=========================================================================== +enum T_Flags +{ + TF_TELEFRAG = 0x00000001, // Allow telefrag in order to teleport. + TF_RANDOMDECIDE = 0x00000002, // Randomly fail based on health. (A_Srcr2Decide) + TF_FORCED = 0x00000004, // Forget what's in the way. TF_Telefrag takes precedence though. + TF_KEEPVELOCITY = 0x00000008, // Preserve velocity. + TF_KEEPANGLE = 0x00000010, // Keep angle. + TF_USESPOTZ = 0x00000020, // Set the z to the spot's z, instead of the floor. + TF_NOSRCFOG = 0x00000040, // Don't leave any fog behind when teleporting. + TF_NODESTFOG = 0x00000080, // Don't spawn any fog at the arrival position. + TF_USEACTORFOG = 0x00000100, // Use the actor's TeleFogSourceType and TeleFogDestType fogs. + TF_NOJUMP = 0x00000200, // Don't jump after teleporting. +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(TeleportState, 0); + ACTION_PARAM_CLASS(TargetType, 1); + ACTION_PARAM_CLASS(FogType, 2); + ACTION_PARAM_INT(Flags, 3); + ACTION_PARAM_FIXED(MinDist, 4); + ACTION_PARAM_FIXED(MaxDist, 5); + + // Randomly choose not to teleport like A_Srcr2Decide. + if (Flags & TF_RANDOMDECIDE) + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); + + if (chanceindex >= countof(chance)) + { + chanceindex = countof(chance) - 1; + } + + if (pr_teleport() >= chance[chanceindex]) return; + } + + DSpotState *state = DSpotState::GetSpotState(); + if (state == NULL) return; + + if (!TargetType) TargetType = PClass::FindClass("BossSpot"); + + AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); + if (spot == NULL) return; + + fixed_t prevX = self->x; + fixed_t prevY = self->y; + fixed_t prevZ = self->z; + fixed_t aboveFloor = spot->z - spot->floorz; + fixed_t finalz = spot->floorz + aboveFloor; + + if (spot->z + self->height > spot->ceilingz) + finalz = spot->ceilingz - self->height; + else if (spot->z < spot->floorz) + finalz = spot->floorz; + + + //Take precedence and cooperate with telefragging first. + bool teleResult = P_TeleportMove(self, spot->x, spot->y, finalz, Flags & TF_TELEFRAG); + + if (Flags & TF_FORCED) + { + //If for some reason the original move didn't work, regardless of telefrag, force it to move. + self->SetOrigin(spot->x, spot->y, finalz); + teleResult = true; + } + + if (teleResult) + { + //If a fog type is defined in the parameter, or the user wants to use the actor's predefined fogs, + //and if there's no desire to be fogless, spawn a fog based upon settings. + if (FogType || (Flags & TF_USEACTORFOG)) + { + if (!(Flags & TF_NOSRCFOG)) + { + if (Flags & TF_USEACTORFOG) + P_SpawnTeleportFog(self, prevX, prevY, prevZ, true); + else + Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + } + if (!(Flags & TF_NODESTFOG)) + { + if (Flags & TF_USEACTORFOG) + P_SpawnTeleportFog(self, self->x, self->y, self->z, false); + else + Spawn(FogType, self->x, self->y, self->z, ALLOW_REPLACE); + } + } + + if (Flags & TF_USESPOTZ) + self->z = spot->z; + else + self->z = self->floorz; + + if (!(Flags & TF_KEEPANGLE)) + self->angle = spot->angle; + + if (!(Flags & TF_KEEPVELOCITY)) + self->velx = self->vely = self->velz = 0; + + if (!(Flags & TF_NOJUMP)) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (TeleportState == NULL) + { + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) return; + } + ACTION_JUMP(TeleportState); + return; + } + } + ACTION_SET_RESULT(teleResult); +} + +//=========================================================================== +// +// A_Turn +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_ANGLE(angle, 0); + self->angle += angle; +} + +//=========================================================================== +// +// A_Quake +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(intensity, 0); + ACTION_PARAM_INT(duration, 1); + ACTION_PARAM_INT(damrad, 2); + ACTION_PARAM_INT(tremrad, 3); + ACTION_PARAM_SOUND(sound, 4); + P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); +} + +//=========================================================================== +// +// A_Weave +// +//=========================================================================== + +void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + fixed_t dist; + + weaveXY = self->WeaveIndexXY & 63; + weaveZ = self->WeaveIndexZ & 63; + angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; + + if (xydist != 0 && xyspeed != 0) + { + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX = self->x - FixedMul (finecosine[angle], dist); + newY = self->y - FixedMul (finesine[angle], dist); + weaveXY = (weaveXY + xyspeed) & 63; + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX += FixedMul (finecosine[angle], dist); + newY += FixedMul (finesine[angle], dist); + if (!(self->flags5 & MF5_NOINTERACTION)) + { + P_TryMove (self, newX, newY, true); + } + else + { + self->UnlinkFromWorld (); + self->flags |= MF_NOBLOCKMAP; + self->x = newX; + self->y = newY; + self->LinkToWorld (); + } + self->WeaveIndexXY = weaveXY; + } + if (zdist != 0 && zspeed != 0) + { + self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + weaveZ = (weaveZ + zspeed) & 63; + self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + self->WeaveIndexZ = weaveZ; + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(xspeed, 0); + ACTION_PARAM_INT(yspeed, 1); + ACTION_PARAM_FIXED(xdist, 2); + ACTION_PARAM_FIXED(ydist, 3); + A_Weave(self, xspeed, yspeed, xdist, ydist); +} + + + + +//=========================================================================== +// +// A_LineEffect +// +// This allows linedef effects to be activated inside deh frames. +// +//=========================================================================== + + +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(tag, 1); + + line_t junk; maplinedef_t oldjunk; + bool res = false; + if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up + { + if ((oldjunk.special = special)) // Linedef type + { + oldjunk.tag = tag; // Sector tag for linedef + P_TranslateLineDef(&junk, &oldjunk); // Turn into native type + res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], + junk.args[1], junk.args[2], junk.args[3], junk.args[4]); + if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, + self->flags6 |= MF6_LINEDONE; // no more for this thing + } + } + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// A Wolf3D-style attack codepointer +// +//========================================================================== +enum WolfAttackFlags +{ + WAF_NORANDOM = 1, + WAF_USEPUFF = 2, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) +{ + ACTION_PARAM_START(9); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_SOUND(sound, 1); + ACTION_PARAM_FIXED(snipe, 2); + ACTION_PARAM_INT(maxdamage, 3); + ACTION_PARAM_INT(blocksize, 4); + ACTION_PARAM_INT(pointblank, 5); + ACTION_PARAM_INT(longrange, 6); + ACTION_PARAM_FIXED(runspeed, 7); + ACTION_PARAM_CLASS(pufftype, 8); + + if (!self->target) + return; + + // Enemy can't see target + if (!P_CheckSight(self, self->target)) + return; + + A_FaceTarget (self); + + + // Target can dodge if it can see enemy + angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; + angle >>= 24; + bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); + + // Distance check is simplistic + fixed_t dx = abs (self->x - self->target->x); + fixed_t dy = abs (self->y - self->target->y); + fixed_t dz; + fixed_t dist = dx > dy ? dx : dy; + + // Some enemies are more precise + dist = FixedMul(dist, snipe); + + // Convert distance into integer number of blocks + dist >>= FRACBITS; + dist /= blocksize; + + // Now for the speed accuracy thingie + fixed_t speed = FixedMul(self->target->velx, self->target->velx) + + FixedMul(self->target->vely, self->target->vely) + + FixedMul(self->target->velz, self->target->velz); + int hitchance = speed < runspeed ? 256 : 160; + + // Distance accuracy (factoring dodge) + hitchance -= dist * (dodge ? 16 : 8); + + // While we're here, we may as well do something for this: + if (self->target->flags & MF_SHADOW) + { + hitchance >>= 2; + } + + // The attack itself + if (pr_cabullet() < hitchance) + { + // Compute position for spawning blood/puff + dx = self->target->x; + dy = self->target->y; + dz = self->target->z + (self->target->height>>1); + angle = R_PointToAngle2(dx, dy, self->x, self->y); + + dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); + dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); + + int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); + if (dist >= pointblank) + damage >>= 1; + if (dist >= longrange) + damage >>= 1; + FName mod = NAME_None; + bool spawnblood = !((self->target->flags & MF_NOBLOOD) + || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); + if (flags & WAF_USEPUFF && pufftype) + { + AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); + mod = dpuff->DamageType; + + if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) + damage = 0; + + if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) + { + spawnblood = false; + P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); + } + } + else if (self->target->flags3 & MF3_GHOST) + damage >>= 2; + if (damage) + { + int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); + if (spawnblood) + { + P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); + P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); + } + } + } + + // And finally, let's play the sound + S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); +} + + +//========================================================================== +// +// A_Warp +// +//========================================================================== + +enum WARPF +{ + WARPF_ABSOLUTEOFFSET = 0x1, + WARPF_ABSOLUTEANGLE = 0x2, + WARPF_USECALLERANGLE = 0x4, + + WARPF_NOCHECKPOSITION = 0x8, + + WARPF_INTERPOLATE = 0x10, + WARPF_WARPINTERPOLATION = 0x20, + WARPF_COPYINTERPOLATION = 0x40, + + WARPF_STOP = 0x80, + WARPF_TOFLOOR = 0x100, + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, + WARPF_BOB = 0x800, + WARPF_MOVEPTR = 0x1000, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) +{ + ACTION_PARAM_START(7); + + ACTION_PARAM_INT(destination_selector, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_INT(flags, 5); + ACTION_PARAM_STATE(success_state, 6); + + AActor *reference = COPY_AAPTR(self, destination_selector); + + //If there is no actor to warp to, fail. + if (!reference) + { + ACTION_SET_RESULT(false); + return; + } + + AActor *caller = self; + + if (flags & WARPF_MOVEPTR) + { + AActor *temp = reference; + reference = caller; + caller = temp; + } + + fixed_t oldx = caller->x; + fixed_t oldy = caller->y; + fixed_t oldz = caller->z; + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; + } + if (!(flags & WARPF_ABSOLUTEPOSITION)) + { + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + angle_t fineangle = angle >> ANGLETOFINESHIFT; + fixed_t xofs1 = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + + caller->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z); + + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with + + if (zofs) + { + // extra unlink, link and environment calculation + caller->SetOrigin( + caller->x, + caller->y, + caller->floorz + zofs); + } + else + { + // if there is no offset, there should be no ill effect from moving down to the + // already identified floor + + // A_Teleport does the same thing anyway + caller->z = caller->floorz; + } + } + else + { + caller->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); + } + } + else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. + { + if (flags & WARPF_TOFLOOR) + { + caller->SetOrigin(xofs, yofs, caller->floorz + zofs); + } + else + { + caller->SetOrigin(xofs, yofs, zofs); + } + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) + { + if (flags & WARPF_TESTONLY) + { + caller->SetOrigin(oldx, oldy, oldz); + } + else + { + caller->angle = angle; + + if (flags & WARPF_STOP) + { + caller->velx = 0; + caller->vely = 0; + caller->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + caller->PrevX += caller->x - oldx; + caller->PrevY += caller->y - oldy; + caller->PrevZ += caller->z - oldz; + } + else if (flags & WARPF_COPYINTERPOLATION) + { + caller->PrevX = caller->x + reference->PrevX - reference->x; + caller->PrevY = caller->y + reference->PrevY - reference->y; + caller->PrevZ = caller->z + reference->PrevZ - reference->z; + } + else if (!(flags & WARPF_INTERPOLATE)) + { + caller->PrevX = caller->x; + caller->PrevY = caller->y; + caller->PrevZ = caller->z; + } + + if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) + { + caller->z += reference->GetBobOffset(); + } + } + + + if (success_state) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + // in this case, you have the statejump to help you handle all the success anyway. + ACTION_JUMP(success_state); + return; + } + + ACTION_SET_RESULT(true); + } + else + { + caller->SetOrigin(oldx, oldy, oldz); + ACTION_SET_RESULT(false); + } + +} + +//========================================================================== +// +// ACS_Named* stuff + +// +// These are exactly like their un-named line special equivalents, except +// they take strings instead of integers to indicate which script to run. +// Some of these probably aren't very useful, but they are included for +// the sake of completeness. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + + +//========================================================================== +// +// A_RadiusGive +// +// Uses code roughly similar to A_Explode (but without all the compatibility +// baggage and damage computation code to give an item to all eligible mobjs +// in range. +// +//========================================================================== +enum RadiusGiveFlags +{ + RGF_GIVESELF = 1 << 0, + RGF_PLAYERS = 1 << 1, + RGF_MONSTERS = 1 << 2, + RGF_OBJECTS = 1 << 3, + RGF_VOODOO = 1 << 4, + RGF_CORPSES = 1 << 5, + RGF_MASK = 2111, + RGF_NOTARGET = 1 << 6, + RGF_NOTRACER = 1 << 7, + RGF_NOMASTER = 1 << 8, + RGF_CUBE = 1 << 9, + RGF_NOSIGHT = 1 << 10, + RGF_MISSILES = 1 << 11, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(amount, 3); + + // We need a valid item, valid targets, and a valid range + if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) + { + return; + } + if (amount == 0) + { + amount = 1; + } + FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); + double distsquared = double(distance) * double(distance); + + AActor *thing; + while ((thing = it.Next())) + { + // Don't give to inventory items + if (thing->flags & MF_SPECIAL) + { + continue; + } + // Avoid giving to self unless requested + if (thing == self && !(flags & RGF_GIVESELF)) + { + continue; + } + // Avoiding special pointers if requested + if (((thing == self->target) && (flags & RGF_NOTARGET)) || + ((thing == self->tracer) && (flags & RGF_NOTRACER)) || + ((thing == self->master) && (flags & RGF_NOMASTER))) + { + continue; + } + // Don't give to dead thing unless requested + if (thing->flags & MF_CORPSE) + { + if (!(flags & RGF_CORPSES)) + { + continue; + } + } + else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) + { + continue; + } + // Players, monsters, and other shootable objects + if (thing->player) + { + if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) + { + continue; + } + if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) + { + continue; + } + } + else if (thing->flags3 & MF3_ISMONSTER) + { + if (!(flags & RGF_MONSTERS)) + { + continue; + } + } + else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) + { + if (!(flags & RGF_OBJECTS)) + { + continue; + } + } + else if (thing->flags & MF_MISSILE) + { + if (!(flags & RGF_MISSILES)) + { + continue; + } + } + else + { + continue; + } + + if (flags & RGF_CUBE) + { // check if inside a cube + if (abs(thing->x - self->x) > distance || + abs(thing->y - self->y) > distance || + abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) + { + continue; + } + } + else + { // check if inside a sphere + TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); + TVector3 spos(self->x, self->y, self->z + self->height/2); + if ((tpos - spos).LengthSquared() > distsquared) + { + continue; + } + } + fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); + + if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. + AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); + if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + { + gift->Amount *= amount; + } + else + { + gift->Amount = amount; + } + gift->flags |= MF_DROPPED; + gift->ClearCounters(); + if (!gift->CallTryPickup (thing)) + { + gift->Destroy (); + } + } + } +} + + +//========================================================================== +// +// A_SetTics +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(tics_to_set, 0); + + if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) + { // Is this a weapon? Need to check psp states for a match, then. Blah. + for (int i = 0; i < NUMPSPRITES; ++i) + { + if (self->player->psprites[i].state == CallingState) + { + self->player->psprites[i].tics = tics_to_set; + return; + } + } + } + // Just set tics for self. + self->tics = tics_to_set; +} + +//========================================================================== +// +// A_SetDamageType +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(damagetype, 0); + + self->DamageType = damagetype; +} + +//========================================================================== +// +// A_DropItem +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_CLASS(spawntype, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(chance, 2); + + P_DropItem(self, spawntype, amount, chance); +} + +//========================================================================== +// +// A_SetSpeed +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(speed, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->Speed = speed; +} + +static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) +{ + return (!(species) || mo->Species == NAME_None || (species && ((exclude) ? (mo->Species != species) : (mo->Species == species)))); +} + +static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) +{ + const PClass *c1 = mo->GetClass(); + return (!(filter) || (filter == NULL) || (filter && ((exclude) ? (c1 != filter) : (c1 == filter)))); +} + +//=========================================================================== +// +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags, str filter, str species) +// Damages the specified actor by the specified amount. Negative values heal. +// Flags: See below. +// Filter: Specified actor is the only type allowed to be affected. +// Species: Specified species is the only type allowed to be affected. +// +// Examples: +// A_Damage(20,"Normal",DMSS_FOILINVUL,0,"DemonicSpecies") <--Only actors +// with a species "DemonicSpecies" will be affected. Use 0 to not filter by actor. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, //Foil invulnerability + DMSS_AFFECTARMOR = 2, //Make it affect armor + DMSS_KILL = 4, //Damages them for their current health + DMSS_NOFACTOR = 8, //Ignore DamageFactors + DMSS_FOILBUDDHA = 16, //Can kill actors with Buddha flag, except the player. + DMSS_NOPROTECT = 32, //Ignores PowerProtection entirely + DMSS_EXFILTER = 64, //Changes filter into a blacklisted class instead of whitelisted. + DMSS_EXSPECIES = 128, // ^ but with species instead. + DMSS_EITHER = 256, //Allow either type or species to be affected. +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); + if ((flags & DMSS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) + { + int dmgFlags = 0; + if (flags & DMSS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (flags & DMSS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor + dmgFlags += DMG_NO_FACTOR; + if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor + dmgFlags += DMG_NO_ARMOR; + if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. + amount += dmgtarget->health; + if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. + dmgFlags += DMG_NO_PROTECT; + + if (amount > 0) + P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. + + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + DoDamage(self, self, amount, DamageType, flags, filter, species); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->target != NULL) + { + DoDamage(self->target, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->tracer != NULL) + { + DoDamage(self->tracer, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->master != NULL) + { + DoDamage(self->master, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) + { + DoDamage(mo, self, amount, DamageType, flags, filter, species); + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) + { + DoDamage(mo, self, amount, DamageType, flags, filter, species); + } + } + } +} + + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, + KILS_FOILBUDDHA = 1 << 3, + KILS_EXFILTER = 1 << 4, + KILS_EXSPECIES = 1 << 5, + KILS_EITHER = 1 << 6, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); + if ((flags & KILS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) //Check this first. I think it'll save the engine a lot more time this way. + { + int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; + + if (KILS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (KILS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + + + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && + (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(killtarget, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); + } + } +} + + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->target != NULL) + { + DoKill(self->target, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->tracer != NULL) + { + DoKill(self->tracer, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->master != NULL) + { + DoKill(self->master, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) + { + DoKill(mo, self, damagetype, flags, filter, species); + } + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) + { + if (mo->master == self->master && mo != self) + { + DoKill(mo, self, damagetype, flags, filter, species); + } + } + } +} + +//=========================================================================== +// +// DoRemove +// +//=========================================================================== + +enum RMVF_flags +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, + RMVF_EXFILTER = 1 << 4, + RMVF_EXSPECIES = 1 << 5, + RMVF_EITHER = 1 << 6, +}; + +static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); + if ((flags & RMVF_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) + { + if ((flags & RMVF_EVERYTHING)) + { + P_RemoveThing(removetarget); + } + if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) + { + P_RemoveThing(removetarget); + } + } +} + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->target != NULL) + { + DoRemove(self->target, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->tracer != NULL) + { + DoRemove(self->tracer, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->master != NULL) + { + DoRemove(self->master, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(4); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags, filter, species); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(4); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags, filter, species); + } + } + } +} + +//=========================================================================== +// +// A_Remove +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(removee, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + AActor *reference = COPY_AAPTR(self, removee); + if (reference != NULL) + { + DoRemove(reference, flags, filter, species); + } +} + +//=========================================================================== +// +// A_SetTeleFog +// +// Sets the teleport fog(s) for the calling actor. +// Takes a name of the classes for te source and destination. +// Can set both at the same time. Use "" to retain the previous fog without +// changing it. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(oldpos, 0); + ACTION_PARAM_NAME(newpos, 1); + const PClass *check = PClass::FindClass(oldpos); + if (check == NULL || !stricmp(oldpos, "none") || !stricmp(oldpos, "null")) + self->TeleFogSourceType = NULL; + else if (!stricmp(oldpos, "")) + { //Don't change it if it's just "" + } + else + self->TeleFogSourceType = check; + + check = PClass::FindClass(newpos); + if (check == NULL || !stricmp(newpos, "none") || !stricmp(newpos, "null")) + self->TeleFogDestType = NULL; + else if (!stricmp(newpos, "")) + { //Don't change it if it's just "" + } + else + self->TeleFogDestType = check; +} + +//=========================================================================== +// +// A_SwapTeleFog +// +// Switches the source and dest telefogs around. +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) +{ + if ((self->TeleFogSourceType != self->TeleFogDestType)) //Does nothing if they're the same. + { + const PClass *temp = self->TeleFogSourceType; + self->TeleFogSourceType = self->TeleFogDestType; + self->TeleFogDestType = temp; + } +} + +//=========================================================================== +// +// A_SetFloatBobPhase +// +// Changes the FloatBobPhase of the actor. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(bob, 0); + + //Respect float bob phase limits. + if (self && (bob >= 0 && bob <= 63)) + self->FloatBobPhase = bob; +} + +//=========================================================================== +// A_SetHealth +// +// Changes the health of the actor. +// Takes a pointer as well. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player) + { + if (health <= 0) + player->mo->health = mobj->health = player->health = 1; //Copied from the buddha cheat. + else + player->mo->health = mobj->health = player->health = health; + } + else if (mobj) + { + if (health <= 0) + mobj->health = 1; + else + mobj->health = health; + } +} + +//=========================================================================== +// A_ResetHealth +// +// Resets the health of the actor to default, except if their dead. +// Takes a pointer. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr, 0); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player && (player->mo->health > 0)) + { + player->health = player->mo->health = player->mo->GetDefault()->health; //Copied from the resurrect cheat. + } + else if (mobj && (mobj->health > 0)) + { + mobj->health = mobj->SpawnHealth(); + } +} + +//=========================================================================== +// +// A_SetRipperLevel(int level) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(level, 0); + self->RipperLevel = level; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(min, 1); + self->RipLevelMin = min; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(max, 1); + self->RipLevelMax = max; } \ No newline at end of file From 72506fa6e90286250a302dd99cf9a9250bc5db11 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 26 Jan 2015 15:00:36 -0500 Subject: [PATCH 115/117] - Fixed: Regression with centered coordinates. --- src/g_shared/sbarinfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 8ffedb5a4..791021ae8 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1280,8 +1280,8 @@ public: // We can't use DTA_HUDRules since it forces a width and height. // Translation: No high res. - bool xright = *x < 0; - bool ybot = *y < 0; + bool xright = *x < 0 && !x.RelCenter(); + bool ybot = *y < 0 && !y.RelCenter(); w = (forceWidth < 0 ? texture->GetScaledWidthDouble() : forceWidth); h = (forceHeight < 0 ? texture->GetScaledHeightDouble() : forceHeight); From d828173ce36f92de616718edfdaadcd91d081e5d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 29 Jan 2015 19:30:26 -0600 Subject: [PATCH 116/117] Revert "- fixed: Since A_Give/TaketoChildren/siblings use the same code as A_Give/TakeInventory but do not use the third parameter this must be ignored by the code. This is merely a band-aid fix, because this problem doesn't exist in the same form in the scripting branch." This reverts commit a4c07a9ee3a445f095d9618f928b1cb83dc54b0f. - This commit touched every line of thingdef_codeptr.cpp, which would make merging it into the scripting branch a pain in the ass. --- src/thingdef/thingdef_codeptr.cpp | 11665 ++++++++++++++-------------- 1 file changed, 5825 insertions(+), 5840 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 63c5d7408..be3921877 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1,4020 +1,4005 @@ -/* -** thingdef.cpp -** -** Code pointers for Actor definitions -** -**--------------------------------------------------------------------------- -** Copyright 2002-2006 Christoph Oelckers -** Copyright 2004-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be -** covered by 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 SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "gi.h" -#include "g_level.h" -#include "actor.h" -#include "info.h" -#include "sc_man.h" -#include "tarray.h" -#include "w_wad.h" -#include "templates.h" -#include "r_defs.h" -#include "a_pickups.h" -#include "s_sound.h" -#include "cmdlib.h" -#include "p_lnspec.h" -#include "p_enemy.h" -#include "a_action.h" -#include "decallib.h" -#include "m_random.h" -#include "i_system.h" -#include "p_local.h" -#include "c_console.h" -#include "doomerrors.h" -#include "a_sharedglobal.h" -#include "thingdef/thingdef.h" -#include "v_video.h" -#include "v_font.h" -#include "doomstat.h" -#include "v_palette.h" -#include "g_shared/a_specialspot.h" -#include "actorptrselect.h" -#include "m_bbox.h" -#include "r_data/r_translate.h" -#include "p_trace.h" -#include "gstrings.h" - - -static FRandom pr_camissile ("CustomActorfire"); -static FRandom pr_camelee ("CustomMelee"); -static FRandom pr_cabullet ("CustomBullet"); -static FRandom pr_cajump ("CustomJump"); -static FRandom pr_cwbullet ("CustomWpBullet"); -static FRandom pr_cwjump ("CustomWpJump"); -static FRandom pr_cwpunch ("CustomWpPunch"); -static FRandom pr_grenade ("ThrowGrenade"); -static FRandom pr_crailgun ("CustomRailgun"); -static FRandom pr_spawndebris ("SpawnDebris"); -static FRandom pr_spawnitemex ("SpawnItemEx"); -static FRandom pr_burst ("Burst"); -static FRandom pr_monsterrefire ("MonsterRefire"); -static FRandom pr_teleport("A_Teleport"); - -//========================================================================== -// -// ACustomInventory :: CallStateChain -// -// Executes the code pointers in a chain of states -// until there is no next state -// -//========================================================================== - -bool ACustomInventory::CallStateChain (AActor *actor, FState * State) -{ - StateCallData StateCall; - bool result = false; - int counter = 0; - - while (State != NULL) - { - // Assume success. The code pointer will set this to false if necessary - StateCall.State = State; - StateCall.Result = true; - if (State->CallAction(actor, this, &StateCall)) - { - // collect all the results. Even one successful call signifies overall success. - result |= StateCall.Result; - } - - - // Since there are no delays it is a good idea to check for infinite loops here! - counter++; - if (counter >= 10000) break; - - if (StateCall.State == State) - { - // Abort immediately if the state jumps to itself! - if (State == State->GetNextState()) - { - return false; - } - - // If both variables are still the same there was no jump - // so we must advance to the next state. - State = State->GetNextState(); - } - else - { - State = StateCall.State; - } - } - return result; -} - -//========================================================================== -// -// A_RearrangePointers -// -// Allow an actor to change its relationship to other actors by -// copying pointers freely between TARGET MASTER and TRACER. -// Can also assign null value, but does not duplicate A_ClearTarget. -// -//========================================================================== - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(ptr_target, 0); - ACTION_PARAM_INT(ptr_master, 1); - ACTION_PARAM_INT(ptr_tracer, 2); - ACTION_PARAM_INT(flags, 3); - - // Rearrange pointers internally - - // Fetch all values before modification, so that all fields can get original values - AActor - *gettarget = self->target, - *getmaster = self->master, - *gettracer = self->tracer; - - switch (ptr_target) // pick the new target - { - case AAPTR_MASTER: - self->target = getmaster; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_TRACER: - self->target = gettracer; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_NULL: - self->target = NULL; - // THIS IS NOT "A_ClearTarget", so no other targeting info is removed - break; - } - - // presently permitting non-monsters to set master - switch (ptr_master) // pick the new master - { - case AAPTR_TARGET: - self->master = gettarget; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_TRACER: - self->master = gettracer; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_NULL: - self->master = NULL; - break; - } - - switch (ptr_tracer) // pick the new tracer - { - case AAPTR_TARGET: - self->tracer = gettarget; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_MASTER: - self->tracer = getmaster; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_NULL: - self->tracer = NULL; - break; - } -} - -//========================================================================== -// -// A_TransferPointer -// -// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), -// or from this actor's MASTER, TARGET or TRACER. -// -// You can copy any one of that actor's pointers -// -// Assign the copied pointer to any one pointer in SELF, -// MASTER, TARGET or TRACER. -// -// Any attempt to make an actor point to itself will replace the pointer -// with a null value. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ptr_source, 0); - ACTION_PARAM_INT(ptr_recepient, 1); - ACTION_PARAM_INT(ptr_sourcefield, 2); - ACTION_PARAM_INT(ptr_recepientfield, 3); - ACTION_PARAM_INT(flags, 4); - - AActor *source, *recepient; - - // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) - - source = COPY_AAPTR(self, ptr_source); - COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value - - // convert source from dataprovider to data - - source = COPY_AAPTR(source, ptr_sourcefield); - - if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL - - if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from - - ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); -} - -//========================================================================== -// -// A_CopyFriendliness -// -// Join forces with one of the actors you are pointing to (MASTER by default) -// -// Normal CopyFriendliness reassigns health. This function will not. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(ptr_source, 0); - - if (self->player) return; - - AActor *source; - COPY_AAPTR_NOT_NULL(self, source, ptr_source); - self->CopyFriendliness(source, false, false); // No change in current target or health -} - -//========================================================================== -// -// Simple flag changers -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) -{ - self->flags |= MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) -{ - self->flags &= ~MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) -{ - self->flags |= MF_FLOAT; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) -{ - self->flags &= ~(MF_FLOAT|MF_INFLOAT); -} - -//========================================================================== -// -// Customizable attack functions which use actor parameters. -// -//========================================================================== -static void DoAttack (AActor *self, bool domelee, bool domissile, - int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) -{ - if (self->target == NULL) return; - - A_FaceTarget (self); - if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) - { - int damage = pr_camelee.HitDice(MeleeDamage); - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (domissile && MissileType != NULL) - { - // This seemingly senseless code is needed for proper aiming. - self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); - self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); -} - -DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) -{ - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(MeleeDamage, 0); - ACTION_PARAM_SOUND(MeleeSound, 1); - ACTION_PARAM_CLASS(MissileType, 2); - ACTION_PARAM_FIXED(MissileHeight, 3); - - if (MissileType == NULL) return; - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -//========================================================================== -// -// Custom sound functions. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_INT(channel, 1); - ACTION_PARAM_FLOAT(volume, 2); - ACTION_PARAM_BOOL(looping, 3); - ACTION_PARAM_FLOAT(attenuation, 4); - - if (!looping) - { - S_Sound (self, channel, soundid, volume, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, channel&7, soundid)) - { - S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(slot, 0); - - S_StopSound(self, slot); -} - -//========================================================================== -// -// These come from a time when DECORATE constants did not exist yet and -// the sound interface was less flexible. As a result the parameters are -// not optimal and these functions have been deprecated in favor of extending -// A_PlaySound and A_StopSound. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_SOUND(soundid, 0); - - S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_NAME(channel, 1); - ACTION_PARAM_BOOL(looping, 2); - ACTION_PARAM_INT(attenuation_raw, 3); - - float attenuation; - switch (attenuation_raw) - { - case -1: attenuation = ATTN_STATIC; break; // drop off rapidly - default: - case 0: attenuation = ATTN_NORM; break; // normal - case 1: - case 2: attenuation = ATTN_NONE; break; // full volume - } - - if (channel < NAME_Auto || channel > NAME_SoundSlot7) - { - channel = NAME_Auto; - } - - if (!looping) - { - S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) - { - S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(channel, 0); - - if (channel > NAME_Auto && channel <= NAME_SoundSlot7) - { - S_StopSound (self, int(channel) - NAME_Auto); - } -} - -//========================================================================== -// -// Generic seeker missile function -// -//========================================================================== -static FRandom pr_seekermissile ("SeekerMissile"); -enum -{ - SMF_LOOK = 1, - SMF_PRECISE = 2, - SMF_CURSPEED = 4, -}; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ang1, 0); - ACTION_PARAM_INT(ang2, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(chance, 3); - ACTION_PARAM_INT(distance, 4); - - if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); - } - if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) - { - if (flags & SMF_LOOK) - { // This monster is no longer seekable, so let us look for another one next time. - self->tracer = NULL; - } - } -} - -//========================================================================== -// -// Hitscan attack with a customizable amount of bullets (specified in damage) -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) -{ - int i; - int bangle; - int slope; - - if (!self->target) return; - - A_FaceTarget (self); - bangle = self->angle; - - slope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i = self->GetMissileDamage (0, 1); i > 0; --i) - { - int angle = bangle + (pr_cabullet.Random2() << 20); - int damage = ((pr_cabullet()%5)+1)*3; - P_LineAttack(self, angle, MISSILERANGE, slope, damage, - NAME_Hitscan, NAME_BulletPuff); - } -} - - -//========================================================================== -// -// Do the state jump -// -//========================================================================== -static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) -{ - if (jumpto == NULL) return; - - if (statecall != NULL) - { - statecall->State = jumpto; - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) - { - P_SetPsprite(self->player, ps_weapon, jumpto); - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) - { - P_SetPsprite(self->player, ps_flash, jumpto); - } - else if (CallingState == self->state) - { - self->SetState (jumpto); - } - else - { - // something went very wrong. This should never happen. - assert(false); - } -} - -// This is just to avoid having to directly reference the internally defined -// CallingState and statecall parameters in the code below. -#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_INT(maxchance, 1); - - if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) - { - int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); - ACTION_PARAM_STATE(jumpto, jumps); - ACTION_JUMP(jumpto); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(health, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_INT(ptr_selector, 2); - - AActor *measured; - - measured = COPY_AAPTR(self, ptr_selector); - - if (measured && measured->health < health) - { - ACTION_JUMP(jump); - } - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (!self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(dist, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - // No target - no jump - if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && - ( (self->z > target->z && self->z - (target->z + target->height) < dist) || - (self->z <=target->z && target->z - (self->z + self->height) < dist) - ) - ) - { - ACTION_JUMP(jump); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) -{ - AActor *target; - - if (!self->player) - { - target = self->target; - } - else - { - // Does the player aim at something that can be shot? - P_BulletSlope(self, &target); - } - DoJumpIfCloser(target, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) -{ - DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) -{ - DoJumpIfCloser(self->master, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(Type, 0); - ACTION_PARAM_INT(ItemAmount, 1); - ACTION_PARAM_STATE(JumpOffset, 2); - ACTION_PARAM_INT(setowner, 3); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (!Type) return; - COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL - - AInventory *Item = owner->FindInventory(Type); - - if (Item) - { - if (ItemAmount > 0) - { - if (Item->Amount >= ItemAmount) - ACTION_JUMP(JumpOffset); - } - else if (Item->Amount >= Item->MaxAmount) - { - ACTION_JUMP(JumpOffset); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) -{ - DoJumpIfInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) -{ - DoJumpIfInventory(self->target, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(Type, 0); - ACTION_PARAM_STATE(JumpOffset, 1); - ACTION_PARAM_INT(amount, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); - - if (armor && armor->ArmorType == Type && armor->Amount >= amount) - ACTION_JUMP(JumpOffset); -} - -//========================================================================== -// -// Parameterized version of A_Explode -// -//========================================================================== - -enum -{ - XF_HURTSOURCE = 1, - XF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_INT(damage, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_BOOL(alert, 3); - ACTION_PARAM_INT(fulldmgdistance, 4); - ACTION_PARAM_INT(nails, 5); - ACTION_PARAM_INT(naildamage, 6); - ACTION_PARAM_CLASS(pufftype, 7); - - if (damage < 0) // get parameters from metadata - { - damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); - distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); - flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); - alert = false; - } - else - { - if (distance <= 0) distance = damage; - } - // NailBomb effect, from SMMU but not from its source code: instead it was implemented and - // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html - - if (nails) - { - angle_t ang; - for (int i = 0; i < nails; i++) - { - ang = i*(ANGLE_MAX/nails); - // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim - P_LineAttack (self, ang, MISSILERANGE, 0, - //P_AimLineAttack (self, ang, MISSILERANGE), - naildamage, NAME_Hitscan, pufftype); - } - } - - P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); - P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) - { - validcount++; - P_RecursiveSound (self->Sector, self->target, false, 0); - } -} - -//========================================================================== -// -// A_RadiusThrust -// -//========================================================================== - -enum -{ - RTF_AFFECTSOURCE = 1, - RTF_NOIMPACTDAMAGE = 2, - RTF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(force, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(fullthrustdistance, 3); - - bool sourcenothrust = false; - - if (force == 0) force = 128; - if (distance <= 0) distance = abs(force); - - // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. - if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) - { - sourcenothrust = true; - self->target->flags2 &= ~MF2_NODMGTHRUST; - } - - P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); - P_CheckSplash(self, distance << FRACBITS); - - if (sourcenothrust) - { - self->target->flags2 |= MF2_NODMGTHRUST; - } -} - -//========================================================================== -// -// Execute a line special / script -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - ACTION_PARAM_INT(arg5, 5); - - bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); - - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// The ultimate code pointer: Fully customizable missiles! -// -//========================================================================== -enum CM_Flags -{ - CMF_AIMMODE = 3, - CMF_TRACKOWNER = 4, - CMF_CHECKTARGETDEAD = 8, - - CMF_ABSOLUTEPITCH = 16, - CMF_OFFSETPITCH = 32, - CMF_SAVEPITCH = 64, - - CMF_ABSOLUTEANGLE = 128 -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(Spawnofs_XY, 2); - ACTION_PARAM_ANGLE(Angle, 3); - ACTION_PARAM_INT(flags, 4); - ACTION_PARAM_ANGLE(pitch, 5); - ACTION_PARAM_INT(ptr, 6); - - AActor *ref = COPY_AAPTR(self, ptr); - - int aimmode = flags & CMF_AIMMODE; - - AActor * targ; - AActor * missile; - - if (ref != NULL || aimmode==2) - { - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = Spawnofs_XY * finecosine[ang]; - fixed_t y = Spawnofs_XY * finesine[ang]; - fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); - - switch (aimmode) - { - case 0: - default: - // same adjustment as above (in all 3 directions this time) - for better aiming! - self->x += x; - self->y += y; - self->z += z; - missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, ref, ti, false); - self->x -= x; - self->y -= y; - self->z -= z; - break; - - case 1: - missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, ref, ti, false); - break; - - case 2: - self->x += x; - self->y += y; - missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); - self->x -= x; - self->y -= y; - - flags |= CMF_ABSOLUTEPITCH; - - break; - } - - if (missile) - { - // Use the actual velocity instead of the missile's Speed property - // so that this can handle missiles with a high vertical velocity - // component properly. - - fixed_t missilespeed; - - if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) - { - if (CMF_OFFSETPITCH & flags) - { - FVector2 velocity (missile->velx, missile->vely); - pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); - } - ang = pitch >> ANGLETOFINESHIFT; - missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); - missile->velz = FixedMul(finesine[ang], missile->Speed); - } - else - { - FVector2 velocity (missile->velx, missile->vely); - missilespeed = (fixed_t)velocity.Length(); - } - - if (CMF_SAVEPITCH & flags) - { - missile->pitch = pitch; - // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter - // contains the unapplied parameter. In that case, it is set as pitch without - // otherwise affecting the spawned actor. - } - - missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; - - ang = missile->angle >> ANGLETOFINESHIFT; - missile->velx = FixedMul (missilespeed, finecosine[ang]); - missile->vely = FixedMul (missilespeed, finesine[ang]); - - // handle projectile shooting projectiles - track the - // links back to a real owner - if (self->isMissile(!!(flags & CMF_TRACKOWNER))) - { - AActor * owner=self ;//->target; - while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; - targ=owner; - missile->target=owner; - // automatic handling of seeker missiles - if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) - { - missile->tracer=self->tracer; - } - } - else if (missile->flags2&MF2_SEEKERMISSILE) - { - // automatic handling of seeker missiles - missile->tracer=self->target; - } - // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong - if (missile->flags4 & MF4_SPECTRAL) - { - if (missile->target != NULL) - { - missile->SetFriendPlayer(missile->target->player); - } - else - { - missile->FriendPlayer = 0; - } - } - P_CheckMissileSpawn(missile, self->radius); - } - } - } - else if (flags & CMF_CHECKTARGETDEAD) - { - // Target is dead and the attack shall be aborted. - if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); - } -} - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum CBA_Flags -{ - CBAF_AIMFACING = 1, - CBAF_NORANDOM = 2, - CBAF_EXPLICITANGLE = 4, - CBAF_NOPITCH = 8, - CBAF_NORANDOMPUFFZ = 16, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(pufftype, 4); - ACTION_PARAM_FIXED(Range, 5); - ACTION_PARAM_INT(Flags, 6); - ACTION_PARAM_INT(ptr, 7); - - AActor *ref = COPY_AAPTR(self, ptr); - - if(Range==0) Range=MISSILERANGE; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if (ref || (Flags & CBAF_AIMFACING)) - { - if (!(Flags & CBAF_AIMFACING)) - { - A_Face(self, ref); - } - bangle = self->angle; - - if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); - - if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i=0 ; itarget) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else - { - if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); - } -} - -//========================================================================== -// -// A fully customizable combo attack -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(damage, 2); - ACTION_PARAM_SOUND(MeleeSound, 3); - ACTION_PARAM_NAME(DamageType, 4); - ACTION_PARAM_BOOL(bleed, 5); - - if (!self->target) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (ti) - { - // This seemingly senseless code is needed for proper aiming. - self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); - self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (!ACTION_CALL_FROM_WEAPON()) return; - - if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) - { - ACTION_JUMP(jump); - } - -} - - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum FB_Flags -{ - FBF_USEAMMO = 1, - FBF_NORANDOM = 2, - FBF_EXPLICITANGLE = 4, - FBF_NOPITCH = 8, - FBF_NOFLASH = 16, - FBF_NORANDOMPUFFZ = 32, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumberOfBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(PuffType, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FIXED(Range, 6); - - if (!self->player) return; - - player_t * player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if ((Flags & FBF_USEAMMO) && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (Range == 0) Range = PLAYERMISSILERANGE; - - if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); - - if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); - bangle = self->angle; - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) - { - int damage = DamagePerBullet; - - if (!(Flags & FBF_NORANDOM)) - damage *= ((pr_cwbullet()%3)+1); - - P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); - } - else - { - if (NumberOfBullets == -1) NumberOfBullets = 1; - for (i=0 ; iplayer) return; - - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - AActor *linetarget; - - if (UseAmmo && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = SpawnOfs_XY * finecosine[ang]; - fixed_t y = SpawnOfs_XY * finesine[ang]; - fixed_t z = SpawnHeight; - fixed_t shootangle = self->angle; - - if (Flags & FPF_AIMATANGLE) shootangle += Angle; - - // Temporarily adjusts the pitch - fixed_t SavedPlayerPitch = self->pitch; - self->pitch -= pitch; - AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); - self->pitch = SavedPlayerPitch; - - // automatic handling of seeker missiles - if (misl) - { - if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; - if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; - if (!(Flags & FPF_AIMATANGLE)) - { - // This original implementation is to aim straight ahead and then offset - // the angle from the resulting direction. - FVector3 velocity(misl->velx, misl->vely, 0); - fixed_t missilespeed = (fixed_t)velocity.Length(); - misl->angle += Angle; - angle_t an = misl->angle >> ANGLETOFINESHIFT; - misl->velx = FixedMul (missilespeed, finecosine[an]); - misl->vely = FixedMul (missilespeed, finesine[an]); - } - } - } -} - - -//========================================================================== -// -// A_CustomPunch -// -// Berserk is not handled here. That can be done with A_CheckIfInventory -// -//========================================================================== - -enum -{ - CPF_USEAMMO = 1, - CPF_DAGGER = 2, - CPF_PULLIN = 4, - CPF_NORANDOMPUFFZ = 8, - CPF_NOTURN = 16, - CPF_STEALARMOR = 32, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_BOOL(norandom, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(PuffType, 3); - ACTION_PARAM_FIXED(Range, 4); - ACTION_PARAM_FIXED(LifeSteal, 5); - ACTION_PARAM_INT(lifestealmax, 6); - ACTION_PARAM_CLASS(armorbonustype, 7); - - if (!self->player) return; - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - - angle_t angle; - int pitch; - AActor * linetarget; - int actualdamage; - - if (!norandom) Damage *= (pr_cwpunch()%8+1); - - angle = self->angle + (pr_cwpunch.Random2() << 18); - if (Range == 0) Range = MELEERANGE; - pitch = P_AimLineAttack (self, angle, Range, &linetarget); - - // only use ammo when actually hitting something! - if ((flags & CPF_USEAMMO) && linetarget && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); - - P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); - - if (linetarget) - { - if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - { - if (flags & CPF_STEALARMOR) - { - if (!armorbonustype) armorbonustype = PClass::FindClass("ArmorBonus"); - - if (armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))) - { - ABasicArmorBonus *armorbonus = static_cast(Spawn (armorbonustype, 0,0,0, NO_REPLACE)); - armorbonus->SaveAmount *= (actualdamage * LifeSteal) >> FRACBITS; - armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax; - armorbonus->flags |= MF_DROPPED; - armorbonus->ClearCounters(); - - if (!armorbonus->CallTryPickup (self)) - { - armorbonus->Destroy (); - } - } - } - - else - { - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS, lifestealmax); - } - } - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if (!(flags & CPF_NOTURN)) - { - // turn to face target - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); - } - - if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; - if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); - } -} - - -//========================================================================== -// -// customizable railgun attack function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_BOOL(UseAmmo, 2); - ACTION_PARAM_COLOR(Color1, 3); - ACTION_PARAM_COLOR(Color2, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - if (!self->player) return; - - AWeapon * weapon=self->player->ReadyWeapon; - - // only use ammo when actually hitting something! - if (UseAmmo) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - angle_t angle; - angle_t slope; - - if (Flags & RAF_EXPLICITANGLE) - { - angle = Spread_XY; - slope = Spread_Z; - } - else - { - angle = pr_crailgun.Random2() * (Spread_XY / 255); - slope = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); -} - -//========================================================================== -// -// also for monsters -// -//========================================================================== -enum -{ - CRF_DONTAIM = 0, - CRF_AIMPARALLEL = 1, - CRF_AIMDIRECT = 2, - CRF_EXPLICITANGLE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_COLOR(Color1, 2); - ACTION_PARAM_COLOR(Color2, 3); - ACTION_PARAM_INT(Flags, 4); - ACTION_PARAM_INT(aim, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - AActor *linetarget; - - fixed_t saved_x = self->x; - fixed_t saved_y = self->y; - angle_t saved_angle = self->angle; - fixed_t saved_pitch = self->pitch; - - if (aim && self->target == NULL) - { - return; - } - // [RH] Andy Baker's stealth monsters - if (self->flags & MF_STEALTH) - { - self->visdir = 1; - } - - self->flags &= ~MF_AMBUSH; - - - if (aim) - { - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x, - self->target->y); - } - self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); - if (linetarget == NULL && aim) - { - // We probably won't hit the target, but aim at it anyway so we don't look stupid. - FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); - double zdiff = (self->target->z + (self->target->height>>1)) - - (self->z + (self->height>>1) - self->floorclip); - self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); - } - // Let the aim trail behind the player - if (aim) - { - saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - - if (aim == CRF_AIMDIRECT) - { - // Tricky: We must offset to the angle of the current position - // but then change the angle again to ensure proper aim. - self->x += Spawnofs_XY * finecosine[self->angle]; - self->y += Spawnofs_XY * finesine[self->angle]; - Spawnofs_XY = 0; - self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - } - - if (self->target->flags & MF_SHADOW) - { - angle_t rnd = pr_crailgun.Random2() << 21; - self->angle += rnd; - saved_angle = rnd; - } - } - - angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; - - angle_t angleoffset; - angle_t slopeoffset; - - if (Flags & CRF_EXPLICITANGLE) - { - angleoffset = Spread_XY; - slopeoffset = Spread_Z; - } - else - { - angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); - slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); - - self->x = saved_x; - self->y = saved_y; - self->angle = saved_angle; - self->pitch = saved_pitch; -} - -//=========================================================================== -// -// DoGiveInventory -// -//=========================================================================== - -static void DoGiveInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(2+use_aaptr); - ACTION_PARAM_CLASS(mi, 0); - ACTION_PARAM_INT(amount, 1); - - if (use_aaptr) - { - ACTION_PARAM_INT(setreceiver, 2); - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - } - - bool res=true; - - if (amount==0) amount=1; - if (mi) - { - AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); - if (!item) - { - ACTION_SET_RESULT(false); - return; - } - if (item->IsKindOf(RUNTIME_CLASS(AHealth))) - { - item->Amount *= amount; - } - else - { - item->Amount = amount; - } - item->flags |= MF_DROPPED; - item->ClearCounters(); - if (!item->CallTryPickup (receiver)) - { - item->Destroy (); - res = false; - } - else res = true; - } - else res = false; - ACTION_SET_RESULT(res); - -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) -{ - DoGiveInventory(self, true, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) -{ - DoGiveInventory(self->target, true, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) -{ - TThinkerIterator it; - AActor * mo; - - while ((mo = it.Next())) - { - if (mo->master == self) DoGiveInventory(mo, false, PUSH_PARAMINFO); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) -{ - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) DoGiveInventory(mo, false, PUSH_PARAMINFO); - } - } -} - -//=========================================================================== -// -// A_TakeInventory -// -//=========================================================================== - -enum -{ - TIF_NOTAKEINFINITE = 1, -}; - -void DoTakeInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(3+use_aaptr); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(flags, 2); - - if (!item) - { - ACTION_SET_RESULT(false); - return; - } - if (use_aaptr) - { - ACTION_PARAM_INT(setreceiver, 3); - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - } - - bool res = false; - - AInventory * inv = receiver->FindInventory(item); - - if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) - { - if (inv->Amount > 0) - { - res = true; - } - // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set - // and infinite ammo is on - if (flags & TIF_NOTAKEINFINITE && - ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && - inv->IsKindOf(RUNTIME_CLASS(AAmmo))) - { - // Nothing to do here, except maybe res = false;? Would it make sense? - } - else if (!amount || amount>=inv->Amount) - { - if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; - else inv->Destroy(); - } - else inv->Amount-=amount; - } - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) -{ - DoTakeInventory(self, true, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) -{ - DoTakeInventory(self->target, true, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) -{ - TThinkerIterator it; - AActor * mo; - - while ((mo = it.Next())) - { - if (mo->master == self) DoTakeInventory(mo, false, PUSH_PARAMINFO); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) -{ - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) DoTakeInventory(mo, false, PUSH_PARAMINFO); - } - } -} - -//=========================================================================== -// -// Common code for A_SpawnItem and A_SpawnItemEx -// -//=========================================================================== - -enum SIX_Flags -{ - SIXF_TRANSFERTRANSLATION = 1 << 0, - SIXF_ABSOLUTEPOSITION = 1 << 1, - SIXF_ABSOLUTEANGLE = 1 << 2, - SIXF_ABSOLUTEVELOCITY = 1 << 3, - SIXF_SETMASTER = 1 << 4, - SIXF_NOCHECKPOSITION = 1 << 5, - SIXF_TELEFRAG = 1 << 6, - SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum - SIXF_TRANSFERAMBUSHFLAG = 1 << 8, - SIXF_TRANSFERPITCH = 1 << 9, - SIXF_TRANSFERPOINTERS = 1 << 10, - SIXF_USEBLOODCOLOR = 1 << 11, - SIXF_CLEARCALLERTID = 1 << 12, - SIXF_MULTIPLYSPEED = 1 << 13, - SIXF_TRANSFERSCALE = 1 << 14, - SIXF_TRANSFERSPECIAL = 1 << 15, - SIXF_CLEARCALLERSPECIAL = 1 << 16, - SIXF_TRANSFERSTENCILCOL = 1 << 17, - SIXF_TRANSFERALPHA = 1 << 18, - SIXF_TRANSFERRENDERSTYLE = 1 << 19, - SIXF_SETTARGET = 1 << 20, - SIXF_SETTRACER = 1 << 21, - SIXF_NOPOINTERS = 1 << 22, - SIXF_ORIGINATOR = 1 << 23, -}; - -static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) -{ - if (mo == NULL) - { - return false; - } - AActor *originator = self; - - if (!(mo->flags2 & MF2_DONTTRANSLATE)) - { - if (flags & SIXF_TRANSFERTRANSLATION) - { - mo->Translation = self->Translation; - } - else if (flags & SIXF_USEBLOODCOLOR) - { - // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. - PalEntry bloodcolor = self->GetBloodColor(); - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); - } - } - if (flags & SIXF_TRANSFERPOINTERS) - { - mo->target = self->target; - mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set - mo->tracer = self->tracer; - } - - mo->angle = self->angle; - if (flags & SIXF_TRANSFERPITCH) - { - mo->pitch = self->pitch; - } - if (!(flags & SIXF_ORIGINATOR)) - { - while (originator && originator->isMissile()) - { - originator = originator->target; - } - } - if (flags & SIXF_TELEFRAG) - { - P_TeleportMove(mo, mo->x, mo->y, mo->z, true); - // This is needed to ensure consistent behavior. - // Otherwise it will only spawn if nothing gets telefragged - flags |= SIXF_NOCHECKPOSITION; - } - if (mo->flags3 & MF3_ISMONSTER) - { - if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) - { - // The monster is blocked so don't spawn it at all! - mo->ClearCounters(); - mo->Destroy(); - return false; - } - else if (originator && !(flags & SIXF_NOPOINTERS)) - { - if (originator->flags3 & MF3_ISMONSTER) - { - // If this is a monster transfer all friendliness information - mo->CopyFriendliness(originator, true); - } - else if (originator->player) - { - // A player always spawns a monster friendly to him - mo->flags |= MF_FRIENDLY; - mo->SetFriendPlayer(originator->player); - - AActor * attacker=originator->player->attacker; - if (attacker) - { - if (!(attacker->flags&MF_FRIENDLY) || - (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) - { - // Target the monster which last attacked the player - mo->LastHeard = mo->target = attacker; - } - } - } - } - } - else if (!(flags & SIXF_TRANSFERPOINTERS)) - { - // If this is a missile or something else set the target to the originator - mo->target = originator ? originator : self; - } - if (flags & SIXF_NOPOINTERS) - { - //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. - mo->LastHeard = NULL; //Sanity check. - mo->target = NULL; - mo->master = NULL; - mo->tracer = NULL; - } - if (flags & SIXF_SETMASTER) - { - mo->master = originator; - } - if (flags & SIXF_SETTARGET) - { - mo->target = originator; - } - if (flags & SIXF_SETTRACER) - { - mo->tracer = originator; - } - if (flags & SIXF_TRANSFERSCALE) - { - mo->scaleX = self->scaleX; - mo->scaleY = self->scaleY; - } - if (flags & SIXF_TRANSFERAMBUSHFLAG) - { - mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); - } - if (flags & SIXF_CLEARCALLERTID) - { - self->RemoveFromHash(); - self->tid = 0; - } - if (flags & SIXF_TRANSFERSPECIAL) - { - mo->special = self->special; - memcpy(mo->args, self->args, sizeof(self->args)); - } - if (flags & SIXF_CLEARCALLERSPECIAL) - { - self->special = 0; - memset(self->args, 0, sizeof(self->args)); - } - if (flags & SIXF_TRANSFERSTENCILCOL) - { - mo->fillcolor = self->fillcolor; - } - if (flags & SIXF_TRANSFERALPHA) - { - mo->alpha = self->alpha; - } - if (flags & SIXF_TRANSFERRENDERSTYLE) - { - mo->RenderStyle = self->RenderStyle; - } - - return true; -} - -//=========================================================================== -// -// A_SpawnItem -// -// Spawns an item in front of the caller like Heretic's time bomb -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_FIXED(zheight, 2); - ACTION_PARAM_BOOL(useammo, 3); - ACTION_PARAM_BOOL(transfer_translation, 4); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - // Don't spawn monsters if this actor has been massacred - if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - if (distance==0) - { - // use the minimum distance that does not result in an overlap - distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; - } - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon so use some ammo - AWeapon * weapon=self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - AActor * mo = Spawn( missile, - self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), - self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), - self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); - - int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state -} - -//=========================================================================== -// -// A_SpawnItemEx -// -// Enhanced spawning function -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) -{ - ACTION_PARAM_START(11); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_FIXED(xvel, 4); - ACTION_PARAM_FIXED(yvel, 5); - ACTION_PARAM_FIXED(zvel, 6); - ACTION_PARAM_ANGLE(Angle, 7); - ACTION_PARAM_INT(flags, 8); - ACTION_PARAM_INT(chance, 9); - ACTION_PARAM_INT(tid, 10); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - fixed_t x,y; - - if (!(flags & SIXF_ABSOLUTEANGLE)) - { - Angle += self->angle; - } - - angle_t ang = Angle >> ANGLETOFINESHIFT; - - if (flags & SIXF_ABSOLUTEPOSITION) - { - x = self->x + xofs; - y = self->y + yofs; - } - else - { - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); - y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); - } - - if (!(flags & SIXF_ABSOLUTEVELOCITY)) - { - // Same orientation issue here! - fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); - yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); - xvel = newxvel; - } - - AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state - if (res) - { - if (tid != 0) - { - assert(mo->tid == 0); - mo->tid = tid; - mo->AddToHash(); - } - if (flags & SIXF_MULTIPLYSPEED) - { - mo->velx = FixedMul(xvel, mo->Speed); - mo->vely = FixedMul(yvel, mo->Speed); - mo->velz = FixedMul(zvel, mo->Speed); - } - else - { - mo->velx = xvel; - mo->vely = yvel; - mo->velz = zvel; - } - mo->angle = Angle; - } -} - -//=========================================================================== -// -// A_ThrowGrenade -// -// Throws a grenade (like Hexen's fighter flechette) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(zheight, 1); - ACTION_PARAM_FIXED(xyvel, 2); - ACTION_PARAM_FIXED(zvel, 3); - ACTION_PARAM_BOOL(useammo, 4); - - if (missile == NULL) return; - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon, so use some ammo - AWeapon *weapon = self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - - AActor * bo; - - bo = Spawn(missile, self->x, self->y, - self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), - ALLOW_REPLACE); - if (bo) - { - P_PlaySpawnSound(bo, self); - if (xyvel != 0) - bo->Speed = xyvel; - bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); - - angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; - angle_t angle = bo->angle >> ANGLETOFINESHIFT; - - // There are two vectors we are concerned about here: xy and z. We rotate - // them separately according to the shooter's pitch and then sum them to - // get the final velocity vector to shoot with. - - fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); - fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); - fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); - fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); - - pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; - fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); - fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); - fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); - fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); - - bo->velx = xy_velx + z_velx + (self->velx >> 1); - bo->vely = xy_vely + z_vely + (self->vely >> 1); - bo->velz = xy_velz + z_velz; - - bo->target = self; - P_CheckMissileSpawn (bo, self->radius); - } - else ACTION_SET_RESULT(false); -} - - -//=========================================================================== -// -// A_Recoil -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(xyvel, 0); - - angle_t angle = self->angle + ANG180; - angle >>= ANGLETOFINESHIFT; - self->velx += FixedMul (xyvel, finecosine[angle]); - self->vely += FixedMul (xyvel, finesine[angle]); -} - - -//=========================================================================== -// -// A_SelectWeapon -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(cls, 0); - - if (cls == NULL || self->player == NULL) - { - ACTION_SET_RESULT(false); - return; - } - - AWeapon * weaponitem = static_cast(self->FindInventory(cls)); - - if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) - { - if (self->player->ReadyWeapon != weaponitem) - { - self->player->PendingWeapon = weaponitem; - } - } - else ACTION_SET_RESULT(false); - -} - - -//=========================================================================== -// -// A_Print -// -//=========================================================================== -EXTERN_CVAR(Float, con_midtime) - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - if (text[0] == '$') text = GStrings(text+1); - if (self->CheckLocalView (consoleplayer) || - (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) - { - float saved = con_midtime; - FFont *font = NULL; - - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - } - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_PrintBold -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - float saved = con_midtime; - FFont *font = NULL; - - if (text[0] == '$') text = GStrings(text+1); - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_Log -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STRING(text, 0); - - if (text[0] == '$') text = GStrings(text+1); - FString formatted = strbin1(text); - Printf("%s\n", formatted.GetChars()); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//========================================================================= -// -// A_LogInt -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(num, 0); - Printf("%d\n", num); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_SetTranslucent -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(alpha, 0); - ACTION_PARAM_INT(mode, 1); - - mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha = clamp(alpha, 0, FRACUNIT); - self->RenderStyle = ERenderStyle(mode); -} - -//=========================================================================== -// -// A_FadeIn -// -// Fades the actor in -// -//=========================================================================== - -enum FadeFlags -{ - FTF_REMOVE = 1 << 0, - FTF_CLAMP = 1 << 1, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_INT(flags, 1); - - if (reduce == 0) - { - reduce = FRACUNIT / 10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha += reduce; - - if (self->alpha >= (FRACUNIT * 1)) - { - if (flags & FTF_CLAMP) - self->alpha = (FRACUNIT * 1); - if (flags & FTF_REMOVE) - self->Destroy(); - } -} - -//=========================================================================== -// -// A_FadeOut -// -// fades the actor out and destroys it when done -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_INT(flags, 1); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha -= reduce; - if (self->alpha <= 0) - { - if (flags & FTF_CLAMP) - self->alpha = 0; - if (flags & FTF_REMOVE) - self->Destroy(); - } -} - -//=========================================================================== -// -// A_FadeTo -// -// fades the actor to a specified transparency by a specified amount and -// destroys it if so desired -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_FIXED(target, 0); - ACTION_PARAM_FIXED(amount, 1); - ACTION_PARAM_INT(flags, 2); - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - - if (self->alpha > target) - { - self->alpha -= amount; - - if (self->alpha < target) - { - self->alpha = target; - } - } - else if (self->alpha < target) - { - self->alpha += amount; - - if (self->alpha > target) - { - self->alpha = target; - } - } - if (flags & FTF_CLAMP) - { - if (self->alpha > (FRACUNIT * 1)) - self->alpha = (FRACUNIT * 1); - else if (self->alpha < 0) - self->alpha = 0; - } - if (self->alpha == target && (flags & FTF_REMOVE)) - { - self->Destroy(); - } -} - -//=========================================================================== -// -// A_Scale(float scalex, optional float scaley) -// -// Scales the actor's graphics. If scaley is 0, use scalex. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_FIXED(scalex, 0); - ACTION_PARAM_FIXED(scaley, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - ref->scaleX = scalex; - ref->scaleY = scaley ? scaley : scalex; -} - -//=========================================================================== -// -// A_SetMass(int mass) -// -// Sets the actor's mass. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(mass, 0); - - self->Mass = mass; -} - -//=========================================================================== -// -// A_SpawnDebris -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) -{ - int i; - AActor * mo; - - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(debris, 0); - ACTION_PARAM_BOOL(transfer_translation, 1); - ACTION_PARAM_FIXED(mult_h, 2); - ACTION_PARAM_FIXED(mult_v, 3); - - if (debris == NULL) return; - - // only positive values make sense here - if (mult_v<=0) mult_v=FRACUNIT; - if (mult_h<=0) mult_h=FRACUNIT; - - for (i = 0; i < GetDefaultByType(debris)->health; i++) - { - mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), - self->y + ((pr_spawndebris()-128)<<12), - self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); - if (mo) - { - if (transfer_translation) - { - mo->Translation = self->Translation; - } - if (i < mo->GetClass()->ActorInfo->NumOwnedStates) - { - mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); - } - mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); - mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - } - } -} - - -//=========================================================================== -// -// A_CheckSight -// jumps if no player can see this actor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - // Always check sight from each player. - if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) - { - return; - } - // If a player is viewing from a non-player, then check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) - { - return; - } - } - } - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckSightOrRange -// Jumps if this actor is out of range of all players *and* out of sight. -// Useful for maps with many multi-actor special effects. -// -//=========================================================================== -static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool twodi) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height) - { - dz = self->z + self->height - eyez; - } - else if (eyez < self->z) - { - dz = self->z - eyez; - } - else - { - dz = 0; - } - double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); - if (distance <= range){ - // Within range - return true; - } - - // Now check LOS. - if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) - { // Visible - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) -{ - ACTION_PARAM_START(3); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(twodi, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckSightOrRange(self, players[i].mo, range, twodi)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckSightOrRange(self, players[i].camera, range, twodi)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckRange -// Jumps if this actor is out of range of all players. -// -//=========================================================================== -static bool DoCheckRange(AActor *self, AActor *camera, double range, bool twodi) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height){ - dz = self->z + self->height - eyez; - } - else if (eyez < self->z){ - dz = self->z - eyez; - } - else{ - dz = 0; - } - double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); - if (distance <= range){ - // Within range - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) -{ - ACTION_PARAM_START(3); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(twodi, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckRange(self, players[i].mo, range, twodi)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckRange(self, players[i].camera, range, twodi)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - - -//=========================================================================== -// -// Inventory drop -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(drop, 0); - - if (drop) - { - AInventory * inv = self->FindInventory(drop); - if (inv) - { - self->DropInventory(inv); - } - } -} - - -//=========================================================================== -// -// A_SetBlend -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_COLOR(color, 0); - ACTION_PARAM_FLOAT(alpha, 1); - ACTION_PARAM_INT(tics, 2); - ACTION_PARAM_COLOR(color2, 3); - - if (color == MAKEARGB(255,255,255,255)) color=0; - if (color2 == MAKEARGB(255,255,255,255)) color2=0; - if (!color2.a) - color2 = color; - - new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, - color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, - (float)tics/TICRATE, self); -} - - -//=========================================================================== -// -// A_JumpIf -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_BOOL(expression, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (expression) ACTION_JUMP(jump); - -} - -//=========================================================================== -// -// A_CountdownArg -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(cnt, 0); - ACTION_PARAM_STATE(state, 1); - - if (cnt<0 || cnt>=5) return; - if (!self->args[cnt]--) - { - if (self->flags&MF_MISSILE) - { - P_ExplodeMissile(self, NULL, NULL); - } - else if (self->flags&MF_SHOOTABLE) - { - P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); - } - else - { - // can't use "Death" as default parameter with current DECORATE parser. - if (state == NULL) state = self->FindState(NAME_Death); - self->SetState(state); - } - } - -} - -//============================================================================ -// -// A_Burst -// -//============================================================================ - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(chunk, 0); - - int i, numChunks; - AActor * mo; - - if (chunk == NULL) return; - - self->velx = self->vely = self->velz = 0; - self->height = self->GetDefault()->height; - - // [RH] In Hexen, this creates a random number of shards (range [24,56]) - // with no relation to the size of the self shattering. I think it should - // base the number of shards on the size of the dead thing, so bigger - // things break up into more shards than smaller things. - // An self with radius 20 and height 64 creates ~40 chunks. - numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); - i = (pr_burst.Random2()) % (numChunks/4); - for (i = MAX (24, numChunks + i); i >= 0; i--) - { - mo = Spawn(chunk, - self->x + (((pr_burst()-128)*self->radius)>>7), - self->y + (((pr_burst()-128)*self->radius)>>7), - self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); - - if (mo) - { - mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; - mo->velx = pr_burst.Random2 () << (FRACBITS-7); - mo->vely = pr_burst.Random2 () << (FRACBITS-7); - mo->RenderStyle = self->RenderStyle; - mo->alpha = self->alpha; - mo->CopyFriendliness(self, true); - } - } - - // [RH] Do some stuff to make this more useful outside Hexen - if (self->flags4 & MF4_BOSSDEATH) - { - CALL_ACTION(A_BossDeath, self); - } - A_Unblock(self, true); - - self->Destroy (); -} - -//=========================================================================== -// -// A_CheckFloor -// [GRB] Jumps if actor is standing on floor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->z <= self->floorz) - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_CheckCeiling -// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling -// - -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); - if (self->z+self->height >= self->ceilingz) // Height needs to be counted - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_Stop -// resets all velocity of the actor to 0 -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_Stop) -{ - self->velx = self->vely = self->velz = 0; - if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -static void CheckStopped(AActor *self) -{ - if (self->player != NULL && - self->player->mo == self && - !(self->player->cheats & CF_PREDICTING) && - !(self->velx | self->vely | self->velz)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -//=========================================================================== -// -// A_Respawn -// -//=========================================================================== - -extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); - -enum RS_Flags -{ - RSF_FOG=1, - RSF_KEEPTARGET=2, - RSF_TELEFRAG=4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(flags, 0); - bool oktorespawn = false; - fixed_t oldx = self->x; - fixed_t oldy = self->y; - fixed_t oldz = self->z; - self->flags |= MF_SOLID; - self->height = self->GetDefault()->height; - CALL_ACTION(A_RestoreSpecialPosition, self); - - if (flags & RSF_TELEFRAG) - { - // [KS] DIE DIE DIE DIE erm *ahem* =) - oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); - if (oktorespawn) - { // Need to do this over again, since P_TeleportMove() will redo - // it with the proper point-on-side calculation. - self->UnlinkFromWorld(); - self->LinkToWorld(true); - sector_t *sec = self->Sector; - self->dropoffz = - self->floorz = sec->floorplane.ZatPoint(self->x, self->y); - self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); - } - } - else - { - oktorespawn = P_CheckPosition(self, self->x, self->y, true); - } - - if (oktorespawn) - { - AActor *defs = self->GetDefault(); - self->health = defs->health; - - // [KS] Don't keep target, because it could be self if the monster committed suicide - // ...Actually it's better off an option, so you have better control over monster behavior. - if (!(flags & RSF_KEEPTARGET)) - { - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; - } - else - { - // Don't attack yourself (Re: "Marine targets itself after suicide") - if (self->target == self) self->target = NULL; - if (self->lastenemy == self) self->lastenemy = NULL; - } - - self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); - self->flags2 = defs->flags2; - self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); - self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); - self->flags5 = defs->flags5; - self->flags6 = defs->flags6; - self->flags7 = defs->flags7; - self->SetState (self->SpawnState); - self->renderflags &= ~RF_INVISIBLE; - - if (flags & RSF_FOG) - { - P_SpawnTeleportFog(self, oldx, oldy, oldz, true); - P_SpawnTeleportFog(self, self->x, self->y, self->z, false); - } - if (self->CountsAsKill()) - { - level.total_monsters++; - } - } - else - { - self->flags &= ~MF_SOLID; - } -} - - -//========================================================================== -// -// A_PlayerSkinCheck -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->player != NULL && - skins[self->player->userinfo.GetSkin()].othergame) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetGravity -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(val, 0); - - self->gravity = clamp (val, 0, FRACUNIT*10); -} - - -// [KS] *** Start of my modifications *** - -//=========================================================================== -// -// A_ClearTarget -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) -{ - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; -} - -//========================================================================== -// -// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, -// fixed range = 0, angle angle = 0, angle pitch = 0, -// fixed offsetheight = 32, fixed offsetwidth = 0, -// int ptr_target = AAPTR_DEFAULT (target) ) -// -//========================================================================== - -enum CLOF_flags -{ - CLOFF_NOAIM_VERT = 0x00000001, - CLOFF_NOAIM_HORZ = 0x00000002, - - CLOFF_JUMPENEMY = 0x00000004, - CLOFF_JUMPFRIEND = 0x00000008, - CLOFF_JUMPOBJECT = 0x00000010, - CLOFF_JUMPNONHOSTILE = 0x00000020, - - CLOFF_SKIPENEMY = 0x00000040, - CLOFF_SKIPFRIEND = 0x00000080, - CLOFF_SKIPOBJECT = 0x00000100, - CLOFF_SKIPNONHOSTILE = 0x00000200, - - CLOFF_MUSTBESHOOTABLE = 0x00000400, - - CLOFF_SKIPTARGET = 0x00000800, - CLOFF_ALLOWNULL = 0x00001000, - CLOFF_CHECKPARTIAL = 0x00002000, - - CLOFF_MUSTBEGHOST = 0x00004000, - CLOFF_IGNOREGHOST = 0x00008000, - - CLOFF_MUSTBESOLID = 0x00010000, - CLOFF_BEYONDTARGET = 0x00020000, - - CLOFF_FROMBASE = 0x00040000, - CLOFF_MUL_HEIGHT = 0x00080000, - CLOFF_MUL_WIDTH = 0x00100000, - - CLOFF_JUMP_ON_MISS = 0x00200000, - CLOFF_AIM_VERT_NOOFFSET = 0x00400000, - - CLOFF_SETTARGET = 0x00800000, - CLOFF_SETMASTER = 0x01000000, - CLOFF_SETTRACER = 0x02000000, -}; - -struct LOFData -{ - AActor *Self; - AActor *Target; - int Flags; - bool BadActor; -}; - -ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) -{ - LOFData *data = (LOFData *)userdata; - int flags = data->Flags; - - if (trace.HitType != TRACE_HitActor) - { - return TRACE_Stop; - } - if (trace.Actor == data->Target) - { - if (flags & CLOFF_SKIPTARGET) - { - if (flags & CLOFF_BEYONDTARGET) - { - return TRACE_Skip; - } - return TRACE_Abort; - } - return TRACE_Stop; - } - if (flags & CLOFF_MUSTBESHOOTABLE) - { // all shootability checks go here - if (!(trace.Actor->flags & MF_SHOOTABLE)) - { - return TRACE_Skip; - } - if (trace.Actor->flags2 & MF2_NONSHOOTABLE) - { - return TRACE_Skip; - } - } - if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) - { - return TRACE_Skip; - } - if (flags & CLOFF_MUSTBEGHOST) - { - if (!(trace.Actor->flags3 & MF3_GHOST)) - { - return TRACE_Skip; - } - } - else if (flags & CLOFF_IGNOREGHOST) - { - if (trace.Actor->flags3 & MF3_GHOST) - { - return TRACE_Skip; - } - } - if ( - ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Stop; - } - if ( - ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Skip; - } - data->BadActor = true; - return TRACE_Abort; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) -{ - // Check line of fire - - /* - Not accounted for / I don't know how it works: FLOORCLIP - */ - - AActor *target; - fixed_t - x1, y1, z1, - vx, vy, vz; - - ACTION_PARAM_START(9); - - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_FIXED(range, 2); - ACTION_PARAM_FIXED(minrange, 3); - { - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_ANGLE(pitch, 5); - ACTION_PARAM_FIXED(offsetheight, 6); - ACTION_PARAM_FIXED(offsetwidth, 7); - ACTION_PARAM_INT(ptr_target, 8); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default - - if (flags & CLOFF_MUL_HEIGHT) - { - if (self->player != NULL) - { - // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player - offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); - } - else - { - offsetheight = FixedMul(offsetheight, self->height); - } - } - if (flags & CLOFF_MUL_WIDTH) - { - offsetwidth = FixedMul(self->radius, offsetwidth); - } - - x1 = self->x; - y1 = self->y; - z1 = self->z + offsetheight - self->floorclip; - - if (!(flags & CLOFF_FROMBASE)) - { // default to hitscan origin - - // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player - z1 += (self->height >> 1); - if (self->player != NULL) - { - z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); - } - else - { - z1 += 8*FRACUNIT; - } - } - - if (target) - { - FVector2 xyvec(target->x - x1, target->y - y1); - fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); - - if (range && !(flags & CLOFF_CHECKPARTIAL)) - { - if (distance > range) return; - } - - { - angle_t ang; - - if (flags & CLOFF_NOAIM_HORZ) - { - ang = self->angle; - } - else ang = R_PointToAngle2 (x1, y1, target->x, target->y); - - angle += ang; - - ang >>= ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - - if (flags & CLOFF_NOAIM_VERT) - { - pitch += self->pitch; - } - else if (flags & CLOFF_AIM_VERT_NOOFFSET) - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); - } - else - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); - } - } - else if (flags & CLOFF_ALLOWNULL) - { - angle += self->angle; - pitch += self->pitch; - - angle_t ang = self->angle >> ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - else return; - - angle >>= ANGLETOFINESHIFT; - pitch = (0-pitch)>>ANGLETOFINESHIFT; - - vx = FixedMul (finecosine[pitch], finecosine[angle]); - vy = FixedMul (finecosine[pitch], finesine[angle]); - vz = -finesine[pitch]; - } - - /* Variable set: - - jump, flags, target - x1,y1,z1 (trace point of origin) - vx,vy,vz (trace unit vector) - range - */ - - sector_t *sec = P_PointInSector(x1, y1); - - if (range == 0) - { - range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; - } - - FTraceResults trace; - LOFData lof_data; - - lof_data.Self = self; - lof_data.Target = target; - lof_data.Flags = flags; - lof_data.BadActor = false; - - Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, - CheckLOFTraceFunc, &lof_data); - - if (trace.HitType == TRACE_HitActor || - ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) - { - if (minrange > 0 && trace.Distance < minrange) - { - return; - } - if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor)) - { - if (flags & (CLOFF_SETTARGET)) self->target = trace.Actor; - if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor; - if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor; - } - - ACTION_JUMP(jump); - } -} - -//========================================================================== -// -// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, -// optional fixed dist_max, optional fixed dist_close) -// -// Jumps if the actor can see its target, or if the player has a linetarget. -// ProjectileTarget affects how projectiles are treated. If set, it will use -// the target of the projectile for seekers, and ignore the target for -// normal projectiles. If not set, it will use the missile's owner instead -// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max -// sets the maximum distance that actor can see, 0 means forever. dist_close -// uses special behavior if certain flags are set, 0 means no checks. -// -//========================================================================== - -enum JLOS_flags -{ - JLOSF_PROJECTILE = 1, - JLOSF_NOSIGHT = 1 << 1, - JLOSF_CLOSENOFOV = 1 << 2, - JLOSF_CLOSENOSIGHT = 1 << 3, - JLOSF_CLOSENOJUMP = 1 << 4, - JLOSF_DEADNOJUMP = 1 << 5, - JLOSF_CHECKMASTER = 1 << 6, - JLOSF_TARGETLOS = 1 << 7, - JLOSF_FLIPFOV = 1 << 8, - JLOSF_ALLYNOJUMP = 1 << 9, - JLOSF_COMBATANTONLY = 1 << 10, - JLOSF_NOAUTOAIM = 1 << 11, - JLOSF_CHECKTRACER = 1 << 12, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target, *viewport; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - bool doCheckSight; - - if (!self->player) - { - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) - { - if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - doCheckSight = !(flags & JLOSF_NOSIGHT); - } - else - { - // Does the player aim at something that can be shot? - P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); - - if (!target) return; - - switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) - { - case JLOSF_TARGETLOS|JLOSF_FLIPFOV: - // target makes sight check, player makes fov check; player has verified fov - fov = 0; - // fall-through - case JLOSF_TARGETLOS: - doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov - break; - default: - // player has verified sight and fov - fov = 0; - // fall-through - case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov - doCheckSight = false; - break; - } - } - - // [FDARI] If target is not a combatant, don't jump - if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; - - // [FDARI] If actors share team, don't jump - if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } - else { viewport = self; } - - if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) - return; - - if (flags & JLOSF_FLIPFOV) - { - if (viewport == self) { viewport = target; target = self; } - else { target = viewport; viewport = self; } - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (viewport->x, - viewport->y, - target->x, - target->y) - - viewport->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - - } - - ACTION_JUMP(jump); -} - - -//========================================================================== -// -// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags -// optional fixed dist_max, optional fixed dist_close) -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) - { - if (self->flags2 & MF2_SEEKERMISSILE) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - bool doCheckSight = !(flags & JLOSF_NOSIGHT); - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (target->x, - target->y, - self->x, - self->y) - - target->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - } - - if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) - return; - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// Modified code pointer from Skulltag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - ACTION_PARAM_START(2); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(dontincrement, 2) - - if (count <= 0) return; - - AWeapon *weapon = self->player->ReadyWeapon; - - int ReloadCounter = weapon->ReloadCounter; - if(!dontincrement || ReloadCounter != 0) - ReloadCounter = (weapon->ReloadCounter+1) % count; - else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. - ReloadCounter = 1; - - // If we have not made our last shot... - if (ReloadCounter != 0) - { - // Go back to the refire frames, instead of continuing on to the reload frames. - ACTION_JUMP(jump); - } - else - { - // We need to reload. However, don't reload if we're out of ammo. - weapon->CheckAmmo( false, false ); - } - - if(!dontincrement) - weapon->ReloadCounter = ReloadCounter; -} - -//=========================================================================== -// -// Resets the counter for the above function -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - AWeapon *weapon = self->player->ReadyWeapon; - weapon->ReloadCounter = 0; -} - -//=========================================================================== -// -// A_ChangeFlag -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_BOOL(expression, 1); - - const char *dot = strchr (flagname, '.'); - FFlagDef *fd; - const PClass *cls = self->GetClass(); - - if (dot != NULL) - { - FString part1(flagname, dot-flagname); - fd = FindFlag (cls, part1, dot+1); - } - else - { - fd = FindFlag (cls, flagname, NULL); - } - - if (fd != NULL) - { - bool kill_before, kill_after; - INTBOOL item_before, item_after; - INTBOOL secret_before, secret_after; - - kill_before = self->CountsAsKill(); - item_before = self->flags & MF_COUNTITEM; - secret_before = self->flags5 & MF5_COUNTSECRET; - - if (fd->structoffset == -1) - { - HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); - } - else - { - DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); - - // If these 2 flags get changed we need to update the blockmap and sector links. - bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); - - if (linkchange) self->UnlinkFromWorld(); - ModActorFlag(self, fd, expression); - if (linkchange) self->LinkToWorld(); - } - kill_after = self->CountsAsKill(); - item_after = self->flags & MF_COUNTITEM; - secret_after = self->flags5 & MF5_COUNTSECRET; - // Was this monster previously worth a kill but no longer is? - // Or vice versa? - if (kill_before != kill_after) - { - if (kill_after) - { // It counts as a kill now. - level.total_monsters++; - } - else - { // It no longer counts as a kill. - level.total_monsters--; - } - } - // same for items - if (item_before != item_after) - { - if (item_after) - { // It counts as an item now. - level.total_items++; - } - else - { // It no longer counts as an item - level.total_items--; - } - } - // and secretd - if (secret_before != secret_after) - { - if (secret_after) - { // It counts as an secret now. - level.total_secrets++; - } - else - { // It no longer counts as an secret - level.total_secrets--; - } - } - } - else - { - Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); - } -} - -//=========================================================================== -// -// A_CheckFlag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_STATE(jumpto, 1); - ACTION_PARAM_INT(checkpointer, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - AActor *owner; - - COPY_AAPTR_NOT_NULL(self, owner, checkpointer); - - if (CheckActorFlag(owner, flagname)) - { - ACTION_JUMP(jumpto); - } -} - -//=========================================================================== -// -// A_RaiseMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - - if (self->master != NULL) - { - if (copy) - P_Thing_Raise(self->master, self); - else - P_Thing_Raise(self->master, NULL); - } -} - -//=========================================================================== -// -// A_RaiseChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - TThinkerIterator it; - AActor *mo; - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self) - { - if (copy) - P_Thing_Raise(mo, self); - else - P_Thing_Raise(mo, NULL); - } - } -} - -//=========================================================================== -// -// A_RaiseSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(copy, 0); - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self) - { - if (copy) - P_Thing_Raise(mo, self); - else - P_Thing_Raise(mo, NULL); - } - } - } -} - -//=========================================================================== -// -// [TP] A_FaceConsolePlayer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { - ACTION_PARAM_START (1); - ACTION_PARAM_ANGLE (MaxTurnAngle, 0); - // NOTE: It does nothing for zdoom. -} - -//=========================================================================== -// -// A_MonsterRefire -// -// Keep firing unless target got out of sight -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(prob, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - A_FaceTarget (self); - - if (pr_monsterrefire() < prob) - return; - - if (!self->target - || P_HitFriend (self) - || self->target->health <= 0 - || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetAngle -// -// Set actor's angle (in degrees). -// -//=========================================================================== -enum -{ - SPF_FORCECLAMP = 1, // players always clamp - SPF_INTERPOLATE = 2, -}; - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_ANGLE(angle, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_SetPitch -// -// Set actor's pitch (in degrees). -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_ANGLE(pitch, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - if (ref->player != NULL || (flags & SPF_FORCECLAMP)) - { // clamp the pitch we set - int min, max; - - if (ref->player != NULL) - { - min = ref->player->MinPitch; - max = ref->player->MaxPitch; - } - else - { - min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); - max = ANGLE_90 - (1 << ANGLETOFINESHIFT); - } - pitch = clamp(pitch, min, max); - } - ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); -} - +/* +** thingdef.cpp +** +** Code pointers for Actor definitions +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Christoph Oelckers +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by 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 SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gi.h" +#include "g_level.h" +#include "actor.h" +#include "info.h" +#include "sc_man.h" +#include "tarray.h" +#include "w_wad.h" +#include "templates.h" +#include "r_defs.h" +#include "a_pickups.h" +#include "s_sound.h" +#include "cmdlib.h" +#include "p_lnspec.h" +#include "p_enemy.h" +#include "a_action.h" +#include "decallib.h" +#include "m_random.h" +#include "i_system.h" +#include "p_local.h" +#include "c_console.h" +#include "doomerrors.h" +#include "a_sharedglobal.h" +#include "thingdef/thingdef.h" +#include "v_video.h" +#include "v_font.h" +#include "doomstat.h" +#include "v_palette.h" +#include "g_shared/a_specialspot.h" +#include "actorptrselect.h" +#include "m_bbox.h" +#include "r_data/r_translate.h" +#include "p_trace.h" +#include "gstrings.h" + + +static FRandom pr_camissile ("CustomActorfire"); +static FRandom pr_camelee ("CustomMelee"); +static FRandom pr_cabullet ("CustomBullet"); +static FRandom pr_cajump ("CustomJump"); +static FRandom pr_cwbullet ("CustomWpBullet"); +static FRandom pr_cwjump ("CustomWpJump"); +static FRandom pr_cwpunch ("CustomWpPunch"); +static FRandom pr_grenade ("ThrowGrenade"); +static FRandom pr_crailgun ("CustomRailgun"); +static FRandom pr_spawndebris ("SpawnDebris"); +static FRandom pr_spawnitemex ("SpawnItemEx"); +static FRandom pr_burst ("Burst"); +static FRandom pr_monsterrefire ("MonsterRefire"); +static FRandom pr_teleport("A_Teleport"); + +//========================================================================== +// +// ACustomInventory :: CallStateChain +// +// Executes the code pointers in a chain of states +// until there is no next state +// +//========================================================================== + +bool ACustomInventory::CallStateChain (AActor *actor, FState * State) +{ + StateCallData StateCall; + bool result = false; + int counter = 0; + + while (State != NULL) + { + // Assume success. The code pointer will set this to false if necessary + StateCall.State = State; + StateCall.Result = true; + if (State->CallAction(actor, this, &StateCall)) + { + // collect all the results. Even one successful call signifies overall success. + result |= StateCall.Result; + } + + + // Since there are no delays it is a good idea to check for infinite loops here! + counter++; + if (counter >= 10000) break; + + if (StateCall.State == State) + { + // Abort immediately if the state jumps to itself! + if (State == State->GetNextState()) + { + return false; + } + + // If both variables are still the same there was no jump + // so we must advance to the next state. + State = State->GetNextState(); + } + else + { + State = StateCall.State; + } + } + return result; +} + +//========================================================================== +// +// A_RearrangePointers +// +// Allow an actor to change its relationship to other actors by +// copying pointers freely between TARGET MASTER and TRACER. +// Can also assign null value, but does not duplicate A_ClearTarget. +// +//========================================================================== + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(ptr_target, 0); + ACTION_PARAM_INT(ptr_master, 1); + ACTION_PARAM_INT(ptr_tracer, 2); + ACTION_PARAM_INT(flags, 3); + + // Rearrange pointers internally + + // Fetch all values before modification, so that all fields can get original values + AActor + *gettarget = self->target, + *getmaster = self->master, + *gettracer = self->tracer; + + switch (ptr_target) // pick the new target + { + case AAPTR_MASTER: + self->target = getmaster; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_TRACER: + self->target = gettracer; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_NULL: + self->target = NULL; + // THIS IS NOT "A_ClearTarget", so no other targeting info is removed + break; + } + + // presently permitting non-monsters to set master + switch (ptr_master) // pick the new master + { + case AAPTR_TARGET: + self->master = gettarget; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_TRACER: + self->master = gettracer; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_NULL: + self->master = NULL; + break; + } + + switch (ptr_tracer) // pick the new tracer + { + case AAPTR_TARGET: + self->tracer = gettarget; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_MASTER: + self->tracer = getmaster; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_NULL: + self->tracer = NULL; + break; + } +} + +//========================================================================== +// +// A_TransferPointer +// +// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), +// or from this actor's MASTER, TARGET or TRACER. +// +// You can copy any one of that actor's pointers +// +// Assign the copied pointer to any one pointer in SELF, +// MASTER, TARGET or TRACER. +// +// Any attempt to make an actor point to itself will replace the pointer +// with a null value. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ptr_source, 0); + ACTION_PARAM_INT(ptr_recepient, 1); + ACTION_PARAM_INT(ptr_sourcefield, 2); + ACTION_PARAM_INT(ptr_recepientfield, 3); + ACTION_PARAM_INT(flags, 4); + + AActor *source, *recepient; + + // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) + + source = COPY_AAPTR(self, ptr_source); + COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value + + // convert source from dataprovider to data + + source = COPY_AAPTR(source, ptr_sourcefield); + + if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL + + if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from + + ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); +} + +//========================================================================== +// +// A_CopyFriendliness +// +// Join forces with one of the actors you are pointing to (MASTER by default) +// +// Normal CopyFriendliness reassigns health. This function will not. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr_source, 0); + + if (self->player) return; + + AActor *source; + COPY_AAPTR_NOT_NULL(self, source, ptr_source); + self->CopyFriendliness(source, false, false); // No change in current target or health +} + +//========================================================================== +// +// Simple flag changers +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) +{ + self->flags |= MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) +{ + self->flags &= ~MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) +{ + self->flags |= MF_FLOAT; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) +{ + self->flags &= ~(MF_FLOAT|MF_INFLOAT); +} + +//========================================================================== +// +// Customizable attack functions which use actor parameters. +// +//========================================================================== +static void DoAttack (AActor *self, bool domelee, bool domissile, + int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) +{ + if (self->target == NULL) return; + + A_FaceTarget (self); + if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) + { + int damage = pr_camelee.HitDice(MeleeDamage); + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); + P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (domissile && MissileType != NULL) + { + // This seemingly senseless code is needed for proper aiming. + self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); + self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); +} + +DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) +{ + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(MeleeDamage, 0); + ACTION_PARAM_SOUND(MeleeSound, 1); + ACTION_PARAM_CLASS(MissileType, 2); + ACTION_PARAM_FIXED(MissileHeight, 3); + + if (MissileType == NULL) return; + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +//========================================================================== +// +// Custom sound functions. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_INT(channel, 1); + ACTION_PARAM_FLOAT(volume, 2); + ACTION_PARAM_BOOL(looping, 3); + ACTION_PARAM_FLOAT(attenuation, 4); + + if (!looping) + { + S_Sound (self, channel, soundid, volume, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, channel&7, soundid)) + { + S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(slot, 0); + + S_StopSound(self, slot); +} + +//========================================================================== +// +// These come from a time when DECORATE constants did not exist yet and +// the sound interface was less flexible. As a result the parameters are +// not optimal and these functions have been deprecated in favor of extending +// A_PlaySound and A_StopSound. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_SOUND(soundid, 0); + + S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_NAME(channel, 1); + ACTION_PARAM_BOOL(looping, 2); + ACTION_PARAM_INT(attenuation_raw, 3); + + float attenuation; + switch (attenuation_raw) + { + case -1: attenuation = ATTN_STATIC; break; // drop off rapidly + default: + case 0: attenuation = ATTN_NORM; break; // normal + case 1: + case 2: attenuation = ATTN_NONE; break; // full volume + } + + if (channel < NAME_Auto || channel > NAME_SoundSlot7) + { + channel = NAME_Auto; + } + + if (!looping) + { + S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) + { + S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(channel, 0); + + if (channel > NAME_Auto && channel <= NAME_SoundSlot7) + { + S_StopSound (self, int(channel) - NAME_Auto); + } +} + +//========================================================================== +// +// Generic seeker missile function +// +//========================================================================== +static FRandom pr_seekermissile ("SeekerMissile"); +enum +{ + SMF_LOOK = 1, + SMF_PRECISE = 2, + SMF_CURSPEED = 4, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ang1, 0); + ACTION_PARAM_INT(ang2, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(chance, 3); + ACTION_PARAM_INT(distance, 4); + + if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); + } + if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) + { + if (flags & SMF_LOOK) + { // This monster is no longer seekable, so let us look for another one next time. + self->tracer = NULL; + } + } +} + +//========================================================================== +// +// Hitscan attack with a customizable amount of bullets (specified in damage) +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) +{ + int i; + int bangle; + int slope; + + if (!self->target) return; + + A_FaceTarget (self); + bangle = self->angle; + + slope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i = self->GetMissileDamage (0, 1); i > 0; --i) + { + int angle = bangle + (pr_cabullet.Random2() << 20); + int damage = ((pr_cabullet()%5)+1)*3; + P_LineAttack(self, angle, MISSILERANGE, slope, damage, + NAME_Hitscan, NAME_BulletPuff); + } +} + + +//========================================================================== +// +// Do the state jump +// +//========================================================================== +static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) +{ + if (jumpto == NULL) return; + + if (statecall != NULL) + { + statecall->State = jumpto; + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) + { + P_SetPsprite(self->player, ps_weapon, jumpto); + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) + { + P_SetPsprite(self->player, ps_flash, jumpto); + } + else if (CallingState == self->state) + { + self->SetState (jumpto); + } + else + { + // something went very wrong. This should never happen. + assert(false); + } +} + +// This is just to avoid having to directly reference the internally defined +// CallingState and statecall parameters in the code below. +#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_INT(maxchance, 1); + + if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) + { + int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); + ACTION_PARAM_STATE(jumpto, jumps); + ACTION_JUMP(jumpto); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_INT(ptr_selector, 2); + + AActor *measured; + + measured = COPY_AAPTR(self, ptr_selector); + + if (measured && measured->health < health) + { + ACTION_JUMP(jump); + } + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (!self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(dist, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + // No target - no jump + if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && + ( (self->z > target->z && self->z - (target->z + target->height) < dist) || + (self->z <=target->z && target->z - (self->z + self->height) < dist) + ) + ) + { + ACTION_JUMP(jump); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) +{ + AActor *target; + + if (!self->player) + { + target = self->target; + } + else + { + // Does the player aim at something that can be shot? + P_BulletSlope(self, &target); + } + DoJumpIfCloser(target, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) +{ + DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) +{ + DoJumpIfCloser(self->master, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(Type, 0); + ACTION_PARAM_INT(ItemAmount, 1); + ACTION_PARAM_STATE(JumpOffset, 2); + ACTION_PARAM_INT(setowner, 3); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (!Type) return; + COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL + + AInventory *Item = owner->FindInventory(Type); + + if (Item) + { + if (ItemAmount > 0) + { + if (Item->Amount >= ItemAmount) + ACTION_JUMP(JumpOffset); + } + else if (Item->Amount >= Item->MaxAmount) + { + ACTION_JUMP(JumpOffset); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) +{ + DoJumpIfInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) +{ + DoJumpIfInventory(self->target, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(Type, 0); + ACTION_PARAM_STATE(JumpOffset, 1); + ACTION_PARAM_INT(amount, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); + + if (armor && armor->ArmorType == Type && armor->Amount >= amount) + ACTION_JUMP(JumpOffset); +} + +//========================================================================== +// +// Parameterized version of A_Explode +// +//========================================================================== + +enum +{ + XF_HURTSOURCE = 1, + XF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_INT(damage, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_BOOL(alert, 3); + ACTION_PARAM_INT(fulldmgdistance, 4); + ACTION_PARAM_INT(nails, 5); + ACTION_PARAM_INT(naildamage, 6); + ACTION_PARAM_CLASS(pufftype, 7); + + if (damage < 0) // get parameters from metadata + { + damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); + distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); + flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); + alert = false; + } + else + { + if (distance <= 0) distance = damage; + } + // NailBomb effect, from SMMU but not from its source code: instead it was implemented and + // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html + + if (nails) + { + angle_t ang; + for (int i = 0; i < nails; i++) + { + ang = i*(ANGLE_MAX/nails); + // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim + P_LineAttack (self, ang, MISSILERANGE, 0, + //P_AimLineAttack (self, ang, MISSILERANGE), + naildamage, NAME_Hitscan, pufftype); + } + } + + P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); + P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) + { + validcount++; + P_RecursiveSound (self->Sector, self->target, false, 0); + } +} + +//========================================================================== +// +// A_RadiusThrust +// +//========================================================================== + +enum +{ + RTF_AFFECTSOURCE = 1, + RTF_NOIMPACTDAMAGE = 2, + RTF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(force, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(fullthrustdistance, 3); + + bool sourcenothrust = false; + + if (force == 0) force = 128; + if (distance <= 0) distance = abs(force); + + // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. + if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) + { + sourcenothrust = true; + self->target->flags2 &= ~MF2_NODMGTHRUST; + } + + P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); + P_CheckSplash(self, distance << FRACBITS); + + if (sourcenothrust) + { + self->target->flags2 |= MF2_NODMGTHRUST; + } +} + +//========================================================================== +// +// Execute a line special / script +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + ACTION_PARAM_INT(arg5, 5); + + bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); + + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// The ultimate code pointer: Fully customizable missiles! +// +//========================================================================== +enum CM_Flags +{ + CMF_AIMMODE = 3, + CMF_TRACKOWNER = 4, + CMF_CHECKTARGETDEAD = 8, + + CMF_ABSOLUTEPITCH = 16, + CMF_OFFSETPITCH = 32, + CMF_SAVEPITCH = 64, + + CMF_ABSOLUTEANGLE = 128 +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(Spawnofs_XY, 2); + ACTION_PARAM_ANGLE(Angle, 3); + ACTION_PARAM_INT(flags, 4); + ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_INT(ptr, 6); + + AActor *ref = COPY_AAPTR(self, ptr); + + int aimmode = flags & CMF_AIMMODE; + + AActor * targ; + AActor * missile; + + if (ref != NULL || aimmode==2) + { + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = Spawnofs_XY * finecosine[ang]; + fixed_t y = Spawnofs_XY * finesine[ang]; + fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); + + switch (aimmode) + { + case 0: + default: + // same adjustment as above (in all 3 directions this time) - for better aiming! + self->x += x; + self->y += y; + self->z += z; + missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, ref, ti, false); + self->x -= x; + self->y -= y; + self->z -= z; + break; + + case 1: + missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, ref, ti, false); + break; + + case 2: + self->x += x; + self->y += y; + missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); + self->x -= x; + self->y -= y; + + flags |= CMF_ABSOLUTEPITCH; + + break; + } + + if (missile) + { + // Use the actual velocity instead of the missile's Speed property + // so that this can handle missiles with a high vertical velocity + // component properly. + + fixed_t missilespeed; + + if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) + { + if (CMF_OFFSETPITCH & flags) + { + FVector2 velocity (missile->velx, missile->vely); + pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); + } + ang = pitch >> ANGLETOFINESHIFT; + missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); + missile->velz = FixedMul(finesine[ang], missile->Speed); + } + else + { + FVector2 velocity (missile->velx, missile->vely); + missilespeed = (fixed_t)velocity.Length(); + } + + if (CMF_SAVEPITCH & flags) + { + missile->pitch = pitch; + // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter + // contains the unapplied parameter. In that case, it is set as pitch without + // otherwise affecting the spawned actor. + } + + missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; + + ang = missile->angle >> ANGLETOFINESHIFT; + missile->velx = FixedMul (missilespeed, finecosine[ang]); + missile->vely = FixedMul (missilespeed, finesine[ang]); + + // handle projectile shooting projectiles - track the + // links back to a real owner + if (self->isMissile(!!(flags & CMF_TRACKOWNER))) + { + AActor * owner=self ;//->target; + while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; + targ=owner; + missile->target=owner; + // automatic handling of seeker missiles + if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) + { + missile->tracer=self->tracer; + } + } + else if (missile->flags2&MF2_SEEKERMISSILE) + { + // automatic handling of seeker missiles + missile->tracer=self->target; + } + // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong + if (missile->flags4 & MF4_SPECTRAL) + { + if (missile->target != NULL) + { + missile->SetFriendPlayer(missile->target->player); + } + else + { + missile->FriendPlayer = 0; + } + } + P_CheckMissileSpawn(missile, self->radius); + } + } + } + else if (flags & CMF_CHECKTARGETDEAD) + { + // Target is dead and the attack shall be aborted. + if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); + } +} + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum CBA_Flags +{ + CBAF_AIMFACING = 1, + CBAF_NORANDOM = 2, + CBAF_EXPLICITANGLE = 4, + CBAF_NOPITCH = 8, + CBAF_NORANDOMPUFFZ = 16, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(pufftype, 4); + ACTION_PARAM_FIXED(Range, 5); + ACTION_PARAM_INT(Flags, 6); + ACTION_PARAM_INT(ptr, 7); + + AActor *ref = COPY_AAPTR(self, ptr); + + if(Range==0) Range=MISSILERANGE; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if (ref || (Flags & CBAF_AIMFACING)) + { + if (!(Flags & CBAF_AIMFACING)) + { + A_Face(self, ref); + } + bangle = self->angle; + + if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); + + if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i=0 ; itarget) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else + { + if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); + } +} + +//========================================================================== +// +// A fully customizable combo attack +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(damage, 2); + ACTION_PARAM_SOUND(MeleeSound, 3); + ACTION_PARAM_NAME(DamageType, 4); + ACTION_PARAM_BOOL(bleed, 5); + + if (!self->target) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (ti) + { + // This seemingly senseless code is needed for proper aiming. + self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); + self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (!ACTION_CALL_FROM_WEAPON()) return; + + if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) + { + ACTION_JUMP(jump); + } + +} + + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum FB_Flags +{ + FBF_USEAMMO = 1, + FBF_NORANDOM = 2, + FBF_EXPLICITANGLE = 4, + FBF_NOPITCH = 8, + FBF_NOFLASH = 16, + FBF_NORANDOMPUFFZ = 32, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumberOfBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(PuffType, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FIXED(Range, 6); + + if (!self->player) return; + + player_t * player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if ((Flags & FBF_USEAMMO) && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (Range == 0) Range = PLAYERMISSILERANGE; + + if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); + + if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); + bangle = self->angle; + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) + { + int damage = DamagePerBullet; + + if (!(Flags & FBF_NORANDOM)) + damage *= ((pr_cwbullet()%3)+1); + + P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); + } + else + { + if (NumberOfBullets == -1) NumberOfBullets = 1; + for (i=0 ; iplayer) return; + + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + AActor *linetarget; + + if (UseAmmo && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = SpawnOfs_XY * finecosine[ang]; + fixed_t y = SpawnOfs_XY * finesine[ang]; + fixed_t z = SpawnHeight; + fixed_t shootangle = self->angle; + + if (Flags & FPF_AIMATANGLE) shootangle += Angle; + + // Temporarily adjusts the pitch + fixed_t SavedPlayerPitch = self->pitch; + self->pitch -= pitch; + AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); + self->pitch = SavedPlayerPitch; + + // automatic handling of seeker missiles + if (misl) + { + if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; + if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; + if (!(Flags & FPF_AIMATANGLE)) + { + // This original implementation is to aim straight ahead and then offset + // the angle from the resulting direction. + FVector3 velocity(misl->velx, misl->vely, 0); + fixed_t missilespeed = (fixed_t)velocity.Length(); + misl->angle += Angle; + angle_t an = misl->angle >> ANGLETOFINESHIFT; + misl->velx = FixedMul (missilespeed, finecosine[an]); + misl->vely = FixedMul (missilespeed, finesine[an]); + } + } + } +} + + +//========================================================================== +// +// A_CustomPunch +// +// Berserk is not handled here. That can be done with A_CheckIfInventory +// +//========================================================================== + +enum +{ + CPF_USEAMMO = 1, + CPF_DAGGER = 2, + CPF_PULLIN = 4, + CPF_NORANDOMPUFFZ = 8, + CPF_NOTURN = 16, + CPF_STEALARMOR = 32, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_BOOL(norandom, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(PuffType, 3); + ACTION_PARAM_FIXED(Range, 4); + ACTION_PARAM_FIXED(LifeSteal, 5); + ACTION_PARAM_INT(lifestealmax, 6); + ACTION_PARAM_CLASS(armorbonustype, 7); + + if (!self->player) return; + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + + angle_t angle; + int pitch; + AActor * linetarget; + int actualdamage; + + if (!norandom) Damage *= (pr_cwpunch()%8+1); + + angle = self->angle + (pr_cwpunch.Random2() << 18); + if (Range == 0) Range = MELEERANGE; + pitch = P_AimLineAttack (self, angle, Range, &linetarget); + + // only use ammo when actually hitting something! + if ((flags & CPF_USEAMMO) && linetarget && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); + + P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); + + if (linetarget) + { + if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) + { + if (flags & CPF_STEALARMOR) + { + if (!armorbonustype) armorbonustype = PClass::FindClass("ArmorBonus"); + + if (armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))) + { + ABasicArmorBonus *armorbonus = static_cast(Spawn (armorbonustype, 0,0,0, NO_REPLACE)); + armorbonus->SaveAmount *= (actualdamage * LifeSteal) >> FRACBITS; + armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax; + armorbonus->flags |= MF_DROPPED; + armorbonus->ClearCounters(); + + if (!armorbonus->CallTryPickup (self)) + { + armorbonus->Destroy (); + } + } + } + + else + { + P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS, lifestealmax); + } + } + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + if (!(flags & CPF_NOTURN)) + { + // turn to face target + self->angle = R_PointToAngle2 (self->x, + self->y, + linetarget->x, + linetarget->y); + } + + if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; + if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); + } +} + + +//========================================================================== +// +// customizable railgun attack function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_BOOL(UseAmmo, 2); + ACTION_PARAM_COLOR(Color1, 3); + ACTION_PARAM_COLOR(Color2, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + if (!self->player) return; + + AWeapon * weapon=self->player->ReadyWeapon; + + // only use ammo when actually hitting something! + if (UseAmmo) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + angle_t angle; + angle_t slope; + + if (Flags & RAF_EXPLICITANGLE) + { + angle = Spread_XY; + slope = Spread_Z; + } + else + { + angle = pr_crailgun.Random2() * (Spread_XY / 255); + slope = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); +} + +//========================================================================== +// +// also for monsters +// +//========================================================================== +enum +{ + CRF_DONTAIM = 0, + CRF_AIMPARALLEL = 1, + CRF_AIMDIRECT = 2, + CRF_EXPLICITANGLE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_COLOR(Color1, 2); + ACTION_PARAM_COLOR(Color2, 3); + ACTION_PARAM_INT(Flags, 4); + ACTION_PARAM_INT(aim, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + AActor *linetarget; + + fixed_t saved_x = self->x; + fixed_t saved_y = self->y; + angle_t saved_angle = self->angle; + fixed_t saved_pitch = self->pitch; + + if (aim && self->target == NULL) + { + return; + } + // [RH] Andy Baker's stealth monsters + if (self->flags & MF_STEALTH) + { + self->visdir = 1; + } + + self->flags &= ~MF_AMBUSH; + + + if (aim) + { + self->angle = R_PointToAngle2 (self->x, + self->y, + self->target->x, + self->target->y); + } + self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); + if (linetarget == NULL && aim) + { + // We probably won't hit the target, but aim at it anyway so we don't look stupid. + FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + double zdiff = (self->target->z + (self->target->height>>1)) - + (self->z + (self->height>>1) - self->floorclip); + self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); + } + // Let the aim trail behind the player + if (aim) + { + saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + + if (aim == CRF_AIMDIRECT) + { + // Tricky: We must offset to the angle of the current position + // but then change the angle again to ensure proper aim. + self->x += Spawnofs_XY * finecosine[self->angle]; + self->y += Spawnofs_XY * finesine[self->angle]; + Spawnofs_XY = 0; + self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + } + + if (self->target->flags & MF_SHADOW) + { + angle_t rnd = pr_crailgun.Random2() << 21; + self->angle += rnd; + saved_angle = rnd; + } + } + + angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; + + angle_t angleoffset; + angle_t slopeoffset; + + if (Flags & CRF_EXPLICITANGLE) + { + angleoffset = Spread_XY; + slopeoffset = Spread_Z; + } + else + { + angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); + slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); + + self->x = saved_x; + self->y = saved_y; + self->angle = saved_angle; + self->pitch = saved_pitch; +} + +//=========================================================================== +// +// DoGiveInventory +// +//=========================================================================== + +static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_CLASS(mi, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(setreceiver, 2); + + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + + bool res=true; + + if (amount==0) amount=1; + if (mi) + { + AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); + if (item->IsKindOf(RUNTIME_CLASS(AHealth))) + { + item->Amount *= amount; + } + else + { + item->Amount = amount; + } + item->flags |= MF_DROPPED; + item->ClearCounters(); + if (!item->CallTryPickup (receiver)) + { + item->Destroy (); + res = false; + } + else res = true; + } + else res = false; + ACTION_SET_RESULT(res); + +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) +{ + DoGiveInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) +{ + DoGiveInventory(self->target, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) +{ + TThinkerIterator it; + AActor * mo; + + while ((mo = it.Next())) + { + if (mo->master == self) DoGiveInventory(mo, PUSH_PARAMINFO); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) +{ + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) DoGiveInventory(mo, PUSH_PARAMINFO); + } + } +} + +//=========================================================================== +// +// A_TakeInventory +// +//=========================================================================== + +enum +{ + TIF_NOTAKEINFINITE = 1, +}; + +void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(setreceiver, 3); + + if (!item) return; + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + + bool res = false; + + AInventory * inv = receiver->FindInventory(item); + + if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) + { + if (inv->Amount > 0) + { + res = true; + } + // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set + // and infinite ammo is on + if (flags & TIF_NOTAKEINFINITE && + ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && + inv->IsKindOf(RUNTIME_CLASS(AAmmo))) + { + // Nothing to do here, except maybe res = false;? Would it make sense? + } + else if (!amount || amount>=inv->Amount) + { + if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; + else inv->Destroy(); + } + else inv->Amount-=amount; + } + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) +{ + DoTakeInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) +{ + DoTakeInventory(self->target, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) +{ + TThinkerIterator it; + AActor * mo; + + while ((mo = it.Next())) + { + if (mo->master == self) DoTakeInventory(mo, PUSH_PARAMINFO); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) +{ + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) DoTakeInventory(mo, PUSH_PARAMINFO); + } + } +} + +//=========================================================================== +// +// Common code for A_SpawnItem and A_SpawnItemEx +// +//=========================================================================== + +enum SIX_Flags +{ + SIXF_TRANSFERTRANSLATION = 1 << 0, + SIXF_ABSOLUTEPOSITION = 1 << 1, + SIXF_ABSOLUTEANGLE = 1 << 2, + SIXF_ABSOLUTEVELOCITY = 1 << 3, + SIXF_SETMASTER = 1 << 4, + SIXF_NOCHECKPOSITION = 1 << 5, + SIXF_TELEFRAG = 1 << 6, + SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum + SIXF_TRANSFERAMBUSHFLAG = 1 << 8, + SIXF_TRANSFERPITCH = 1 << 9, + SIXF_TRANSFERPOINTERS = 1 << 10, + SIXF_USEBLOODCOLOR = 1 << 11, + SIXF_CLEARCALLERTID = 1 << 12, + SIXF_MULTIPLYSPEED = 1 << 13, + SIXF_TRANSFERSCALE = 1 << 14, + SIXF_TRANSFERSPECIAL = 1 << 15, + SIXF_CLEARCALLERSPECIAL = 1 << 16, + SIXF_TRANSFERSTENCILCOL = 1 << 17, + SIXF_TRANSFERALPHA = 1 << 18, + SIXF_TRANSFERRENDERSTYLE = 1 << 19, + SIXF_SETTARGET = 1 << 20, + SIXF_SETTRACER = 1 << 21, + SIXF_NOPOINTERS = 1 << 22, + SIXF_ORIGINATOR = 1 << 23, +}; + +static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) +{ + if (mo == NULL) + { + return false; + } + AActor *originator = self; + + if (!(mo->flags2 & MF2_DONTTRANSLATE)) + { + if (flags & SIXF_TRANSFERTRANSLATION) + { + mo->Translation = self->Translation; + } + else if (flags & SIXF_USEBLOODCOLOR) + { + // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. + PalEntry bloodcolor = self->GetBloodColor(); + mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + } + } + if (flags & SIXF_TRANSFERPOINTERS) + { + mo->target = self->target; + mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set + mo->tracer = self->tracer; + } + + mo->angle = self->angle; + if (flags & SIXF_TRANSFERPITCH) + { + mo->pitch = self->pitch; + } + if (!(flags & SIXF_ORIGINATOR)) + { + while (originator && originator->isMissile()) + { + originator = originator->target; + } + } + if (flags & SIXF_TELEFRAG) + { + P_TeleportMove(mo, mo->x, mo->y, mo->z, true); + // This is needed to ensure consistent behavior. + // Otherwise it will only spawn if nothing gets telefragged + flags |= SIXF_NOCHECKPOSITION; + } + if (mo->flags3 & MF3_ISMONSTER) + { + if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) + { + // The monster is blocked so don't spawn it at all! + mo->ClearCounters(); + mo->Destroy(); + return false; + } + else if (originator && !(flags & SIXF_NOPOINTERS)) + { + if (originator->flags3 & MF3_ISMONSTER) + { + // If this is a monster transfer all friendliness information + mo->CopyFriendliness(originator, true); + } + else if (originator->player) + { + // A player always spawns a monster friendly to him + mo->flags |= MF_FRIENDLY; + mo->SetFriendPlayer(originator->player); + + AActor * attacker=originator->player->attacker; + if (attacker) + { + if (!(attacker->flags&MF_FRIENDLY) || + (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) + { + // Target the monster which last attacked the player + mo->LastHeard = mo->target = attacker; + } + } + } + } + } + else if (!(flags & SIXF_TRANSFERPOINTERS)) + { + // If this is a missile or something else set the target to the originator + mo->target = originator ? originator : self; + } + if (flags & SIXF_NOPOINTERS) + { + //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. + mo->LastHeard = NULL; //Sanity check. + mo->target = NULL; + mo->master = NULL; + mo->tracer = NULL; + } + if (flags & SIXF_SETMASTER) + { + mo->master = originator; + } + if (flags & SIXF_SETTARGET) + { + mo->target = originator; + } + if (flags & SIXF_SETTRACER) + { + mo->tracer = originator; + } + if (flags & SIXF_TRANSFERSCALE) + { + mo->scaleX = self->scaleX; + mo->scaleY = self->scaleY; + } + if (flags & SIXF_TRANSFERAMBUSHFLAG) + { + mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); + } + if (flags & SIXF_CLEARCALLERTID) + { + self->RemoveFromHash(); + self->tid = 0; + } + if (flags & SIXF_TRANSFERSPECIAL) + { + mo->special = self->special; + memcpy(mo->args, self->args, sizeof(self->args)); + } + if (flags & SIXF_CLEARCALLERSPECIAL) + { + self->special = 0; + memset(self->args, 0, sizeof(self->args)); + } + if (flags & SIXF_TRANSFERSTENCILCOL) + { + mo->fillcolor = self->fillcolor; + } + if (flags & SIXF_TRANSFERALPHA) + { + mo->alpha = self->alpha; + } + if (flags & SIXF_TRANSFERRENDERSTYLE) + { + mo->RenderStyle = self->RenderStyle; + } + + return true; +} + +//=========================================================================== +// +// A_SpawnItem +// +// Spawns an item in front of the caller like Heretic's time bomb +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_FIXED(zheight, 2); + ACTION_PARAM_BOOL(useammo, 3); + ACTION_PARAM_BOOL(transfer_translation, 4); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + // Don't spawn monsters if this actor has been massacred + if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + if (distance==0) + { + // use the minimum distance that does not result in an overlap + distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; + } + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon so use some ammo + AWeapon * weapon=self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + AActor * mo = Spawn( missile, + self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), + self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), + self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); + + int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state +} + +//=========================================================================== +// +// A_SpawnItemEx +// +// Enhanced spawning function +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) +{ + ACTION_PARAM_START(11); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_FIXED(xvel, 4); + ACTION_PARAM_FIXED(yvel, 5); + ACTION_PARAM_FIXED(zvel, 6); + ACTION_PARAM_ANGLE(Angle, 7); + ACTION_PARAM_INT(flags, 8); + ACTION_PARAM_INT(chance, 9); + ACTION_PARAM_INT(tid, 10); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + fixed_t x,y; + + if (!(flags & SIXF_ABSOLUTEANGLE)) + { + Angle += self->angle; + } + + angle_t ang = Angle >> ANGLETOFINESHIFT; + + if (flags & SIXF_ABSOLUTEPOSITION) + { + x = self->x + xofs; + y = self->y + yofs; + } + else + { + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); + y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); + } + + if (!(flags & SIXF_ABSOLUTEVELOCITY)) + { + // Same orientation issue here! + fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); + yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); + xvel = newxvel; + } + + AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state + if (res) + { + if (tid != 0) + { + assert(mo->tid == 0); + mo->tid = tid; + mo->AddToHash(); + } + if (flags & SIXF_MULTIPLYSPEED) + { + mo->velx = FixedMul(xvel, mo->Speed); + mo->vely = FixedMul(yvel, mo->Speed); + mo->velz = FixedMul(zvel, mo->Speed); + } + else + { + mo->velx = xvel; + mo->vely = yvel; + mo->velz = zvel; + } + mo->angle = Angle; + } +} + +//=========================================================================== +// +// A_ThrowGrenade +// +// Throws a grenade (like Hexen's fighter flechette) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(zheight, 1); + ACTION_PARAM_FIXED(xyvel, 2); + ACTION_PARAM_FIXED(zvel, 3); + ACTION_PARAM_BOOL(useammo, 4); + + if (missile == NULL) return; + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon, so use some ammo + AWeapon *weapon = self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + + AActor * bo; + + bo = Spawn(missile, self->x, self->y, + self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), + ALLOW_REPLACE); + if (bo) + { + P_PlaySpawnSound(bo, self); + if (xyvel != 0) + bo->Speed = xyvel; + bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); + + angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; + angle_t angle = bo->angle >> ANGLETOFINESHIFT; + + // There are two vectors we are concerned about here: xy and z. We rotate + // them separately according to the shooter's pitch and then sum them to + // get the final velocity vector to shoot with. + + fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); + fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); + fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); + fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); + + pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; + fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); + fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); + fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); + fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); + + bo->velx = xy_velx + z_velx + (self->velx >> 1); + bo->vely = xy_vely + z_vely + (self->vely >> 1); + bo->velz = xy_velz + z_velz; + + bo->target = self; + P_CheckMissileSpawn (bo, self->radius); + } + else ACTION_SET_RESULT(false); +} + + +//=========================================================================== +// +// A_Recoil +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(xyvel, 0); + + angle_t angle = self->angle + ANG180; + angle >>= ANGLETOFINESHIFT; + self->velx += FixedMul (xyvel, finecosine[angle]); + self->vely += FixedMul (xyvel, finesine[angle]); +} + + +//=========================================================================== +// +// A_SelectWeapon +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(cls, 0); + + if (cls == NULL || self->player == NULL) + { + ACTION_SET_RESULT(false); + return; + } + + AWeapon * weaponitem = static_cast(self->FindInventory(cls)); + + if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) + { + if (self->player->ReadyWeapon != weaponitem) + { + self->player->PendingWeapon = weaponitem; + } + } + else ACTION_SET_RESULT(false); + +} + + +//=========================================================================== +// +// A_Print +// +//=========================================================================== +EXTERN_CVAR(Float, con_midtime) + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + if (text[0] == '$') text = GStrings(text+1); + if (self->CheckLocalView (consoleplayer) || + (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) + { + float saved = con_midtime; + FFont *font = NULL; + + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + } + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_PrintBold +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + float saved = con_midtime; + FFont *font = NULL; + + if (text[0] == '$') text = GStrings(text+1); + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_Log +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STRING(text, 0); + + if (text[0] == '$') text = GStrings(text+1); + FString formatted = strbin1(text); + Printf("%s\n", formatted.GetChars()); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//========================================================================= +// +// A_LogInt +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(num, 0); + Printf("%d\n", num); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_SetTranslucent +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(alpha, 0); + ACTION_PARAM_INT(mode, 1); + + mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha = clamp(alpha, 0, FRACUNIT); + self->RenderStyle = ERenderStyle(mode); +} + +//=========================================================================== +// +// A_FadeIn +// +// Fades the actor in +// +//=========================================================================== + +enum FadeFlags +{ + FTF_REMOVE = 1 << 0, + FTF_CLAMP = 1 << 1, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_INT(flags, 1); + + if (reduce == 0) + { + reduce = FRACUNIT / 10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha += reduce; + + if (self->alpha >= (FRACUNIT * 1)) + { + if (flags & FTF_CLAMP) + self->alpha = (FRACUNIT * 1); + if (flags & FTF_REMOVE) + self->Destroy(); + } +} + +//=========================================================================== +// +// A_FadeOut +// +// fades the actor out and destroys it when done +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_INT(flags, 1); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha -= reduce; + if (self->alpha <= 0) + { + if (flags & FTF_CLAMP) + self->alpha = 0; + if (flags & FTF_REMOVE) + self->Destroy(); + } +} + +//=========================================================================== +// +// A_FadeTo +// +// fades the actor to a specified transparency by a specified amount and +// destroys it if so desired +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_FIXED(target, 0); + ACTION_PARAM_FIXED(amount, 1); + ACTION_PARAM_INT(flags, 2); + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + + if (self->alpha > target) + { + self->alpha -= amount; + + if (self->alpha < target) + { + self->alpha = target; + } + } + else if (self->alpha < target) + { + self->alpha += amount; + + if (self->alpha > target) + { + self->alpha = target; + } + } + if (flags & FTF_CLAMP) + { + if (self->alpha > (FRACUNIT * 1)) + self->alpha = (FRACUNIT * 1); + else if (self->alpha < 0) + self->alpha = 0; + } + if (self->alpha == target && (flags & FTF_REMOVE)) + { + self->Destroy(); + } +} + +//=========================================================================== +// +// A_Scale(float scalex, optional float scaley) +// +// Scales the actor's graphics. If scaley is 0, use scalex. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_FIXED(scalex, 0); + ACTION_PARAM_FIXED(scaley, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->scaleX = scalex; + ref->scaleY = scaley ? scaley : scalex; +} + +//=========================================================================== +// +// A_SetMass(int mass) +// +// Sets the actor's mass. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(mass, 0); + + self->Mass = mass; +} + +//=========================================================================== +// +// A_SpawnDebris +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) +{ + int i; + AActor * mo; + + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(debris, 0); + ACTION_PARAM_BOOL(transfer_translation, 1); + ACTION_PARAM_FIXED(mult_h, 2); + ACTION_PARAM_FIXED(mult_v, 3); + + if (debris == NULL) return; + + // only positive values make sense here + if (mult_v<=0) mult_v=FRACUNIT; + if (mult_h<=0) mult_h=FRACUNIT; + + for (i = 0; i < GetDefaultByType(debris)->health; i++) + { + mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), + self->y + ((pr_spawndebris()-128)<<12), + self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); + if (mo) + { + if (transfer_translation) + { + mo->Translation = self->Translation; + } + if (i < mo->GetClass()->ActorInfo->NumOwnedStates) + { + mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); + } + mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); + mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + } + } +} + + +//=========================================================================== +// +// A_CheckSight +// jumps if no player can see this actor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + for (int i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + // Always check sight from each player. + if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) + { + return; + } + // If a player is viewing from a non-player, then check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) + { + return; + } + } + } + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckSightOrRange +// Jumps if this actor is out of range of all players *and* out of sight. +// Useful for maps with many multi-actor special effects. +// +//=========================================================================== +static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool twodi) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height) + { + dz = self->z + self->height - eyez; + } + else if (eyez < self->z) + { + dz = self->z - eyez; + } + else + { + dz = 0; + } + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ + // Within range + return true; + } + + // Now check LOS. + if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) + { // Visible + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) +{ + ACTION_PARAM_START(3); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckSightOrRange(self, players[i].mo, range, twodi)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckSightOrRange(self, players[i].camera, range, twodi)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckRange +// Jumps if this actor is out of range of all players. +// +//=========================================================================== +static bool DoCheckRange(AActor *self, AActor *camera, double range, bool twodi) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height){ + dz = self->z + self->height - eyez; + } + else if (eyez < self->z){ + dz = self->z - eyez; + } + else{ + dz = 0; + } + double distance = (dx * dx) + (dy * dy) + (twodi == 0? (dz * dz) : 0); + if (distance <= range){ + // Within range + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) +{ + ACTION_PARAM_START(3); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(twodi, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckRange(self, players[i].mo, range, twodi)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckRange(self, players[i].camera, range, twodi)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + + +//=========================================================================== +// +// Inventory drop +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(drop, 0); + + if (drop) + { + AInventory * inv = self->FindInventory(drop); + if (inv) + { + self->DropInventory(inv); + } + } +} + + +//=========================================================================== +// +// A_SetBlend +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_COLOR(color, 0); + ACTION_PARAM_FLOAT(alpha, 1); + ACTION_PARAM_INT(tics, 2); + ACTION_PARAM_COLOR(color2, 3); + + if (color == MAKEARGB(255,255,255,255)) color=0; + if (color2 == MAKEARGB(255,255,255,255)) color2=0; + if (!color2.a) + color2 = color; + + new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, + color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, + (float)tics/TICRATE, self); +} + + +//=========================================================================== +// +// A_JumpIf +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(expression, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (expression) ACTION_JUMP(jump); + +} + +//=========================================================================== +// +// A_CountdownArg +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(cnt, 0); + ACTION_PARAM_STATE(state, 1); + + if (cnt<0 || cnt>=5) return; + if (!self->args[cnt]--) + { + if (self->flags&MF_MISSILE) + { + P_ExplodeMissile(self, NULL, NULL); + } + else if (self->flags&MF_SHOOTABLE) + { + P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); + } + else + { + // can't use "Death" as default parameter with current DECORATE parser. + if (state == NULL) state = self->FindState(NAME_Death); + self->SetState(state); + } + } + +} + +//============================================================================ +// +// A_Burst +// +//============================================================================ + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(chunk, 0); + + int i, numChunks; + AActor * mo; + + if (chunk == NULL) return; + + self->velx = self->vely = self->velz = 0; + self->height = self->GetDefault()->height; + + // [RH] In Hexen, this creates a random number of shards (range [24,56]) + // with no relation to the size of the self shattering. I think it should + // base the number of shards on the size of the dead thing, so bigger + // things break up into more shards than smaller things. + // An self with radius 20 and height 64 creates ~40 chunks. + numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); + i = (pr_burst.Random2()) % (numChunks/4); + for (i = MAX (24, numChunks + i); i >= 0; i--) + { + mo = Spawn(chunk, + self->x + (((pr_burst()-128)*self->radius)>>7), + self->y + (((pr_burst()-128)*self->radius)>>7), + self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); + + if (mo) + { + mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; + mo->velx = pr_burst.Random2 () << (FRACBITS-7); + mo->vely = pr_burst.Random2 () << (FRACBITS-7); + mo->RenderStyle = self->RenderStyle; + mo->alpha = self->alpha; + mo->CopyFriendliness(self, true); + } + } + + // [RH] Do some stuff to make this more useful outside Hexen + if (self->flags4 & MF4_BOSSDEATH) + { + CALL_ACTION(A_BossDeath, self); + } + A_Unblock(self, true); + + self->Destroy (); +} + +//=========================================================================== +// +// A_CheckFloor +// [GRB] Jumps if actor is standing on floor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->z <= self->floorz) + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_CheckCeiling +// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling +// + +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); + if (self->z+self->height >= self->ceilingz) // Height needs to be counted + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_Stop +// resets all velocity of the actor to 0 +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_Stop) +{ + self->velx = self->vely = self->velz = 0; + if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +static void CheckStopped(AActor *self) +{ + if (self->player != NULL && + self->player->mo == self && + !(self->player->cheats & CF_PREDICTING) && + !(self->velx | self->vely | self->velz)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +//=========================================================================== +// +// A_Respawn +// +//=========================================================================== + +extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); + +enum RS_Flags +{ + RSF_FOG=1, + RSF_KEEPTARGET=2, + RSF_TELEFRAG=4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + bool oktorespawn = false; + fixed_t oldx = self->x; + fixed_t oldy = self->y; + fixed_t oldz = self->z; + self->flags |= MF_SOLID; + self->height = self->GetDefault()->height; + CALL_ACTION(A_RestoreSpecialPosition, self); + + if (flags & RSF_TELEFRAG) + { + // [KS] DIE DIE DIE DIE erm *ahem* =) + oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); + if (oktorespawn) + { // Need to do this over again, since P_TeleportMove() will redo + // it with the proper point-on-side calculation. + self->UnlinkFromWorld(); + self->LinkToWorld(true); + sector_t *sec = self->Sector; + self->dropoffz = + self->floorz = sec->floorplane.ZatPoint(self->x, self->y); + self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); + P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); + } + } + else + { + oktorespawn = P_CheckPosition(self, self->x, self->y, true); + } + + if (oktorespawn) + { + AActor *defs = self->GetDefault(); + self->health = defs->health; + + // [KS] Don't keep target, because it could be self if the monster committed suicide + // ...Actually it's better off an option, so you have better control over monster behavior. + if (!(flags & RSF_KEEPTARGET)) + { + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; + } + else + { + // Don't attack yourself (Re: "Marine targets itself after suicide") + if (self->target == self) self->target = NULL; + if (self->lastenemy == self) self->lastenemy = NULL; + } + + self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); + self->flags2 = defs->flags2; + self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); + self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); + self->flags5 = defs->flags5; + self->flags6 = defs->flags6; + self->flags7 = defs->flags7; + self->SetState (self->SpawnState); + self->renderflags &= ~RF_INVISIBLE; + + if (flags & RSF_FOG) + { + P_SpawnTeleportFog(self, oldx, oldy, oldz, true); + P_SpawnTeleportFog(self, self->x, self->y, self->z, false); + } + if (self->CountsAsKill()) + { + level.total_monsters++; + } + } + else + { + self->flags &= ~MF_SOLID; + } +} + + +//========================================================================== +// +// A_PlayerSkinCheck +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->player != NULL && + skins[self->player->userinfo.GetSkin()].othergame) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetGravity +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(val, 0); + + self->gravity = clamp (val, 0, FRACUNIT*10); +} + + +// [KS] *** Start of my modifications *** + +//=========================================================================== +// +// A_ClearTarget +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) +{ + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; +} + +//========================================================================== +// +// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, +// fixed range = 0, angle angle = 0, angle pitch = 0, +// fixed offsetheight = 32, fixed offsetwidth = 0, +// int ptr_target = AAPTR_DEFAULT (target) ) +// +//========================================================================== + +enum CLOF_flags +{ + CLOFF_NOAIM_VERT = 0x00000001, + CLOFF_NOAIM_HORZ = 0x00000002, + + CLOFF_JUMPENEMY = 0x00000004, + CLOFF_JUMPFRIEND = 0x00000008, + CLOFF_JUMPOBJECT = 0x00000010, + CLOFF_JUMPNONHOSTILE = 0x00000020, + + CLOFF_SKIPENEMY = 0x00000040, + CLOFF_SKIPFRIEND = 0x00000080, + CLOFF_SKIPOBJECT = 0x00000100, + CLOFF_SKIPNONHOSTILE = 0x00000200, + + CLOFF_MUSTBESHOOTABLE = 0x00000400, + + CLOFF_SKIPTARGET = 0x00000800, + CLOFF_ALLOWNULL = 0x00001000, + CLOFF_CHECKPARTIAL = 0x00002000, + + CLOFF_MUSTBEGHOST = 0x00004000, + CLOFF_IGNOREGHOST = 0x00008000, + + CLOFF_MUSTBESOLID = 0x00010000, + CLOFF_BEYONDTARGET = 0x00020000, + + CLOFF_FROMBASE = 0x00040000, + CLOFF_MUL_HEIGHT = 0x00080000, + CLOFF_MUL_WIDTH = 0x00100000, + + CLOFF_JUMP_ON_MISS = 0x00200000, + CLOFF_AIM_VERT_NOOFFSET = 0x00400000, + + CLOFF_SETTARGET = 0x00800000, + CLOFF_SETMASTER = 0x01000000, + CLOFF_SETTRACER = 0x02000000, +}; + +struct LOFData +{ + AActor *Self; + AActor *Target; + int Flags; + bool BadActor; +}; + +ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) +{ + LOFData *data = (LOFData *)userdata; + int flags = data->Flags; + + if (trace.HitType != TRACE_HitActor) + { + return TRACE_Stop; + } + if (trace.Actor == data->Target) + { + if (flags & CLOFF_SKIPTARGET) + { + if (flags & CLOFF_BEYONDTARGET) + { + return TRACE_Skip; + } + return TRACE_Abort; + } + return TRACE_Stop; + } + if (flags & CLOFF_MUSTBESHOOTABLE) + { // all shootability checks go here + if (!(trace.Actor->flags & MF_SHOOTABLE)) + { + return TRACE_Skip; + } + if (trace.Actor->flags2 & MF2_NONSHOOTABLE) + { + return TRACE_Skip; + } + } + if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) + { + return TRACE_Skip; + } + if (flags & CLOFF_MUSTBEGHOST) + { + if (!(trace.Actor->flags3 & MF3_GHOST)) + { + return TRACE_Skip; + } + } + else if (flags & CLOFF_IGNOREGHOST) + { + if (trace.Actor->flags3 & MF3_GHOST) + { + return TRACE_Skip; + } + } + if ( + ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Stop; + } + if ( + ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Skip; + } + data->BadActor = true; + return TRACE_Abort; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) +{ + // Check line of fire + + /* + Not accounted for / I don't know how it works: FLOORCLIP + */ + + AActor *target; + fixed_t + x1, y1, z1, + vx, vy, vz; + + ACTION_PARAM_START(9); + + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_FIXED(range, 2); + ACTION_PARAM_FIXED(minrange, 3); + { + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_FIXED(offsetheight, 6); + ACTION_PARAM_FIXED(offsetwidth, 7); + ACTION_PARAM_INT(ptr_target, 8); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default + + if (flags & CLOFF_MUL_HEIGHT) + { + if (self->player != NULL) + { + // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player + offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); + } + else + { + offsetheight = FixedMul(offsetheight, self->height); + } + } + if (flags & CLOFF_MUL_WIDTH) + { + offsetwidth = FixedMul(self->radius, offsetwidth); + } + + x1 = self->x; + y1 = self->y; + z1 = self->z + offsetheight - self->floorclip; + + if (!(flags & CLOFF_FROMBASE)) + { // default to hitscan origin + + // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player + z1 += (self->height >> 1); + if (self->player != NULL) + { + z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); + } + else + { + z1 += 8*FRACUNIT; + } + } + + if (target) + { + FVector2 xyvec(target->x - x1, target->y - y1); + fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); + + if (range && !(flags & CLOFF_CHECKPARTIAL)) + { + if (distance > range) return; + } + + { + angle_t ang; + + if (flags & CLOFF_NOAIM_HORZ) + { + ang = self->angle; + } + else ang = R_PointToAngle2 (x1, y1, target->x, target->y); + + angle += ang; + + ang >>= ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + + if (flags & CLOFF_NOAIM_VERT) + { + pitch += self->pitch; + } + else if (flags & CLOFF_AIM_VERT_NOOFFSET) + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); + } + else + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); + } + } + else if (flags & CLOFF_ALLOWNULL) + { + angle += self->angle; + pitch += self->pitch; + + angle_t ang = self->angle >> ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + else return; + + angle >>= ANGLETOFINESHIFT; + pitch = (0-pitch)>>ANGLETOFINESHIFT; + + vx = FixedMul (finecosine[pitch], finecosine[angle]); + vy = FixedMul (finecosine[pitch], finesine[angle]); + vz = -finesine[pitch]; + } + + /* Variable set: + + jump, flags, target + x1,y1,z1 (trace point of origin) + vx,vy,vz (trace unit vector) + range + */ + + sector_t *sec = P_PointInSector(x1, y1); + + if (range == 0) + { + range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; + } + + FTraceResults trace; + LOFData lof_data; + + lof_data.Self = self; + lof_data.Target = target; + lof_data.Flags = flags; + lof_data.BadActor = false; + + Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, + CheckLOFTraceFunc, &lof_data); + + if (trace.HitType == TRACE_HitActor || + ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) + { + if (minrange > 0 && trace.Distance < minrange) + { + return; + } + if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor)) + { + if (flags & (CLOFF_SETTARGET)) self->target = trace.Actor; + if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor; + if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor; + } + + ACTION_JUMP(jump); + } +} + +//========================================================================== +// +// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, +// optional fixed dist_max, optional fixed dist_close) +// +// Jumps if the actor can see its target, or if the player has a linetarget. +// ProjectileTarget affects how projectiles are treated. If set, it will use +// the target of the projectile for seekers, and ignore the target for +// normal projectiles. If not set, it will use the missile's owner instead +// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max +// sets the maximum distance that actor can see, 0 means forever. dist_close +// uses special behavior if certain flags are set, 0 means no checks. +// +//========================================================================== + +enum JLOS_flags +{ + JLOSF_PROJECTILE = 1, + JLOSF_NOSIGHT = 1 << 1, + JLOSF_CLOSENOFOV = 1 << 2, + JLOSF_CLOSENOSIGHT = 1 << 3, + JLOSF_CLOSENOJUMP = 1 << 4, + JLOSF_DEADNOJUMP = 1 << 5, + JLOSF_CHECKMASTER = 1 << 6, + JLOSF_TARGETLOS = 1 << 7, + JLOSF_FLIPFOV = 1 << 8, + JLOSF_ALLYNOJUMP = 1 << 9, + JLOSF_COMBATANTONLY = 1 << 10, + JLOSF_NOAUTOAIM = 1 << 11, + JLOSF_CHECKTRACER = 1 << 12, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target, *viewport; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + bool doCheckSight; + + if (!self->player) + { + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) + { + if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + doCheckSight = !(flags & JLOSF_NOSIGHT); + } + else + { + // Does the player aim at something that can be shot? + P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); + + if (!target) return; + + switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) + { + case JLOSF_TARGETLOS|JLOSF_FLIPFOV: + // target makes sight check, player makes fov check; player has verified fov + fov = 0; + // fall-through + case JLOSF_TARGETLOS: + doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov + break; + default: + // player has verified sight and fov + fov = 0; + // fall-through + case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov + doCheckSight = false; + break; + } + } + + // [FDARI] If target is not a combatant, don't jump + if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; + + // [FDARI] If actors share team, don't jump + if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } + else { viewport = self; } + + if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) + return; + + if (flags & JLOSF_FLIPFOV) + { + if (viewport == self) { viewport = target; target = self; } + else { target = viewport; viewport = self; } + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (viewport->x, + viewport->y, + target->x, + target->y) + - viewport->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + + } + + ACTION_JUMP(jump); +} + + +//========================================================================== +// +// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags +// optional fixed dist_max, optional fixed dist_close) +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + { + if (self->flags2 & MF2_SEEKERMISSILE) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + bool doCheckSight = !(flags & JLOSF_NOSIGHT); + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (target->x, + target->y, + self->x, + self->y) + - target->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + } + + if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) + return; + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// Modified code pointer from Skulltag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + ACTION_PARAM_START(2); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(dontincrement, 2) + + if (count <= 0) return; + + AWeapon *weapon = self->player->ReadyWeapon; + + int ReloadCounter = weapon->ReloadCounter; + if(!dontincrement || ReloadCounter != 0) + ReloadCounter = (weapon->ReloadCounter+1) % count; + else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. + ReloadCounter = 1; + + // If we have not made our last shot... + if (ReloadCounter != 0) + { + // Go back to the refire frames, instead of continuing on to the reload frames. + ACTION_JUMP(jump); + } + else + { + // We need to reload. However, don't reload if we're out of ammo. + weapon->CheckAmmo( false, false ); + } + + if(!dontincrement) + weapon->ReloadCounter = ReloadCounter; +} + +//=========================================================================== +// +// Resets the counter for the above function +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + AWeapon *weapon = self->player->ReadyWeapon; + weapon->ReloadCounter = 0; +} + +//=========================================================================== +// +// A_ChangeFlag +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_BOOL(expression, 1); + + const char *dot = strchr (flagname, '.'); + FFlagDef *fd; + const PClass *cls = self->GetClass(); + + if (dot != NULL) + { + FString part1(flagname, dot-flagname); + fd = FindFlag (cls, part1, dot+1); + } + else + { + fd = FindFlag (cls, flagname, NULL); + } + + if (fd != NULL) + { + bool kill_before, kill_after; + INTBOOL item_before, item_after; + INTBOOL secret_before, secret_after; + + kill_before = self->CountsAsKill(); + item_before = self->flags & MF_COUNTITEM; + secret_before = self->flags5 & MF5_COUNTSECRET; + + if (fd->structoffset == -1) + { + HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); + } + else + { + DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); + + // If these 2 flags get changed we need to update the blockmap and sector links. + bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); + + if (linkchange) self->UnlinkFromWorld(); + ModActorFlag(self, fd, expression); + if (linkchange) self->LinkToWorld(); + } + kill_after = self->CountsAsKill(); + item_after = self->flags & MF_COUNTITEM; + secret_after = self->flags5 & MF5_COUNTSECRET; + // Was this monster previously worth a kill but no longer is? + // Or vice versa? + if (kill_before != kill_after) + { + if (kill_after) + { // It counts as a kill now. + level.total_monsters++; + } + else + { // It no longer counts as a kill. + level.total_monsters--; + } + } + // same for items + if (item_before != item_after) + { + if (item_after) + { // It counts as an item now. + level.total_items++; + } + else + { // It no longer counts as an item + level.total_items--; + } + } + // and secretd + if (secret_before != secret_after) + { + if (secret_after) + { // It counts as an secret now. + level.total_secrets++; + } + else + { // It no longer counts as an secret + level.total_secrets--; + } + } + } + else + { + Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); + } +} + +//=========================================================================== +// +// A_CheckFlag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_STATE(jumpto, 1); + ACTION_PARAM_INT(checkpointer, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + AActor *owner; + + COPY_AAPTR_NOT_NULL(self, owner, checkpointer); + + if (CheckActorFlag(owner, flagname)) + { + ACTION_JUMP(jumpto); + } +} + +//=========================================================================== +// +// A_RaiseMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + + if (self->master != NULL) + { + if (copy) + P_Thing_Raise(self->master, self); + else + P_Thing_Raise(self->master, NULL); + } +} + +//=========================================================================== +// +// A_RaiseChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + TThinkerIterator it; + AActor *mo; + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self) + { + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); + } + } +} + +//=========================================================================== +// +// A_RaiseSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self) + { + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); + } + } + } +} + +//=========================================================================== +// +// [TP] A_FaceConsolePlayer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { + ACTION_PARAM_START (1); + ACTION_PARAM_ANGLE (MaxTurnAngle, 0); + // NOTE: It does nothing for zdoom. +} + +//=========================================================================== +// +// A_MonsterRefire +// +// Keep firing unless target got out of sight +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(prob, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + A_FaceTarget (self); + + if (pr_monsterrefire() < prob) + return; + + if (!self->target + || P_HitFriend (self) + || self->target->health <= 0 + || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetAngle +// +// Set actor's angle (in degrees). +// +//=========================================================================== +enum +{ + SPF_FORCECLAMP = 1, // players always clamp + SPF_INTERPOLATE = 2, +}; + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_ANGLE(angle, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_SetPitch +// +// Set actor's pitch (in degrees). +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_ANGLE(pitch, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + if (ref->player != NULL || (flags & SPF_FORCECLAMP)) + { // clamp the pitch we set + int min, max; + + if (ref->player != NULL) + { + min = ref->player->MinPitch; + max = ref->player->MaxPitch; + } + else + { + min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); + max = ANGLE_90 - (1 << ANGLETOFINESHIFT); + } + pitch = clamp(pitch, min, max); + } + ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); +} + //=========================================================================== // // [Nash] A_SetRoll @@ -4028,1830 +4013,1830 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) ACTION_PARAM_START(3); ACTION_PARAM_ANGLE(roll, 0); ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_INT(ptr, 2); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; + ACTION_PARAM_INT(ptr, 2); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; } ref->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); } - -//=========================================================================== -// -// A_ScaleVelocity -// -// Scale actor's velocity. -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(scale, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - INTBOOL was_moving = self->velx | self->vely | self->velz; - - ref->velx = FixedMul(ref->velx, scale); - ref->vely = FixedMul(ref->vely, scale); - ref->velz = FixedMul(ref->velz, scale); - - // If the actor was previously moving but now is not, and is a player, - // update its player variables. (See A_Stop.) - if (was_moving) - { - CheckStopped(ref); - } -} - -//=========================================================================== -// -// A_ChangeVelocity -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_FIXED(x, 0); - ACTION_PARAM_FIXED(y, 1); - ACTION_PARAM_FIXED(z, 2); - ACTION_PARAM_INT(flags, 3); - ACTION_PARAM_INT(ptr, 4); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - INTBOOL was_moving = ref->velx | ref->vely | ref->velz; - - fixed_t vx = x, vy = y, vz = z; - fixed_t sina = finesine[ref->angle >> ANGLETOFINESHIFT]; - fixed_t cosa = finecosine[ref->angle >> ANGLETOFINESHIFT]; - - if (flags & 1) // relative axes - make x, y relative to actor's current angle - { - vx = DMulScale16(x, cosa, -y, sina); - vy = DMulScale16(x, sina, y, cosa); - } - if (flags & 2) // discard old velocity - replace old velocity with new velocity - { - ref->velx = vx; - ref->vely = vy; - ref->velz = vz; - } - else // add new velocity to old velocity - { - ref->velx += vx; - ref->vely += vy; - ref->velz += vz; - } - - if (was_moving) - { - CheckStopped(self); - } -} - -//=========================================================================== -// -// A_SetArg -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(pos, 0); - ACTION_PARAM_INT(value, 1); - - // Set the value of the specified arg - if ((size_t)pos < countof(self->args)) - { - self->args[pos] = value; - } -} - -//=========================================================================== -// -// A_SetSpecial -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(spec, 0); - ACTION_PARAM_INT(arg0, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - ACTION_PARAM_INT(arg4, 5); - - self->special = spec; - self->args[0] = arg0; - self->args[1] = arg1; - self->args[2] = arg2; - self->args[3] = arg3; - self->args[4] = arg4; -} - -//=========================================================================== -// -// A_SetUserVar -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(value, 1); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Int) - { - Printf("%s is not a user variable in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user variable. - *(int *)(reinterpret_cast(self) + var->offset) = value; -} - -//=========================================================================== -// -// A_SetUserArray -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(pos, 1); - ACTION_PARAM_INT(value, 2); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) - { - Printf("%s is not a user array in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - if (pos < 0 || pos >= var->ValueType.size) - { - Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user array at index pos. - ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; -} - -//=========================================================================== -// -// A_Teleport(optional state teleportstate, optional class targettype, -// optional class fogtype, optional int flags, optional fixed mindist, -// optional fixed maxdist) -// -// Attempts to teleport to a targettype at least mindist away and at most -// maxdist away (0 means unlimited). If successful, spawn a fogtype at old -// location and place calling actor in teleportstate. -// -//=========================================================================== -enum T_Flags -{ - TF_TELEFRAG = 0x00000001, // Allow telefrag in order to teleport. - TF_RANDOMDECIDE = 0x00000002, // Randomly fail based on health. (A_Srcr2Decide) - TF_FORCED = 0x00000004, // Forget what's in the way. TF_Telefrag takes precedence though. - TF_KEEPVELOCITY = 0x00000008, // Preserve velocity. - TF_KEEPANGLE = 0x00000010, // Keep angle. - TF_USESPOTZ = 0x00000020, // Set the z to the spot's z, instead of the floor. - TF_NOSRCFOG = 0x00000040, // Don't leave any fog behind when teleporting. - TF_NODESTFOG = 0x00000080, // Don't spawn any fog at the arrival position. - TF_USEACTORFOG = 0x00000100, // Use the actor's TeleFogSourceType and TeleFogDestType fogs. - TF_NOJUMP = 0x00000200, // Don't jump after teleporting. -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_STATE(TeleportState, 0); - ACTION_PARAM_CLASS(TargetType, 1); - ACTION_PARAM_CLASS(FogType, 2); - ACTION_PARAM_INT(Flags, 3); - ACTION_PARAM_FIXED(MinDist, 4); - ACTION_PARAM_FIXED(MaxDist, 5); - - // Randomly choose not to teleport like A_Srcr2Decide. - if (Flags & TF_RANDOMDECIDE) - { - static const int chance[] = - { - 192, 120, 120, 120, 64, 64, 32, 16, 0 - }; - - unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); - - if (chanceindex >= countof(chance)) - { - chanceindex = countof(chance) - 1; - } - - if (pr_teleport() >= chance[chanceindex]) return; - } - - DSpotState *state = DSpotState::GetSpotState(); - if (state == NULL) return; - - if (!TargetType) TargetType = PClass::FindClass("BossSpot"); - - AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); - if (spot == NULL) return; - - fixed_t prevX = self->x; - fixed_t prevY = self->y; - fixed_t prevZ = self->z; - fixed_t aboveFloor = spot->z - spot->floorz; - fixed_t finalz = spot->floorz + aboveFloor; - - if (spot->z + self->height > spot->ceilingz) - finalz = spot->ceilingz - self->height; - else if (spot->z < spot->floorz) - finalz = spot->floorz; - - - //Take precedence and cooperate with telefragging first. - bool teleResult = P_TeleportMove(self, spot->x, spot->y, finalz, Flags & TF_TELEFRAG); - - if (Flags & TF_FORCED) - { - //If for some reason the original move didn't work, regardless of telefrag, force it to move. - self->SetOrigin(spot->x, spot->y, finalz); - teleResult = true; - } - - if (teleResult) - { - //If a fog type is defined in the parameter, or the user wants to use the actor's predefined fogs, - //and if there's no desire to be fogless, spawn a fog based upon settings. - if (FogType || (Flags & TF_USEACTORFOG)) - { - if (!(Flags & TF_NOSRCFOG)) - { - if (Flags & TF_USEACTORFOG) - P_SpawnTeleportFog(self, prevX, prevY, prevZ, true); - else - Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); - } - if (!(Flags & TF_NODESTFOG)) - { - if (Flags & TF_USEACTORFOG) - P_SpawnTeleportFog(self, self->x, self->y, self->z, false); - else - Spawn(FogType, self->x, self->y, self->z, ALLOW_REPLACE); - } - } - - if (Flags & TF_USESPOTZ) - self->z = spot->z; - else - self->z = self->floorz; - - if (!(Flags & TF_KEEPANGLE)) - self->angle = spot->angle; - - if (!(Flags & TF_KEEPVELOCITY)) - self->velx = self->vely = self->velz = 0; - - if (!(Flags & TF_NOJUMP)) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (TeleportState == NULL) - { - // Default to Teleport. - TeleportState = self->FindState("Teleport"); - // If still nothing, then return. - if (!TeleportState) return; - } - ACTION_JUMP(TeleportState); - return; - } - } - ACTION_SET_RESULT(teleResult); -} - -//=========================================================================== -// -// A_Turn -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_ANGLE(angle, 0); - self->angle += angle; -} - -//=========================================================================== -// -// A_Quake -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(intensity, 0); - ACTION_PARAM_INT(duration, 1); - ACTION_PARAM_INT(damrad, 2); - ACTION_PARAM_INT(tremrad, 3); - ACTION_PARAM_SOUND(sound, 4); - P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); -} - -//=========================================================================== -// -// A_Weave -// -//=========================================================================== - -void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) -{ - fixed_t newX, newY; - int weaveXY, weaveZ; - int angle; - fixed_t dist; - - weaveXY = self->WeaveIndexXY & 63; - weaveZ = self->WeaveIndexZ & 63; - angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; - - if (xydist != 0 && xyspeed != 0) - { - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX = self->x - FixedMul (finecosine[angle], dist); - newY = self->y - FixedMul (finesine[angle], dist); - weaveXY = (weaveXY + xyspeed) & 63; - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX += FixedMul (finecosine[angle], dist); - newY += FixedMul (finesine[angle], dist); - if (!(self->flags5 & MF5_NOINTERACTION)) - { - P_TryMove (self, newX, newY, true); - } - else - { - self->UnlinkFromWorld (); - self->flags |= MF_NOBLOCKMAP; - self->x = newX; - self->y = newY; - self->LinkToWorld (); - } - self->WeaveIndexXY = weaveXY; - } - if (zdist != 0 && zspeed != 0) - { - self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - weaveZ = (weaveZ + zspeed) & 63; - self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - self->WeaveIndexZ = weaveZ; - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(xspeed, 0); - ACTION_PARAM_INT(yspeed, 1); - ACTION_PARAM_FIXED(xdist, 2); - ACTION_PARAM_FIXED(ydist, 3); - A_Weave(self, xspeed, yspeed, xdist, ydist); -} - - - - -//=========================================================================== -// -// A_LineEffect -// -// This allows linedef effects to be activated inside deh frames. -// -//=========================================================================== - - -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(tag, 1); - - line_t junk; maplinedef_t oldjunk; - bool res = false; - if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up - { - if ((oldjunk.special = special)) // Linedef type - { - oldjunk.tag = tag; // Sector tag for linedef - P_TranslateLineDef(&junk, &oldjunk); // Turn into native type - res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], - junk.args[1], junk.args[2], junk.args[3], junk.args[4]); - if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, - self->flags6 |= MF6_LINEDONE; // no more for this thing - } - } - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// A Wolf3D-style attack codepointer -// -//========================================================================== -enum WolfAttackFlags -{ - WAF_NORANDOM = 1, - WAF_USEPUFF = 2, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) -{ - ACTION_PARAM_START(9); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_SOUND(sound, 1); - ACTION_PARAM_FIXED(snipe, 2); - ACTION_PARAM_INT(maxdamage, 3); - ACTION_PARAM_INT(blocksize, 4); - ACTION_PARAM_INT(pointblank, 5); - ACTION_PARAM_INT(longrange, 6); - ACTION_PARAM_FIXED(runspeed, 7); - ACTION_PARAM_CLASS(pufftype, 8); - - if (!self->target) - return; - - // Enemy can't see target - if (!P_CheckSight(self, self->target)) - return; - - A_FaceTarget (self); - - - // Target can dodge if it can see enemy - angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; - angle >>= 24; - bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); - - // Distance check is simplistic - fixed_t dx = abs (self->x - self->target->x); - fixed_t dy = abs (self->y - self->target->y); - fixed_t dz; - fixed_t dist = dx > dy ? dx : dy; - - // Some enemies are more precise - dist = FixedMul(dist, snipe); - - // Convert distance into integer number of blocks - dist >>= FRACBITS; - dist /= blocksize; - - // Now for the speed accuracy thingie - fixed_t speed = FixedMul(self->target->velx, self->target->velx) - + FixedMul(self->target->vely, self->target->vely) - + FixedMul(self->target->velz, self->target->velz); - int hitchance = speed < runspeed ? 256 : 160; - - // Distance accuracy (factoring dodge) - hitchance -= dist * (dodge ? 16 : 8); - - // While we're here, we may as well do something for this: - if (self->target->flags & MF_SHADOW) - { - hitchance >>= 2; - } - - // The attack itself - if (pr_cabullet() < hitchance) - { - // Compute position for spawning blood/puff - dx = self->target->x; - dy = self->target->y; - dz = self->target->z + (self->target->height>>1); - angle = R_PointToAngle2(dx, dy, self->x, self->y); - - dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); - dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); - - int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); - if (dist >= pointblank) - damage >>= 1; - if (dist >= longrange) - damage >>= 1; - FName mod = NAME_None; - bool spawnblood = !((self->target->flags & MF_NOBLOOD) - || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); - if (flags & WAF_USEPUFF && pufftype) - { - AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); - mod = dpuff->DamageType; - - if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) - damage = 0; - - if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) - { - spawnblood = false; - P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); - } - } - else if (self->target->flags3 & MF3_GHOST) - damage >>= 2; - if (damage) - { - int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); - if (spawnblood) - { - P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); - P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); - } - } - } - - // And finally, let's play the sound - S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); -} - - -//========================================================================== -// -// A_Warp -// -//========================================================================== - -enum WARPF -{ - WARPF_ABSOLUTEOFFSET = 0x1, - WARPF_ABSOLUTEANGLE = 0x2, - WARPF_USECALLERANGLE = 0x4, - - WARPF_NOCHECKPOSITION = 0x8, - - WARPF_INTERPOLATE = 0x10, - WARPF_WARPINTERPOLATION = 0x20, - WARPF_COPYINTERPOLATION = 0x40, - - WARPF_STOP = 0x80, - WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200, - WARPF_ABSOLUTEPOSITION = 0x400, - WARPF_BOB = 0x800, - WARPF_MOVEPTR = 0x1000, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) -{ - ACTION_PARAM_START(7); - - ACTION_PARAM_INT(destination_selector, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_INT(flags, 5); - ACTION_PARAM_STATE(success_state, 6); - - AActor *reference = COPY_AAPTR(self, destination_selector); - - //If there is no actor to warp to, fail. - if (!reference) - { - ACTION_SET_RESULT(false); - return; - } - - AActor *caller = self; - - if (flags & WARPF_MOVEPTR) - { - AActor *temp = reference; - reference = caller; - caller = temp; - } - - fixed_t oldx = caller->x; - fixed_t oldy = caller->y; - fixed_t oldz = caller->z; - - if (!(flags & WARPF_ABSOLUTEANGLE)) - { - angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; - } - if (!(flags & WARPF_ABSOLUTEPOSITION)) - { - if (!(flags & WARPF_ABSOLUTEOFFSET)) - { - angle_t fineangle = angle >> ANGLETOFINESHIFT; - fixed_t xofs1 = xofs; - - // (borrowed from A_SpawnItemEx, assumed workable) - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - - xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z); - - // now the caller's floorz should be appropriate for the assigned xy-position - // assigning position again with - - if (zofs) - { - // extra unlink, link and environment calculation - caller->SetOrigin( - caller->x, - caller->y, - caller->floorz + zofs); - } - else - { - // if there is no offset, there should be no ill effect from moving down to the - // already identified floor - - // A_Teleport does the same thing anyway - caller->z = caller->floorz; - } - } - else - { - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); - } - } - else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. - { - if (flags & WARPF_TOFLOOR) - { - caller->SetOrigin(xofs, yofs, caller->floorz + zofs); - } - else - { - caller->SetOrigin(xofs, yofs, zofs); - } - } - - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) - { - if (flags & WARPF_TESTONLY) - { - caller->SetOrigin(oldx, oldy, oldz); - } - else - { - caller->angle = angle; - - if (flags & WARPF_STOP) - { - caller->velx = 0; - caller->vely = 0; - caller->velz = 0; - } - - if (flags & WARPF_WARPINTERPOLATION) - { - caller->PrevX += caller->x - oldx; - caller->PrevY += caller->y - oldy; - caller->PrevZ += caller->z - oldz; - } - else if (flags & WARPF_COPYINTERPOLATION) - { - caller->PrevX = caller->x + reference->PrevX - reference->x; - caller->PrevY = caller->y + reference->PrevY - reference->y; - caller->PrevZ = caller->z + reference->PrevZ - reference->z; - } - else if (!(flags & WARPF_INTERPOLATE)) - { - caller->PrevX = caller->x; - caller->PrevY = caller->y; - caller->PrevZ = caller->z; - } - - if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) - { - caller->z += reference->GetBobOffset(); - } - } - - - if (success_state) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - // in this case, you have the statejump to help you handle all the success anyway. - ACTION_JUMP(success_state); - return; - } - - ACTION_SET_RESULT(true); - } - else - { - caller->SetOrigin(oldx, oldy, oldz); - ACTION_SET_RESULT(false); - } - -} - -//========================================================================== -// -// ACS_Named* stuff - -// -// These are exactly like their un-named line special equivalents, except -// they take strings instead of integers to indicate which script to run. -// Some of these probably aren't very useful, but they are included for -// the sake of completeness. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - - -//========================================================================== -// -// A_RadiusGive -// -// Uses code roughly similar to A_Explode (but without all the compatibility -// baggage and damage computation code to give an item to all eligible mobjs -// in range. -// -//========================================================================== -enum RadiusGiveFlags -{ - RGF_GIVESELF = 1 << 0, - RGF_PLAYERS = 1 << 1, - RGF_MONSTERS = 1 << 2, - RGF_OBJECTS = 1 << 3, - RGF_VOODOO = 1 << 4, - RGF_CORPSES = 1 << 5, - RGF_MASK = 2111, - RGF_NOTARGET = 1 << 6, - RGF_NOTRACER = 1 << 7, - RGF_NOMASTER = 1 << 8, - RGF_CUBE = 1 << 9, - RGF_NOSIGHT = 1 << 10, - RGF_MISSILES = 1 << 11, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(amount, 3); - - // We need a valid item, valid targets, and a valid range - if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) - { - return; - } - if (amount == 0) - { - amount = 1; - } - FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); - double distsquared = double(distance) * double(distance); - - AActor *thing; - while ((thing = it.Next())) - { - // Don't give to inventory items - if (thing->flags & MF_SPECIAL) - { - continue; - } - // Avoid giving to self unless requested - if (thing == self && !(flags & RGF_GIVESELF)) - { - continue; - } - // Avoiding special pointers if requested - if (((thing == self->target) && (flags & RGF_NOTARGET)) || - ((thing == self->tracer) && (flags & RGF_NOTRACER)) || - ((thing == self->master) && (flags & RGF_NOMASTER))) - { - continue; - } - // Don't give to dead thing unless requested - if (thing->flags & MF_CORPSE) - { - if (!(flags & RGF_CORPSES)) - { - continue; - } - } - else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) - { - continue; - } - // Players, monsters, and other shootable objects - if (thing->player) - { - if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) - { - continue; - } - if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) - { - continue; - } - } - else if (thing->flags3 & MF3_ISMONSTER) - { - if (!(flags & RGF_MONSTERS)) - { - continue; - } - } - else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) - { - if (!(flags & RGF_OBJECTS)) - { - continue; - } - } - else if (thing->flags & MF_MISSILE) - { - if (!(flags & RGF_MISSILES)) - { - continue; - } - } - else - { - continue; - } - - if (flags & RGF_CUBE) - { // check if inside a cube - if (abs(thing->x - self->x) > distance || - abs(thing->y - self->y) > distance || - abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) - { - continue; - } - } - else - { // check if inside a sphere - TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); - TVector3 spos(self->x, self->y, self->z + self->height/2); - if ((tpos - spos).LengthSquared() > distsquared) - { - continue; - } - } - fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); - - if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. - AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); - if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) - { - gift->Amount *= amount; - } - else - { - gift->Amount = amount; - } - gift->flags |= MF_DROPPED; - gift->ClearCounters(); - if (!gift->CallTryPickup (thing)) - { - gift->Destroy (); - } - } - } -} - - -//========================================================================== -// -// A_SetTics -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(tics_to_set, 0); - - if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) - { // Is this a weapon? Need to check psp states for a match, then. Blah. - for (int i = 0; i < NUMPSPRITES; ++i) - { - if (self->player->psprites[i].state == CallingState) - { - self->player->psprites[i].tics = tics_to_set; - return; - } - } - } - // Just set tics for self. - self->tics = tics_to_set; -} - -//========================================================================== -// -// A_SetDamageType -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(damagetype, 0); - - self->DamageType = damagetype; -} - -//========================================================================== -// -// A_DropItem -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_CLASS(spawntype, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(chance, 2); - - P_DropItem(self, spawntype, amount, chance); -} - -//========================================================================== -// -// A_SetSpeed -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(speed, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (!ref) - { - ACTION_SET_RESULT(false); - return; - } - - ref->Speed = speed; -} - -static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) -{ - return (!(species) || mo->Species == NAME_None || (species && ((exclude) ? (mo->Species != species) : (mo->Species == species)))); -} - -static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) -{ - const PClass *c1 = mo->GetClass(); - return (!(filter) || (filter == NULL) || (filter && ((exclude) ? (c1 != filter) : (c1 == filter)))); -} - -//=========================================================================== -// -// Common A_Damage handler -// -// A_Damage* (int amount, str damagetype, int flags, str filter, str species) -// Damages the specified actor by the specified amount. Negative values heal. -// Flags: See below. -// Filter: Specified actor is the only type allowed to be affected. -// Species: Specified species is the only type allowed to be affected. -// -// Examples: -// A_Damage(20,"Normal",DMSS_FOILINVUL,0,"DemonicSpecies") <--Only actors -// with a species "DemonicSpecies" will be affected. Use 0 to not filter by actor. -// -//=========================================================================== - -enum DMSS -{ - DMSS_FOILINVUL = 1, //Foil invulnerability - DMSS_AFFECTARMOR = 2, //Make it affect armor - DMSS_KILL = 4, //Damages them for their current health - DMSS_NOFACTOR = 8, //Ignore DamageFactors - DMSS_FOILBUDDHA = 16, //Can kill actors with Buddha flag, except the player. - DMSS_NOPROTECT = 32, //Ignores PowerProtection entirely - DMSS_EXFILTER = 64, //Changes filter into a blacklisted class instead of whitelisted. - DMSS_EXSPECIES = 128, // ^ but with species instead. - DMSS_EITHER = 256, //Allow either type or species to be affected. -}; - -static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); - if ((flags & DMSS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) - { - int dmgFlags = 0; - if (flags & DMSS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (flags & DMSS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor - dmgFlags += DMG_NO_FACTOR; - if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor - dmgFlags += DMG_NO_ARMOR; - if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. - amount += dmgtarget->health; - if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. - dmgFlags += DMG_NO_PROTECT; - - if (amount > 0) - P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. - - else if (amount < 0) - { - amount = -amount; - P_GiveBody(dmgtarget, amount); - } - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - DoDamage(self, self, amount, DamageType, flags, filter, species); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->target != NULL) - { - DoDamage(self->target, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->tracer != NULL) - { - DoDamage(self->tracer, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - if (self->master != NULL) - { - DoDamage(self->master, self, amount, DamageType, flags, filter, species); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - TThinkerIterator it; - AActor * mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - DoDamage(mo, self, amount, DamageType, flags, filter, species); - } - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(filter, 3); - ACTION_PARAM_NAME(species, 4); - - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) - { - DoDamage(mo, self, amount, DamageType, flags, filter, species); - } - } - } -} - - -//=========================================================================== -// -// A_Kill*(damagetype, int flags) -// -//=========================================================================== -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, - KILS_FOILBUDDHA = 1 << 3, - KILS_EXFILTER = 1 << 4, - KILS_EXSPECIES = 1 << 5, - KILS_EITHER = 1 << 6, -}; - -static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); - if ((flags & KILS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) //Check this first. I think it'll save the engine a lot more time this way. - { - int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; - - if (KILS_FOILINVUL) - dmgFlags += DMG_FOILINVUL; - if (KILS_FOILBUDDHA) - dmgFlags += DMG_FOILBUDDHA; - - - if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && - (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(killtarget, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); - } - } -} - - -//=========================================================================== -// -// A_KillTarget(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->target != NULL) - { - DoKill(self->target, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillTracer(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->tracer != NULL) - { - DoKill(self->tracer, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->master != NULL) - { - DoKill(self->master, self, damagetype, flags, filter, species); - } -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - DoKill(mo, self, damagetype, flags, filter, species); - } - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) - { - DoKill(mo, self, damagetype, flags, filter, species); - } - } - } -} - -//=========================================================================== -// -// DoRemove -// -//=========================================================================== - -enum RMVF_flags -{ - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3, - RMVF_EXFILTER = 1 << 4, - RMVF_EXSPECIES = 1 << 5, - RMVF_EITHER = 1 << 6, -}; - -static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) -{ - bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), - speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); - if ((flags & RMVF_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) - { - if ((flags & RMVF_EVERYTHING)) - { - P_RemoveThing(removetarget); - } - if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) - { - P_RemoveThing(removetarget); - } - if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) - { - P_RemoveThing(removetarget); - } - } -} - -//=========================================================================== -// -// A_RemoveTarget -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->target != NULL) - { - DoRemove(self->target, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveTracer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->tracer != NULL) - { - DoRemove(self->tracer, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_CLASS(filter, 1); - ACTION_PARAM_NAME(species, 2); - - if (self->master != NULL) - { - DoRemove(self->master, flags, filter, species); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(4); - ACTION_PARAM_BOOL(removeall, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - DoRemove(mo, flags, filter, species); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(4); - ACTION_PARAM_BOOL(removeall, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - DoRemove(mo, flags, filter, species); - } - } - } -} - -//=========================================================================== -// -// A_Remove -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(removee, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_CLASS(filter, 2); - ACTION_PARAM_NAME(species, 3); - - AActor *reference = COPY_AAPTR(self, removee); - if (reference != NULL) - { - DoRemove(reference, flags, filter, species); - } -} - -//=========================================================================== -// -// A_SetTeleFog -// -// Sets the teleport fog(s) for the calling actor. -// Takes a name of the classes for te source and destination. -// Can set both at the same time. Use "" to retain the previous fog without -// changing it. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(oldpos, 0); - ACTION_PARAM_NAME(newpos, 1); - const PClass *check = PClass::FindClass(oldpos); - if (check == NULL || !stricmp(oldpos, "none") || !stricmp(oldpos, "null")) - self->TeleFogSourceType = NULL; - else if (!stricmp(oldpos, "")) - { //Don't change it if it's just "" - } - else - self->TeleFogSourceType = check; - - check = PClass::FindClass(newpos); - if (check == NULL || !stricmp(newpos, "none") || !stricmp(newpos, "null")) - self->TeleFogDestType = NULL; - else if (!stricmp(newpos, "")) - { //Don't change it if it's just "" - } - else - self->TeleFogDestType = check; -} - -//=========================================================================== -// -// A_SwapTeleFog -// -// Switches the source and dest telefogs around. -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) -{ - if ((self->TeleFogSourceType != self->TeleFogDestType)) //Does nothing if they're the same. - { - const PClass *temp = self->TeleFogSourceType; - self->TeleFogSourceType = self->TeleFogDestType; - self->TeleFogDestType = temp; - } -} - -//=========================================================================== -// -// A_SetFloatBobPhase -// -// Changes the FloatBobPhase of the actor. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(bob, 0); - - //Respect float bob phase limits. - if (self && (bob >= 0 && bob <= 63)) - self->FloatBobPhase = bob; -} - -//=========================================================================== -// A_SetHealth -// -// Changes the health of the actor. -// Takes a pointer as well. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(health, 0); - ACTION_PARAM_INT(ptr, 1); - - AActor *mobj = COPY_AAPTR(self, ptr); - - if (!mobj) - { - return; - } - - player_t *player = mobj->player; - if (player) - { - if (health <= 0) - player->mo->health = mobj->health = player->health = 1; //Copied from the buddha cheat. - else - player->mo->health = mobj->health = player->health = health; - } - else if (mobj) - { - if (health <= 0) - mobj->health = 1; - else - mobj->health = health; - } -} - -//=========================================================================== -// A_ResetHealth -// -// Resets the health of the actor to default, except if their dead. -// Takes a pointer. -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(ptr, 0); - - AActor *mobj = COPY_AAPTR(self, ptr); - - if (!mobj) - { - return; - } - - player_t *player = mobj->player; - if (player && (player->mo->health > 0)) - { - player->health = player->mo->health = player->mo->GetDefault()->health; //Copied from the resurrect cheat. - } - else if (mobj && (mobj->health > 0)) - { - mobj->health = mobj->SpawnHealth(); - } -} - -//=========================================================================== -// -// A_SetRipperLevel(int level) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(level, 0); - self->RipperLevel = level; -} - -//=========================================================================== -// -// A_SetRipMin(int min) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(min, 1); - self->RipLevelMin = min; -} - -//=========================================================================== -// -// A_SetRipMin(int min) -// -// Sets the ripper level/requirement of the calling actor. -// Also sets the minimum and maximum levels to rip through. -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(max, 1); - self->RipLevelMax = max; + +//=========================================================================== +// +// A_ScaleVelocity +// +// Scale actor's velocity. +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(scale, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + INTBOOL was_moving = self->velx | self->vely | self->velz; + + ref->velx = FixedMul(ref->velx, scale); + ref->vely = FixedMul(ref->vely, scale); + ref->velz = FixedMul(ref->velz, scale); + + // If the actor was previously moving but now is not, and is a player, + // update its player variables. (See A_Stop.) + if (was_moving) + { + CheckStopped(ref); + } +} + +//=========================================================================== +// +// A_ChangeVelocity +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_FIXED(x, 0); + ACTION_PARAM_FIXED(y, 1); + ACTION_PARAM_FIXED(z, 2); + ACTION_PARAM_INT(flags, 3); + ACTION_PARAM_INT(ptr, 4); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + INTBOOL was_moving = ref->velx | ref->vely | ref->velz; + + fixed_t vx = x, vy = y, vz = z; + fixed_t sina = finesine[ref->angle >> ANGLETOFINESHIFT]; + fixed_t cosa = finecosine[ref->angle >> ANGLETOFINESHIFT]; + + if (flags & 1) // relative axes - make x, y relative to actor's current angle + { + vx = DMulScale16(x, cosa, -y, sina); + vy = DMulScale16(x, sina, y, cosa); + } + if (flags & 2) // discard old velocity - replace old velocity with new velocity + { + ref->velx = vx; + ref->vely = vy; + ref->velz = vz; + } + else // add new velocity to old velocity + { + ref->velx += vx; + ref->vely += vy; + ref->velz += vz; + } + + if (was_moving) + { + CheckStopped(self); + } +} + +//=========================================================================== +// +// A_SetArg +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(pos, 0); + ACTION_PARAM_INT(value, 1); + + // Set the value of the specified arg + if ((size_t)pos < countof(self->args)) + { + self->args[pos] = value; + } +} + +//=========================================================================== +// +// A_SetSpecial +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(spec, 0); + ACTION_PARAM_INT(arg0, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + ACTION_PARAM_INT(arg4, 5); + + self->special = spec; + self->args[0] = arg0; + self->args[1] = arg1; + self->args[2] = arg2; + self->args[3] = arg3; + self->args[4] = arg4; +} + +//=========================================================================== +// +// A_SetUserVar +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(value, 1); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Int) + { + Printf("%s is not a user variable in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user variable. + *(int *)(reinterpret_cast(self) + var->offset) = value; +} + +//=========================================================================== +// +// A_SetUserArray +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(pos, 1); + ACTION_PARAM_INT(value, 2); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) + { + Printf("%s is not a user array in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + if (pos < 0 || pos >= var->ValueType.size) + { + Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user array at index pos. + ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; +} + +//=========================================================================== +// +// A_Teleport(optional state teleportstate, optional class targettype, +// optional class fogtype, optional int flags, optional fixed mindist, +// optional fixed maxdist) +// +// Attempts to teleport to a targettype at least mindist away and at most +// maxdist away (0 means unlimited). If successful, spawn a fogtype at old +// location and place calling actor in teleportstate. +// +//=========================================================================== +enum T_Flags +{ + TF_TELEFRAG = 0x00000001, // Allow telefrag in order to teleport. + TF_RANDOMDECIDE = 0x00000002, // Randomly fail based on health. (A_Srcr2Decide) + TF_FORCED = 0x00000004, // Forget what's in the way. TF_Telefrag takes precedence though. + TF_KEEPVELOCITY = 0x00000008, // Preserve velocity. + TF_KEEPANGLE = 0x00000010, // Keep angle. + TF_USESPOTZ = 0x00000020, // Set the z to the spot's z, instead of the floor. + TF_NOSRCFOG = 0x00000040, // Don't leave any fog behind when teleporting. + TF_NODESTFOG = 0x00000080, // Don't spawn any fog at the arrival position. + TF_USEACTORFOG = 0x00000100, // Use the actor's TeleFogSourceType and TeleFogDestType fogs. + TF_NOJUMP = 0x00000200, // Don't jump after teleporting. +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(TeleportState, 0); + ACTION_PARAM_CLASS(TargetType, 1); + ACTION_PARAM_CLASS(FogType, 2); + ACTION_PARAM_INT(Flags, 3); + ACTION_PARAM_FIXED(MinDist, 4); + ACTION_PARAM_FIXED(MaxDist, 5); + + // Randomly choose not to teleport like A_Srcr2Decide. + if (Flags & TF_RANDOMDECIDE) + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); + + if (chanceindex >= countof(chance)) + { + chanceindex = countof(chance) - 1; + } + + if (pr_teleport() >= chance[chanceindex]) return; + } + + DSpotState *state = DSpotState::GetSpotState(); + if (state == NULL) return; + + if (!TargetType) TargetType = PClass::FindClass("BossSpot"); + + AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); + if (spot == NULL) return; + + fixed_t prevX = self->x; + fixed_t prevY = self->y; + fixed_t prevZ = self->z; + fixed_t aboveFloor = spot->z - spot->floorz; + fixed_t finalz = spot->floorz + aboveFloor; + + if (spot->z + self->height > spot->ceilingz) + finalz = spot->ceilingz - self->height; + else if (spot->z < spot->floorz) + finalz = spot->floorz; + + + //Take precedence and cooperate with telefragging first. + bool teleResult = P_TeleportMove(self, spot->x, spot->y, finalz, Flags & TF_TELEFRAG); + + if (Flags & TF_FORCED) + { + //If for some reason the original move didn't work, regardless of telefrag, force it to move. + self->SetOrigin(spot->x, spot->y, finalz); + teleResult = true; + } + + if (teleResult) + { + //If a fog type is defined in the parameter, or the user wants to use the actor's predefined fogs, + //and if there's no desire to be fogless, spawn a fog based upon settings. + if (FogType || (Flags & TF_USEACTORFOG)) + { + if (!(Flags & TF_NOSRCFOG)) + { + if (Flags & TF_USEACTORFOG) + P_SpawnTeleportFog(self, prevX, prevY, prevZ, true); + else + Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + } + if (!(Flags & TF_NODESTFOG)) + { + if (Flags & TF_USEACTORFOG) + P_SpawnTeleportFog(self, self->x, self->y, self->z, false); + else + Spawn(FogType, self->x, self->y, self->z, ALLOW_REPLACE); + } + } + + if (Flags & TF_USESPOTZ) + self->z = spot->z; + else + self->z = self->floorz; + + if (!(Flags & TF_KEEPANGLE)) + self->angle = spot->angle; + + if (!(Flags & TF_KEEPVELOCITY)) + self->velx = self->vely = self->velz = 0; + + if (!(Flags & TF_NOJUMP)) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (TeleportState == NULL) + { + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) return; + } + ACTION_JUMP(TeleportState); + return; + } + } + ACTION_SET_RESULT(teleResult); +} + +//=========================================================================== +// +// A_Turn +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_ANGLE(angle, 0); + self->angle += angle; +} + +//=========================================================================== +// +// A_Quake +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(intensity, 0); + ACTION_PARAM_INT(duration, 1); + ACTION_PARAM_INT(damrad, 2); + ACTION_PARAM_INT(tremrad, 3); + ACTION_PARAM_SOUND(sound, 4); + P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); +} + +//=========================================================================== +// +// A_Weave +// +//=========================================================================== + +void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + fixed_t dist; + + weaveXY = self->WeaveIndexXY & 63; + weaveZ = self->WeaveIndexZ & 63; + angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; + + if (xydist != 0 && xyspeed != 0) + { + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX = self->x - FixedMul (finecosine[angle], dist); + newY = self->y - FixedMul (finesine[angle], dist); + weaveXY = (weaveXY + xyspeed) & 63; + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX += FixedMul (finecosine[angle], dist); + newY += FixedMul (finesine[angle], dist); + if (!(self->flags5 & MF5_NOINTERACTION)) + { + P_TryMove (self, newX, newY, true); + } + else + { + self->UnlinkFromWorld (); + self->flags |= MF_NOBLOCKMAP; + self->x = newX; + self->y = newY; + self->LinkToWorld (); + } + self->WeaveIndexXY = weaveXY; + } + if (zdist != 0 && zspeed != 0) + { + self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + weaveZ = (weaveZ + zspeed) & 63; + self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + self->WeaveIndexZ = weaveZ; + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(xspeed, 0); + ACTION_PARAM_INT(yspeed, 1); + ACTION_PARAM_FIXED(xdist, 2); + ACTION_PARAM_FIXED(ydist, 3); + A_Weave(self, xspeed, yspeed, xdist, ydist); +} + + + + +//=========================================================================== +// +// A_LineEffect +// +// This allows linedef effects to be activated inside deh frames. +// +//=========================================================================== + + +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(tag, 1); + + line_t junk; maplinedef_t oldjunk; + bool res = false; + if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up + { + if ((oldjunk.special = special)) // Linedef type + { + oldjunk.tag = tag; // Sector tag for linedef + P_TranslateLineDef(&junk, &oldjunk); // Turn into native type + res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], + junk.args[1], junk.args[2], junk.args[3], junk.args[4]); + if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, + self->flags6 |= MF6_LINEDONE; // no more for this thing + } + } + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// A Wolf3D-style attack codepointer +// +//========================================================================== +enum WolfAttackFlags +{ + WAF_NORANDOM = 1, + WAF_USEPUFF = 2, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) +{ + ACTION_PARAM_START(9); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_SOUND(sound, 1); + ACTION_PARAM_FIXED(snipe, 2); + ACTION_PARAM_INT(maxdamage, 3); + ACTION_PARAM_INT(blocksize, 4); + ACTION_PARAM_INT(pointblank, 5); + ACTION_PARAM_INT(longrange, 6); + ACTION_PARAM_FIXED(runspeed, 7); + ACTION_PARAM_CLASS(pufftype, 8); + + if (!self->target) + return; + + // Enemy can't see target + if (!P_CheckSight(self, self->target)) + return; + + A_FaceTarget (self); + + + // Target can dodge if it can see enemy + angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; + angle >>= 24; + bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); + + // Distance check is simplistic + fixed_t dx = abs (self->x - self->target->x); + fixed_t dy = abs (self->y - self->target->y); + fixed_t dz; + fixed_t dist = dx > dy ? dx : dy; + + // Some enemies are more precise + dist = FixedMul(dist, snipe); + + // Convert distance into integer number of blocks + dist >>= FRACBITS; + dist /= blocksize; + + // Now for the speed accuracy thingie + fixed_t speed = FixedMul(self->target->velx, self->target->velx) + + FixedMul(self->target->vely, self->target->vely) + + FixedMul(self->target->velz, self->target->velz); + int hitchance = speed < runspeed ? 256 : 160; + + // Distance accuracy (factoring dodge) + hitchance -= dist * (dodge ? 16 : 8); + + // While we're here, we may as well do something for this: + if (self->target->flags & MF_SHADOW) + { + hitchance >>= 2; + } + + // The attack itself + if (pr_cabullet() < hitchance) + { + // Compute position for spawning blood/puff + dx = self->target->x; + dy = self->target->y; + dz = self->target->z + (self->target->height>>1); + angle = R_PointToAngle2(dx, dy, self->x, self->y); + + dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); + dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); + + int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); + if (dist >= pointblank) + damage >>= 1; + if (dist >= longrange) + damage >>= 1; + FName mod = NAME_None; + bool spawnblood = !((self->target->flags & MF_NOBLOOD) + || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); + if (flags & WAF_USEPUFF && pufftype) + { + AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); + mod = dpuff->DamageType; + + if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) + damage = 0; + + if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) + { + spawnblood = false; + P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); + } + } + else if (self->target->flags3 & MF3_GHOST) + damage >>= 2; + if (damage) + { + int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); + if (spawnblood) + { + P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); + P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); + } + } + } + + // And finally, let's play the sound + S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); +} + + +//========================================================================== +// +// A_Warp +// +//========================================================================== + +enum WARPF +{ + WARPF_ABSOLUTEOFFSET = 0x1, + WARPF_ABSOLUTEANGLE = 0x2, + WARPF_USECALLERANGLE = 0x4, + + WARPF_NOCHECKPOSITION = 0x8, + + WARPF_INTERPOLATE = 0x10, + WARPF_WARPINTERPOLATION = 0x20, + WARPF_COPYINTERPOLATION = 0x40, + + WARPF_STOP = 0x80, + WARPF_TOFLOOR = 0x100, + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, + WARPF_BOB = 0x800, + WARPF_MOVEPTR = 0x1000, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) +{ + ACTION_PARAM_START(7); + + ACTION_PARAM_INT(destination_selector, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_INT(flags, 5); + ACTION_PARAM_STATE(success_state, 6); + + AActor *reference = COPY_AAPTR(self, destination_selector); + + //If there is no actor to warp to, fail. + if (!reference) + { + ACTION_SET_RESULT(false); + return; + } + + AActor *caller = self; + + if (flags & WARPF_MOVEPTR) + { + AActor *temp = reference; + reference = caller; + caller = temp; + } + + fixed_t oldx = caller->x; + fixed_t oldy = caller->y; + fixed_t oldz = caller->z; + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; + } + if (!(flags & WARPF_ABSOLUTEPOSITION)) + { + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + angle_t fineangle = angle >> ANGLETOFINESHIFT; + fixed_t xofs1 = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + + caller->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z); + + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with + + if (zofs) + { + // extra unlink, link and environment calculation + caller->SetOrigin( + caller->x, + caller->y, + caller->floorz + zofs); + } + else + { + // if there is no offset, there should be no ill effect from moving down to the + // already identified floor + + // A_Teleport does the same thing anyway + caller->z = caller->floorz; + } + } + else + { + caller->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); + } + } + else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. + { + if (flags & WARPF_TOFLOOR) + { + caller->SetOrigin(xofs, yofs, caller->floorz + zofs); + } + else + { + caller->SetOrigin(xofs, yofs, zofs); + } + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) + { + if (flags & WARPF_TESTONLY) + { + caller->SetOrigin(oldx, oldy, oldz); + } + else + { + caller->angle = angle; + + if (flags & WARPF_STOP) + { + caller->velx = 0; + caller->vely = 0; + caller->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + caller->PrevX += caller->x - oldx; + caller->PrevY += caller->y - oldy; + caller->PrevZ += caller->z - oldz; + } + else if (flags & WARPF_COPYINTERPOLATION) + { + caller->PrevX = caller->x + reference->PrevX - reference->x; + caller->PrevY = caller->y + reference->PrevY - reference->y; + caller->PrevZ = caller->z + reference->PrevZ - reference->z; + } + else if (!(flags & WARPF_INTERPOLATE)) + { + caller->PrevX = caller->x; + caller->PrevY = caller->y; + caller->PrevZ = caller->z; + } + + if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) + { + caller->z += reference->GetBobOffset(); + } + } + + + if (success_state) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + // in this case, you have the statejump to help you handle all the success anyway. + ACTION_JUMP(success_state); + return; + } + + ACTION_SET_RESULT(true); + } + else + { + caller->SetOrigin(oldx, oldy, oldz); + ACTION_SET_RESULT(false); + } + +} + +//========================================================================== +// +// ACS_Named* stuff + +// +// These are exactly like their un-named line special equivalents, except +// they take strings instead of integers to indicate which script to run. +// Some of these probably aren't very useful, but they are included for +// the sake of completeness. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + + +//========================================================================== +// +// A_RadiusGive +// +// Uses code roughly similar to A_Explode (but without all the compatibility +// baggage and damage computation code to give an item to all eligible mobjs +// in range. +// +//========================================================================== +enum RadiusGiveFlags +{ + RGF_GIVESELF = 1 << 0, + RGF_PLAYERS = 1 << 1, + RGF_MONSTERS = 1 << 2, + RGF_OBJECTS = 1 << 3, + RGF_VOODOO = 1 << 4, + RGF_CORPSES = 1 << 5, + RGF_MASK = 2111, + RGF_NOTARGET = 1 << 6, + RGF_NOTRACER = 1 << 7, + RGF_NOMASTER = 1 << 8, + RGF_CUBE = 1 << 9, + RGF_NOSIGHT = 1 << 10, + RGF_MISSILES = 1 << 11, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(amount, 3); + + // We need a valid item, valid targets, and a valid range + if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) + { + return; + } + if (amount == 0) + { + amount = 1; + } + FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); + double distsquared = double(distance) * double(distance); + + AActor *thing; + while ((thing = it.Next())) + { + // Don't give to inventory items + if (thing->flags & MF_SPECIAL) + { + continue; + } + // Avoid giving to self unless requested + if (thing == self && !(flags & RGF_GIVESELF)) + { + continue; + } + // Avoiding special pointers if requested + if (((thing == self->target) && (flags & RGF_NOTARGET)) || + ((thing == self->tracer) && (flags & RGF_NOTRACER)) || + ((thing == self->master) && (flags & RGF_NOMASTER))) + { + continue; + } + // Don't give to dead thing unless requested + if (thing->flags & MF_CORPSE) + { + if (!(flags & RGF_CORPSES)) + { + continue; + } + } + else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) + { + continue; + } + // Players, monsters, and other shootable objects + if (thing->player) + { + if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) + { + continue; + } + if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) + { + continue; + } + } + else if (thing->flags3 & MF3_ISMONSTER) + { + if (!(flags & RGF_MONSTERS)) + { + continue; + } + } + else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) + { + if (!(flags & RGF_OBJECTS)) + { + continue; + } + } + else if (thing->flags & MF_MISSILE) + { + if (!(flags & RGF_MISSILES)) + { + continue; + } + } + else + { + continue; + } + + if (flags & RGF_CUBE) + { // check if inside a cube + if (abs(thing->x - self->x) > distance || + abs(thing->y - self->y) > distance || + abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) + { + continue; + } + } + else + { // check if inside a sphere + TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); + TVector3 spos(self->x, self->y, self->z + self->height/2); + if ((tpos - spos).LengthSquared() > distsquared) + { + continue; + } + } + fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); + + if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. + AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); + if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + { + gift->Amount *= amount; + } + else + { + gift->Amount = amount; + } + gift->flags |= MF_DROPPED; + gift->ClearCounters(); + if (!gift->CallTryPickup (thing)) + { + gift->Destroy (); + } + } + } +} + + +//========================================================================== +// +// A_SetTics +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(tics_to_set, 0); + + if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) + { // Is this a weapon? Need to check psp states for a match, then. Blah. + for (int i = 0; i < NUMPSPRITES; ++i) + { + if (self->player->psprites[i].state == CallingState) + { + self->player->psprites[i].tics = tics_to_set; + return; + } + } + } + // Just set tics for self. + self->tics = tics_to_set; +} + +//========================================================================== +// +// A_SetDamageType +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(damagetype, 0); + + self->DamageType = damagetype; +} + +//========================================================================== +// +// A_DropItem +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_CLASS(spawntype, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(chance, 2); + + P_DropItem(self, spawntype, amount, chance); +} + +//========================================================================== +// +// A_SetSpeed +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(speed, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->Speed = speed; +} + +static bool DoCheckSpecies(AActor *mo, FName species, bool exclude) +{ + return (!(species) || mo->Species == NAME_None || (species && ((exclude) ? (mo->Species != species) : (mo->Species == species)))); +} + +static bool DoCheckFilter(AActor *mo, const PClass *filter, bool exclude) +{ + const PClass *c1 = mo->GetClass(); + return (!(filter) || (filter == NULL) || (filter && ((exclude) ? (c1 != filter) : (c1 == filter)))); +} + +//=========================================================================== +// +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags, str filter, str species) +// Damages the specified actor by the specified amount. Negative values heal. +// Flags: See below. +// Filter: Specified actor is the only type allowed to be affected. +// Species: Specified species is the only type allowed to be affected. +// +// Examples: +// A_Damage(20,"Normal",DMSS_FOILINVUL,0,"DemonicSpecies") <--Only actors +// with a species "DemonicSpecies" will be affected. Use 0 to not filter by actor. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, //Foil invulnerability + DMSS_AFFECTARMOR = 2, //Make it affect armor + DMSS_KILL = 4, //Damages them for their current health + DMSS_NOFACTOR = 8, //Ignore DamageFactors + DMSS_FOILBUDDHA = 16, //Can kill actors with Buddha flag, except the player. + DMSS_NOPROTECT = 32, //Ignores PowerProtection entirely + DMSS_EXFILTER = 64, //Changes filter into a blacklisted class instead of whitelisted. + DMSS_EXSPECIES = 128, // ^ but with species instead. + DMSS_EITHER = 256, //Allow either type or species to be affected. +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(dmgtarget, filter, (flags & DMSS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(dmgtarget, species, (flags & DMSS_EXSPECIES) ? true : false); + if ((flags & DMSS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) + { + int dmgFlags = 0; + if (flags & DMSS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (flags & DMSS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor + dmgFlags += DMG_NO_FACTOR; + if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor + dmgFlags += DMG_NO_ARMOR; + if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. + amount += dmgtarget->health; + if (flags & DMSS_NOPROTECT) //Ignore PowerProtection. + dmgFlags += DMG_NO_PROTECT; + + if (amount > 0) + P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. + + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + DoDamage(self, self, amount, DamageType, flags, filter, species); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->target != NULL) + { + DoDamage(self->target, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->tracer != NULL) + { + DoDamage(self->tracer, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + if (self->master != NULL) + { + DoDamage(self->master, self, amount, DamageType, flags, filter, species); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) + { + DoDamage(mo, self, amount, DamageType, flags, filter, species); + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(filter, 3); + ACTION_PARAM_NAME(species, 4); + + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) + { + DoDamage(mo, self, amount, DamageType, flags, filter, species); + } + } + } +} + + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, + KILS_FOILBUDDHA = 1 << 3, + KILS_EXFILTER = 1 << 4, + KILS_EXSPECIES = 1 << 5, + KILS_EITHER = 1 << 6, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(killtarget, filter, (flags & KILS_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(killtarget, species, (flags & KILS_EXSPECIES) ? true : false); + if ((flags & KILS_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) //Check this first. I think it'll save the engine a lot more time this way. + { + int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; + + if (KILS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (KILS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + + + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && + (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(killtarget, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); + } + } +} + + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->target != NULL) + { + DoKill(self->target, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->tracer != NULL) + { + DoKill(self->tracer, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->master != NULL) + { + DoKill(self->master, self, damagetype, flags, filter, species); + } +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) + { + DoKill(mo, self, damagetype, flags, filter, species); + } + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) + { + if (mo->master == self->master && mo != self) + { + DoKill(mo, self, damagetype, flags, filter, species); + } + } + } +} + +//=========================================================================== +// +// DoRemove +// +//=========================================================================== + +enum RMVF_flags +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, + RMVF_EXFILTER = 1 << 4, + RMVF_EXSPECIES = 1 << 5, + RMVF_EITHER = 1 << 6, +}; + +static void DoRemove(AActor *removetarget, int flags, const PClass *filter, FName species) +{ + bool filterpass = DoCheckFilter(removetarget, filter, (flags & RMVF_EXFILTER) ? true : false), + speciespass = DoCheckSpecies(removetarget, species, (flags & RMVF_EXSPECIES) ? true : false); + if ((flags & RMVF_EITHER) ? (filterpass || speciespass) : (filterpass && speciespass)) + { + if ((flags & RMVF_EVERYTHING)) + { + P_RemoveThing(removetarget); + } + if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) + { + P_RemoveThing(removetarget); + } + } +} + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->target != NULL) + { + DoRemove(self->target, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->tracer != NULL) + { + DoRemove(self->tracer, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_CLASS(filter, 1); + ACTION_PARAM_NAME(species, 2); + + if (self->master != NULL) + { + DoRemove(self->master, flags, filter, species); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(4); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags, filter, species); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(4); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags, filter, species); + } + } + } +} + +//=========================================================================== +// +// A_Remove +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(removee, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_CLASS(filter, 2); + ACTION_PARAM_NAME(species, 3); + + AActor *reference = COPY_AAPTR(self, removee); + if (reference != NULL) + { + DoRemove(reference, flags, filter, species); + } +} + +//=========================================================================== +// +// A_SetTeleFog +// +// Sets the teleport fog(s) for the calling actor. +// Takes a name of the classes for te source and destination. +// Can set both at the same time. Use "" to retain the previous fog without +// changing it. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(oldpos, 0); + ACTION_PARAM_NAME(newpos, 1); + const PClass *check = PClass::FindClass(oldpos); + if (check == NULL || !stricmp(oldpos, "none") || !stricmp(oldpos, "null")) + self->TeleFogSourceType = NULL; + else if (!stricmp(oldpos, "")) + { //Don't change it if it's just "" + } + else + self->TeleFogSourceType = check; + + check = PClass::FindClass(newpos); + if (check == NULL || !stricmp(newpos, "none") || !stricmp(newpos, "null")) + self->TeleFogDestType = NULL; + else if (!stricmp(newpos, "")) + { //Don't change it if it's just "" + } + else + self->TeleFogDestType = check; +} + +//=========================================================================== +// +// A_SwapTeleFog +// +// Switches the source and dest telefogs around. +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) +{ + if ((self->TeleFogSourceType != self->TeleFogDestType)) //Does nothing if they're the same. + { + const PClass *temp = self->TeleFogSourceType; + self->TeleFogSourceType = self->TeleFogDestType; + self->TeleFogDestType = temp; + } +} + +//=========================================================================== +// +// A_SetFloatBobPhase +// +// Changes the FloatBobPhase of the actor. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatBobPhase) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(bob, 0); + + //Respect float bob phase limits. + if (self && (bob >= 0 && bob <= 63)) + self->FloatBobPhase = bob; +} + +//=========================================================================== +// A_SetHealth +// +// Changes the health of the actor. +// Takes a pointer as well. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player) + { + if (health <= 0) + player->mo->health = mobj->health = player->health = 1; //Copied from the buddha cheat. + else + player->mo->health = mobj->health = player->health = health; + } + else if (mobj) + { + if (health <= 0) + mobj->health = 1; + else + mobj->health = health; + } +} + +//=========================================================================== +// A_ResetHealth +// +// Resets the health of the actor to default, except if their dead. +// Takes a pointer. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr, 0); + + AActor *mobj = COPY_AAPTR(self, ptr); + + if (!mobj) + { + return; + } + + player_t *player = mobj->player; + if (player && (player->mo->health > 0)) + { + player->health = player->mo->health = player->mo->GetDefault()->health; //Copied from the resurrect cheat. + } + else if (mobj && (mobj->health > 0)) + { + mobj->health = mobj->SpawnHealth(); + } +} + +//=========================================================================== +// +// A_SetRipperLevel(int level) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(level, 0); + self->RipperLevel = level; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(min, 1); + self->RipLevelMin = min; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(max, 1); + self->RipLevelMax = max; } \ No newline at end of file From b69edbbec12e8b483fb4e977dfeb041084f1b6ae Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 29 Jan 2015 19:38:16 -0600 Subject: [PATCH 117/117] Redo commit a4c07a9 - Line endings are inconsistant in this file, but at least the commit doesn't touch the whole file. --- src/thingdef/thingdef_codeptr.cpp | 49 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index be3921877..9db52f728 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1670,14 +1670,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) // //=========================================================================== -static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) +static void DoGiveInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) { - ACTION_PARAM_START(3); + ACTION_PARAM_START(2+use_aaptr); ACTION_PARAM_CLASS(mi, 0); ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(setreceiver, 2); - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + if (use_aaptr) + { + ACTION_PARAM_INT(setreceiver, 2); + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + } bool res=true; @@ -1685,6 +1688,11 @@ static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) if (mi) { AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); + if (!item) + { + ACTION_SET_RESULT(false); + return; + } if (item->IsKindOf(RUNTIME_CLASS(AHealth))) { item->Amount *= amount; @@ -1709,12 +1717,12 @@ static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) { - DoGiveInventory(self, PUSH_PARAMINFO); + DoGiveInventory(self, true, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) { - DoGiveInventory(self->target, PUSH_PARAMINFO); + DoGiveInventory(self->target, true, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) @@ -1724,7 +1732,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) while ((mo = it.Next())) { - if (mo->master == self) DoGiveInventory(mo, PUSH_PARAMINFO); + if (mo->master == self) DoGiveInventory(mo, false, PUSH_PARAMINFO); } } @@ -1737,7 +1745,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) { while ((mo = it.Next())) { - if (mo->master == self->master && mo != self) DoGiveInventory(mo, PUSH_PARAMINFO); + if (mo->master == self->master && mo != self) DoGiveInventory(mo, false, PUSH_PARAMINFO); } } } @@ -1753,16 +1761,23 @@ enum TIF_NOTAKEINFINITE = 1, }; -void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) +void DoTakeInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(3+use_aaptr); ACTION_PARAM_CLASS(item, 0); ACTION_PARAM_INT(amount, 1); ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(setreceiver, 3); - if (!item) return; - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + if (!item) + { + ACTION_SET_RESULT(false); + return; + } + if (use_aaptr) + { + ACTION_PARAM_INT(setreceiver, 3); + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + } bool res = false; @@ -1794,12 +1809,12 @@ void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) { - DoTakeInventory(self, PUSH_PARAMINFO); + DoTakeInventory(self, true, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) { - DoTakeInventory(self->target, PUSH_PARAMINFO); + DoTakeInventory(self->target, true, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) @@ -1809,7 +1824,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) while ((mo = it.Next())) { - if (mo->master == self) DoTakeInventory(mo, PUSH_PARAMINFO); + if (mo->master == self) DoTakeInventory(mo, false, PUSH_PARAMINFO); } } @@ -1822,7 +1837,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) { while ((mo = it.Next())) { - if (mo->master == self->master && mo != self) DoTakeInventory(mo, PUSH_PARAMINFO); + if (mo->master == self->master && mo != self) DoTakeInventory(mo, false, PUSH_PARAMINFO); } } }