From 32af6cb0cff91b4eade5eb9f7cdaaff60c92484c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:18:15 +0300 Subject: [PATCH 001/313] Added support for Command/Meta key --- src/c_bind.cpp | 4 ++++ src/c_console.cpp | 4 ++++ src/ct_chat.cpp | 8 ++++++++ src/d_gui.h | 3 ++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 0744efeee..66ed14ca5 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -230,7 +230,11 @@ const char *KeyNames[NUM_KEYS] = NULL, NULL, NULL, NULL, NULL, "pause", NULL, "home", //C0 "uparrow", "pgup", NULL, "leftarrow",NULL, "rightarrow",NULL, "end", //C8 "downarrow","pgdn", "ins", "del", NULL, NULL, NULL, NULL, //D0 +#ifdef __APPLE__ + NULL, NULL, NULL, "command", NULL, "apps", "power", "sleep", //D8 +#else // !__APPLE__ NULL, NULL, NULL, "lwin", "rwin", "apps", "power", "sleep", //D8 +#endif // __APPLE__ NULL, NULL, NULL, "wake", NULL, "search", "favorites","refresh", //E0 "webstop", "webforward","webback", "mycomputer","mail", "mediaselect",NULL, NULL, //E8 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F0 diff --git a/src/c_console.cpp b/src/c_console.cpp index e7c7dc30b..873cf5d7b 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1421,7 +1421,11 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case 'V': TabbedLast = false; TabbedList = false; +#ifdef __APPLE__ + if (ev->data3 & GKM_META) +#else // !__APPLE__ if (ev->data3 & GKM_CTRL) +#endif // __APPLE__ { if (data1 == 'C') { // copy to clipboard diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index c0c1f13f0..29edf3bcc 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -145,12 +145,20 @@ bool CT_Responder (event_t *ev) CT_BackSpace (); return true; } +#ifdef __APPLE__ + else if (ev->data1 == 'C' && (ev->data3 & GKM_META)) +#else // !__APPLE__ else if (ev->data1 == 'C' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ { I_PutInClipboard ((char *)ChatQueue); return true; } +#ifdef __APPLE__ + else if (ev->data1 == 'V' && (ev->data3 & GKM_META)) +#else // !__APPLE__ else if (ev->data1 == 'V' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ { CT_PasteChat(I_GetFromClipboard(false)); } diff --git a/src/d_gui.h b/src/d_gui.h index b88041771..86c3761ed 100644 --- a/src/d_gui.h +++ b/src/d_gui.h @@ -70,7 +70,8 @@ enum GUIKeyModifiers GKM_SHIFT = 1, GKM_CTRL = 2, GKM_ALT = 4, - GKM_LBUTTON = 8 + GKM_META = 8, + GKM_LBUTTON = 16 }; // Special codes for some GUI keys, including a few real ASCII codes. From 39e2ebe42502f674c1ea8d386355c6c47a606a11 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:19:07 +0300 Subject: [PATCH 002/313] Fixed GNU inline assembly for Clang --- src/gccinlines.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gccinlines.h b/src/gccinlines.h index b905449de..ecdf45df6 100644 --- a/src/gccinlines.h +++ b/src/gccinlines.h @@ -41,7 +41,7 @@ static inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) : "a,a,a,a,a,a" (a), "m,r,m,r,d,d" (b), "r,r,m,m,r,m" (c) - : "%cc" + : "cc" ); return result; @@ -59,7 +59,7 @@ static inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) : "a,a,a,a" (a), "m,r,m,r" (b), "c,c,I,I" (c) - : "%cc" + : "cc" ); return result; } @@ -210,7 +210,7 @@ static inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) : "a" (lo), "d" (hi), "r" (b) - : "%cc"); + : "cc"); return result; } @@ -226,7 +226,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b) "=&d,d" (dummy) : "a,a" (a), "r,m" (b) - : "%cc"); + : "cc"); return result; } @@ -241,7 +241,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b) : "a,a" (a<>(32-s)), \ "r,m" (b) \ - : "%cc"); \ + : "cc"); \ return result; \ } @@ -287,7 +287,7 @@ static inline SDWORD DivScale32 (SDWORD a, SDWORD b) "=d,d" (dummy) : "d,d" (a), "r,m" (b) - : "%cc"); + : "cc"); return result; } @@ -313,7 +313,7 @@ static inline void clearbufshort (void *buff, unsigned int count, WORD clear) "rep stosw" :"=D" (buff), "=c" (count) :"D" (buff), "c" (count), "a" (clear|(clear<<16)) - :"%cc"); + :"cc"); } static inline SDWORD ksgn (SDWORD a) @@ -327,6 +327,6 @@ static inline SDWORD ksgn (SDWORD a) "adc $0,%1" :"=r" (dummy), "=r" (result) :"0" (a) - :"%cc"); + :"cc"); return result; } From 0a5dd940728ed9f94a74ddd92b394aabc23f20c9 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:20:30 +0300 Subject: [PATCH 003/313] Fixed whole program (link-time) optimization build with Clang --- src/g_level.h | 2 +- src/thingdef/thingdef.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/g_level.h b/src/g_level.h index 0cd366798..ec867c226 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -52,7 +52,7 @@ class FScanner; #define GCC_YSEG #else #define MSVC_YSEG -#define GCC_YSEG __attribute__((section(SECTION_YREG))) +#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) #endif struct FIntermissionDescriptor; diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index c388a8544..5225b787a 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -266,11 +266,11 @@ enum EDefinitionType #define GCC_MSEG #else #define MSVC_ASEG -#define GCC_ASEG __attribute__((section(SECTION_AREG))) +#define GCC_ASEG __attribute__((section(SECTION_AREG))) __attribute__((used)) #define MSVC_PSEG -#define GCC_PSEG __attribute__((section(SECTION_GREG))) +#define GCC_PSEG __attribute__((section(SECTION_GREG))) __attribute__((used)) #define MSVC_MSEG -#define GCC_MSEG __attribute__((section(SECTION_MREG))) +#define GCC_MSEG __attribute__((section(SECTION_MREG))) __attribute__((used)) #endif From 2efb62e8ef75e53229ad277fdbae57dba139e09a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:21:00 +0300 Subject: [PATCH 004/313] Added work-around for vectorization issue in Apple's GCC 4.x --- src/nodebuild_utility.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nodebuild_utility.cpp b/src/nodebuild_utility.cpp index e7d5865d5..14ab7be58 100644 --- a/src/nodebuild_utility.cpp +++ b/src/nodebuild_utility.cpp @@ -69,7 +69,13 @@ static const int PO_LINE_EXPLICIT = 5; angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y) { const double rad2bam = double(1<<30) / M_PI; +#if defined __APPLE__ && !defined __llvm__ + // Work-around for vectorization issue in Apple's GCC 4.x + // See https://gcc.gnu.org/wiki/Math_Optimization_Flags for details + long double ang = atan2l (double(y), double(x)); +#else // !__APPLE__ || __llvm__ double ang = atan2 (double(y), double(x)); +#endif // __APPLE__ && !__llvm__ return angle_t(ang * rad2bam) << 1; } From 906102c3b6112e83514dc0a58a8b1bf1026da796 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:23:08 +0300 Subject: [PATCH 005/313] Added HID Utilities source code https://developer.apple.com/library/mac/samplecode/HID_Utilities/Introduction/Intro.html https://developer.apple.com/library/mac/samplecode/HID_Utilities/HID_Utilities.zip --- src/cocoa/HID_Config_Utilities.c | 919 +++++++++++++++++++++ src/cocoa/HID_Error_Handler.c | 101 +++ src/cocoa/HID_Name_Lookup.c | 1203 ++++++++++++++++++++++++++++ src/cocoa/HID_Queue_Utilities.c | 354 ++++++++ src/cocoa/HID_Utilities.c | 1061 ++++++++++++++++++++++++ src/cocoa/HID_Utilities_External.h | 417 ++++++++++ src/cocoa/IOHIDDevice_.c | 544 +++++++++++++ src/cocoa/IOHIDDevice_.h | 422 ++++++++++ src/cocoa/IOHIDElement_.c | 502 ++++++++++++ src/cocoa/IOHIDElement_.h | 339 ++++++++ src/cocoa/IOHIDLib_.h | 111 +++ src/cocoa/ImmrHIDUtilAddOn.c | 101 +++ src/cocoa/ImmrHIDUtilAddOn.h | 50 ++ 13 files changed, 6124 insertions(+) create mode 100755 src/cocoa/HID_Config_Utilities.c create mode 100755 src/cocoa/HID_Error_Handler.c create mode 100755 src/cocoa/HID_Name_Lookup.c create mode 100755 src/cocoa/HID_Queue_Utilities.c create mode 100755 src/cocoa/HID_Utilities.c create mode 100755 src/cocoa/HID_Utilities_External.h create mode 100755 src/cocoa/IOHIDDevice_.c create mode 100755 src/cocoa/IOHIDDevice_.h create mode 100755 src/cocoa/IOHIDElement_.c create mode 100755 src/cocoa/IOHIDElement_.h create mode 100755 src/cocoa/IOHIDLib_.h create mode 100755 src/cocoa/ImmrHIDUtilAddOn.c create mode 100755 src/cocoa/ImmrHIDUtilAddOn.h diff --git a/src/cocoa/HID_Config_Utilities.c b/src/cocoa/HID_Config_Utilities.c new file mode 100755 index 000000000..6cd1ad56a --- /dev/null +++ b/src/cocoa/HID_Config_Utilities.c @@ -0,0 +1,919 @@ +// 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. +// +//***************************************************** +#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 diff --git a/src/cocoa/HID_Error_Handler.c b/src/cocoa/HID_Error_Handler.c new file mode 100755 index 000000000..8aaca7f36 --- /dev/null +++ b/src/cocoa/HID_Error_Handler.c @@ -0,0 +1,101 @@ +// 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. +// +//***************************************************** +#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 diff --git a/src/cocoa/HID_Name_Lookup.c b/src/cocoa/HID_Name_Lookup.c new file mode 100755 index 000000000..4e4b023bd --- /dev/null +++ b/src/cocoa/HID_Name_Lookup.c @@ -0,0 +1,1203 @@ +// 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. +// +//***************************************************** +#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 diff --git a/src/cocoa/HID_Queue_Utilities.c b/src/cocoa/HID_Queue_Utilities.c new file mode 100755 index 000000000..586609d7f --- /dev/null +++ b/src/cocoa/HID_Queue_Utilities.c @@ -0,0 +1,354 @@ +// 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 "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 */ diff --git a/src/cocoa/HID_Utilities.c b/src/cocoa/HID_Utilities.c new file mode 100755 index 000000000..2beb6ca02 --- /dev/null +++ b/src/cocoa/HID_Utilities.c @@ -0,0 +1,1061 @@ +// 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. +// +//*************************************************** +#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 diff --git a/src/cocoa/HID_Utilities_External.h b/src/cocoa/HID_Utilities_External.h new file mode 100755 index 000000000..bd498bc51 --- /dev/null +++ b/src/cocoa/HID_Utilities_External.h @@ -0,0 +1,417 @@ +// 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/cocoa/IOHIDDevice_.c b/src/cocoa/IOHIDDevice_.c new file mode 100755 index 000000000..c1e2d12cf --- /dev/null +++ b/src/cocoa/IOHIDDevice_.c @@ -0,0 +1,544 @@ +// File: IOHIDDevice_.c +// Abstract: convieance functions for IOHIDDeviceGetProperty +// 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. +// +//***************************************************** +#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_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long *outValue); +static void IOHIDDevice_SetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long 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: long - the vendor ID for this device +// + +long IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the VendorIDSource for this device +// + +long IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the product ID for this device +// + +long IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: CFStringRef - the VersionNumber for this device +// + +long IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: CFStringRef - the CountryCode for this device +// + +long IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the location ID for this device +// + +long IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), (long *) &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) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: CFStringRef - the PrimaryUsage for this device +// + +uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: CFStringRef - the PrimaryUsagePage for this device +// + +uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the MaxInputReportSize for this device +// + +long IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the MaxOutput for this device +// + +long IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the MaxFeatureReportSize for this device +// + +long IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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: long - the ReportInterval for this device +// +#ifndef kIOHIDReportIntervalKey +#define kIOHIDReportIntervalKey "ReportInterval" +#endif +long IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { + long result = 0; + (void) IOHIDDevice_GetLongProperty(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_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (long *) &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_SetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (long) 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_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (long *) &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_SetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (long) inTransactionRef); +} + +//***************************************************** +#pragma mark - local (static) function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// IOHIDDevice_GetLongProperty( inIOHIDDeviceRef, inKey, outValue ) +// +// Purpose: convieance function to return a long 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_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long *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_GetLongProperty + +//************************************************************************* +// +// IOHIDDevice_SetLongProperty( 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_SetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long inValue) { + CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); + if ( tCFNumberRef ) { + IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef); + CFRelease(tCFNumberRef); + } +} // IOHIDDevice_SetLongProperty + +//***************************************************** diff --git a/src/cocoa/IOHIDDevice_.h b/src/cocoa/IOHIDDevice_.h new file mode 100755 index 000000000..739352844 --- /dev/null +++ b/src/cocoa/IOHIDDevice_.h @@ -0,0 +1,422 @@ +// File: IOHIDDevice_.h +// Abstract: convieance functions for IOHIDDeviceGetProperty +// 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 __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: long - the vendor ID for this device + // + + extern long IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef ) + // + // Purpose: get the VendorIDSource for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the VendorIDSource for this device + // + + extern long IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetProductID( inIOHIDDeviceRef ) + // + // Purpose: get the product ID for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the product ID for this device + // + + extern long IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef ) + // + // Purpose: get the VersionNumber CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the VersionNumber for this device + // + + extern long 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: long - the CountryCode for this device + // + + extern long IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetLocationID( inIOHIDDeviceRef ) + // + // Purpose: get the location ID for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the location ID for this device + // + + extern long 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: long - the MaxInputReportSize for this device + // + + extern long IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef ) + // + // Purpose: get the MaxOutputReportSize for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the MaxOutputReportSize for this device + // + + extern long IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef ) + // + // Purpose: get the MaxFeatureReportSize for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the MaxFeatureReportSize for this device + // + + extern long IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetReportInterval( inIOHIDDeviceRef ) + // + // Purpose: get the ReportInterval for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: long - the ReportInterval for this device + // + + extern long 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/cocoa/IOHIDElement_.c b/src/cocoa/IOHIDElement_.c new file mode 100755 index 000000000..f4d8be9eb --- /dev/null +++ b/src/cocoa/IOHIDElement_.c @@ -0,0 +1,502 @@ +// 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. +// +//***************************************************** +#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 + +//***************************************************** diff --git a/src/cocoa/IOHIDElement_.h b/src/cocoa/IOHIDElement_.h new file mode 100755 index 000000000..a8a631668 --- /dev/null +++ b/src/cocoa/IOHIDElement_.h @@ -0,0 +1,339 @@ +// 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/cocoa/IOHIDLib_.h b/src/cocoa/IOHIDLib_.h new file mode 100755 index 000000000..38c6248ac --- /dev/null +++ b/src/cocoa/IOHIDLib_.h @@ -0,0 +1,111 @@ +// 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/cocoa/ImmrHIDUtilAddOn.c b/src/cocoa/ImmrHIDUtilAddOn.c new file mode 100755 index 000000000..07e6a31b1 --- /dev/null +++ b/src/cocoa/ImmrHIDUtilAddOn.c @@ -0,0 +1,101 @@ +// 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 +#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 diff --git a/src/cocoa/ImmrHIDUtilAddOn.h b/src/cocoa/ImmrHIDUtilAddOn.h new file mode 100755 index 000000000..72de752e3 --- /dev/null +++ b/src/cocoa/ImmrHIDUtilAddOn.h @@ -0,0 +1,50 @@ +// 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); From d6cc6ee452633ebfe7f587bf8d51edf38652ee54 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:26:17 +0300 Subject: [PATCH 006/313] Added notification dialog in case of fatal error --- src/sdl/i_main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 27324edf6..3e199a165 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -75,6 +75,10 @@ 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 --------------------------------------------- @@ -356,6 +360,11 @@ int main (int argc, char **argv) I_ShutdownJoysticks(); if (error.GetMessage ()) fprintf (stderr, "%s\n", error.GetMessage ()); + +#ifdef __APPLE__ + Mac_I_FatalError(error.GetMessage()); +#endif // __APPLE__ + exit (-1); } catch (...) From fecd1b640103640af70fa4135f5ffa6c515708fe Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:33:29 +0300 Subject: [PATCH 007/313] Moved cursor and timer implementations into separate files --- src/CMakeLists.txt | 2 + src/sdl/i_cursor.cpp | 73 +++++++++++++ src/sdl/i_system.cpp | 250 ++----------------------------------------- src/sdl/i_timer.cpp | 210 ++++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 244 deletions(-) create mode 100644 src/sdl/i_cursor.cpp create mode 100644 src/sdl/i_timer.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1444f1cf3..87c3f4dfa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -559,11 +559,13 @@ set( PLAT_SDL_SOURCES sdl/crashcatcher.c sdl/hardware.cpp sdl/i_cd.cpp + sdl/i_cursor.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 ) set( PLAT_MAC_SOURCES diff --git a/src/sdl/i_cursor.cpp b/src/sdl/i_cursor.cpp new file mode 100644 index 000000000..c4202a6b3 --- /dev/null +++ b/src/sdl/i_cursor.cpp @@ -0,0 +1,73 @@ + +// Moved from sdl/i_system.cpp + +#include + +#include + +#include "bitmap.h" +#include "v_palette.h" +#include "textures.h" + + +extern SDL_Surface *cursorSurface; +extern SDL_Rect cursorBlit; + + +bool I_SetCursor(FTexture *cursorpic) +{ + if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) + { + // Must be no larger than 32x32. + if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) + { + 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)); + + SDL_ShowCursor(0); + SDL_LockSurface(cursorSurface); + BYTE buffer[32*32*4]; + memset(buffer, 0, 32*32*4); + FBitmap bmp(buffer, 32*4, 32, 32); + cursorpic->CopyTrueColorPixels(&bmp, 0, 0); + memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); + SDL_UnlockSurface(cursorSurface); + } + else + { + SDL_ShowCursor(1); + + if (cursorSurface != NULL) + { + 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_system.cpp b/src/sdl/i_system.cpp index 9b3027a7d..ca5ffa2e2 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -122,190 +122,6 @@ void I_EndRead(void) } -static DWORD TicStart; -static DWORD TicNext; -static DWORD BaseTime; -static int TicFrozen; - -// Signal based timer. -static Semaphore timerWait; -static int tics; -static DWORD sig_start, sig_next; - -void I_SelectTimer(); - -// [RH] Returns time in milliseconds -unsigned int I_MSTime (void) -{ - unsigned int time = SDL_GetTicks (); - return time - BaseTime; -} - -// Exactly the same thing, but based does no modification to the time. -unsigned int I_FPSTime() -{ - return SDL_GetTicks(); -} - -// -// I_GetTime -// returns time in 1/35th second tics -// -int I_GetTimeSelect (bool saveMS) -{ - I_SelectTimer(); - return I_GetTime (saveMS); -} - -int I_GetTimePolled (bool saveMS) -{ - if (TicFrozen != 0) - { - return TicFrozen; - } - - DWORD tm = SDL_GetTicks(); - - if (saveMS) - { - TicStart = tm; - TicNext = Scale((Scale (tm, TICRATE, 1000) + 1), 1000, TICRATE); - } - return Scale(tm - BaseTime, TICRATE, 1000); -} - -int I_GetTimeSignaled (bool saveMS) -{ - if (saveMS) - { - TicStart = sig_start; - TicNext = sig_next; - } - return tics; -} - -int I_WaitForTicPolled (int prevtic) -{ - int time; - - assert (TicFrozen == 0); - while ((time = I_GetTimePolled(false)) <= prevtic) - ; - - return time; -} - -int I_WaitForTicSignaled (int prevtic) -{ - assert (TicFrozen == 0); - - while(tics <= prevtic) - { - SEMAPHORE_WAIT(timerWait) - } - - return tics; -} - -void I_FreezeTimeSelect (bool frozen) -{ - I_SelectTimer(); - return I_FreezeTime (frozen); -} - -void I_FreezeTimePolled (bool frozen) -{ - if (frozen) - { - assert(TicFrozen == 0); - TicFrozen = I_GetTimePolled(false); - } - else - { - assert(TicFrozen != 0); - int froze = TicFrozen; - TicFrozen = 0; - int now = I_GetTimePolled(false); - BaseTime += (now - froze) * 1000 / TICRATE; - } -} - -void I_FreezeTimeSignaled (bool frozen) -{ - TicFrozen = frozen; -} - -int I_WaitForTicSelect (int prevtic) -{ - I_SelectTimer(); - return I_WaitForTic (prevtic); -} - -// -// I_HandleAlarm -// Should be called every time there is an alarm. -// -void I_HandleAlarm (int sig) -{ - if(!TicFrozen) - tics++; - sig_start = SDL_GetTicks(); - sig_next = Scale((Scale (sig_start, TICRATE, 1000) + 1), 1000, TICRATE); - SEMAPHORE_SIGNAL(timerWait) -} - -// -// I_SelectTimer -// Sets up the timer function based on if we can use signals for efficent CPU -// usage. -// -void I_SelectTimer() -{ - SEMAPHORE_INIT(timerWait, 0, 0) -#ifndef __sun - signal(SIGALRM, I_HandleAlarm); -#else - struct sigaction alrmaction; - sigaction(SIGALRM, NULL, &alrmaction); - alrmaction.sa_handler = I_HandleAlarm; - sigaction(SIGALRM, &alrmaction, NULL); -#endif - - struct itimerval itv; - itv.it_interval.tv_sec = itv.it_value.tv_sec = 0; - itv.it_interval.tv_usec = itv.it_value.tv_usec = 1000000/TICRATE; - - if (setitimer(ITIMER_REAL, &itv, NULL) != 0) - { - I_GetTime = I_GetTimePolled; - I_FreezeTime = I_FreezeTimePolled; - I_WaitForTic = I_WaitForTicPolled; - } - else - { - I_GetTime = I_GetTimeSignaled; - I_FreezeTime = I_FreezeTimeSignaled; - I_WaitForTic = I_WaitForTicSignaled; - } -} - -// Returns the fractional amount of a tic passed since the most recent tic -fixed_t I_GetTimeFrac (uint32 *ms) -{ - DWORD now = SDL_GetTicks (); - if (ms) *ms = TicNext; - DWORD step = TicNext - TicStart; - if (step == 0) - { - return FRACUNIT; - } - else - { - fixed_t frac = clamp ((now - TicStart)*FRACUNIT/step, 0, FRACUNIT); - return frac; - } -} - void I_WaitVBL (int count) { // I_WaitVBL is never used to actually synchronize to the @@ -327,6 +143,9 @@ void SetLanguageIDs () LanguageIDs[3] = LanguageIDs[2] = LanguageIDs[1] = LanguageIDs[0] = lang; } +void I_InitTimer (); +void I_ShutdownTimer (); + // // I_Init // @@ -335,11 +154,9 @@ void I_Init (void) CheckCPUID (&CPU); DumpCPUInfo (&CPU); - I_GetTime = I_GetTimeSelect; - I_WaitForTic = I_WaitForTicSelect; - I_FreezeTime = I_FreezeTimeSelect; atterm (I_ShutdownSound); I_InitSound (); + I_InitTimer (); } // @@ -355,6 +172,8 @@ void I_Quit (void) G_CheckDemoStatus(); C_DeinitConsole(); + + I_ShutdownTimer(); } @@ -900,60 +719,3 @@ SDL_Cursor *CreateColorCursor(FTexture *cursorpic) SDL_Surface *cursorSurface = NULL; SDL_Rect cursorBlit = {0, 0, 32, 32}; -bool I_SetCursor(FTexture *cursorpic) -{ - if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) - { - // Must be no larger than 32x32. - if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) - { - 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)); - - SDL_ShowCursor(0); - SDL_LockSurface(cursorSurface); - BYTE buffer[32*32*4]; - memset(buffer, 0, 32*32*4); - FBitmap bmp(buffer, 32*4, 32, 32); - cursorpic->CopyTrueColorPixels(&bmp, 0, 0); - memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); - SDL_UnlockSurface(cursorSurface); - } - else - { - SDL_ShowCursor(1); - - if (cursorSurface != NULL) - { - 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_timer.cpp b/src/sdl/i_timer.cpp new file mode 100644 index 000000000..b36bfc840 --- /dev/null +++ b/src/sdl/i_timer.cpp @@ -0,0 +1,210 @@ + +// Moved from sdl/i_system.cpp + +#include +#include + +#include + +#include "basictypes.h" +#include "basicinlines.h" +#include "hardware.h" +#include "i_system.h" +#include "templates.h" + + +static DWORD TicStart; +static DWORD TicNext; +static DWORD BaseTime; +static int TicFrozen; + +// Signal based timer. +static Semaphore timerWait; +static int tics; +static DWORD sig_start, sig_next; + +void I_SelectTimer(); + +// [RH] Returns time in milliseconds +unsigned int I_MSTime (void) +{ + unsigned int time = SDL_GetTicks (); + return time - BaseTime; +} + +// Exactly the same thing, but based does no modification to the time. +unsigned int I_FPSTime() +{ + return SDL_GetTicks(); +} + +// +// I_GetTime +// returns time in 1/35th second tics +// +int I_GetTimeSelect (bool saveMS) +{ + I_SelectTimer(); + return I_GetTime (saveMS); +} + +int I_GetTimePolled (bool saveMS) +{ + if (TicFrozen != 0) + { + return TicFrozen; + } + + DWORD tm = SDL_GetTicks(); + + if (saveMS) + { + TicStart = tm; + TicNext = Scale((Scale (tm, TICRATE, 1000) + 1), 1000, TICRATE); + } + return Scale(tm - BaseTime, TICRATE, 1000); +} + +int I_GetTimeSignaled (bool saveMS) +{ + if (saveMS) + { + TicStart = sig_start; + TicNext = sig_next; + } + return tics; +} + +int I_WaitForTicPolled (int prevtic) +{ + int time; + + assert (TicFrozen == 0); + while ((time = I_GetTimePolled(false)) <= prevtic) + ; + + return time; +} + +int I_WaitForTicSignaled (int prevtic) +{ + assert (TicFrozen == 0); + + while(tics <= prevtic) + { + SEMAPHORE_WAIT(timerWait) + } + + return tics; +} + +void I_FreezeTimeSelect (bool frozen) +{ + I_SelectTimer(); + return I_FreezeTime (frozen); +} + +void I_FreezeTimePolled (bool frozen) +{ + if (frozen) + { + assert(TicFrozen == 0); + TicFrozen = I_GetTimePolled(false); + } + else + { + assert(TicFrozen != 0); + int froze = TicFrozen; + TicFrozen = 0; + int now = I_GetTimePolled(false); + BaseTime += (now - froze) * 1000 / TICRATE; + } +} + +void I_FreezeTimeSignaled (bool frozen) +{ + TicFrozen = frozen; +} + +int I_WaitForTicSelect (int prevtic) +{ + I_SelectTimer(); + return I_WaitForTic (prevtic); +} + +// +// I_HandleAlarm +// Should be called every time there is an alarm. +// +void I_HandleAlarm (int sig) +{ + if(!TicFrozen) + tics++; + sig_start = SDL_GetTicks(); + sig_next = Scale((Scale (sig_start, TICRATE, 1000) + 1), 1000, TICRATE); + SEMAPHORE_SIGNAL(timerWait) +} + +// +// I_SelectTimer +// Sets up the timer function based on if we can use signals for efficent CPU +// usage. +// +void I_SelectTimer() +{ + SEMAPHORE_INIT(timerWait, 0, 0) +#ifndef __sun + signal(SIGALRM, I_HandleAlarm); +#else + struct sigaction alrmaction; + sigaction(SIGALRM, NULL, &alrmaction); + alrmaction.sa_handler = I_HandleAlarm; + sigaction(SIGALRM, &alrmaction, NULL); +#endif + + struct itimerval itv; + itv.it_interval.tv_sec = itv.it_value.tv_sec = 0; + itv.it_interval.tv_usec = itv.it_value.tv_usec = 1000000/TICRATE; + + if (setitimer(ITIMER_REAL, &itv, NULL) != 0) + { + I_GetTime = I_GetTimePolled; + I_FreezeTime = I_FreezeTimePolled; + I_WaitForTic = I_WaitForTicPolled; + } + else + { + I_GetTime = I_GetTimeSignaled; + I_FreezeTime = I_FreezeTimeSignaled; + I_WaitForTic = I_WaitForTicSignaled; + } +} + +// Returns the fractional amount of a tic passed since the most recent tic +fixed_t I_GetTimeFrac (uint32 *ms) +{ + DWORD now = SDL_GetTicks (); + if (ms) *ms = TicNext; + DWORD step = TicNext - TicStart; + if (step == 0) + { + return FRACUNIT; + } + else + { + fixed_t frac = clamp ((now - TicStart)*FRACUNIT/step, 0, FRACUNIT); + return frac; + } +} + +void I_InitTimer () +{ + I_GetTime = I_GetTimeSelect; + I_WaitForTic = I_WaitForTicSelect; + I_FreezeTime = I_FreezeTimeSelect; +} + +void I_ShutdownTimer () +{ + +} From e12f860f1bdc63d5f4835b568cb4023f7d72e15c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:36:02 +0300 Subject: [PATCH 008/313] Added native Cocoa back-end implementation --- src/cocoa/i_backend_cocoa.mm | 1744 ++++++++++++++++++++++++++++++++++ src/cocoa/i_joystick.cpp | 776 +++++++++++++++ src/cocoa/i_timer.cpp | 191 ++++ src/cocoa/zdoom-info.plist | 47 + src/cocoa/zdoom.icns | Bin 0 -> 196176 bytes src/cocoa/zdoom.xib | 874 +++++++++++++++++ 6 files changed, 3632 insertions(+) create mode 100644 src/cocoa/i_backend_cocoa.mm create mode 100644 src/cocoa/i_joystick.cpp create mode 100644 src/cocoa/i_timer.cpp create mode 100644 src/cocoa/zdoom-info.plist create mode 100644 src/cocoa/zdoom.icns create mode 100644 src/cocoa/zdoom.xib diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm new file mode 100644 index 000000000..d4c353edd --- /dev/null +++ b/src/cocoa/i_backend_cocoa.mm @@ -0,0 +1,1744 @@ +/* + ** 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 "basictypes.h" +#include "basicinlines.h" +#include "bitmap.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_event.h" +#include "d_gui.h" +#include "dikeys.h" +#include "doomdef.h" +#include "doomstat.h" +#include "s_sound.h" +#include "textures.h" +#include "v_video.h" +#include "version.h" + +#undef Class + + +#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) + + +// --------------------------------------------------------------------------- + + +#ifndef NSAppKitVersionNumber10_7 + +@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 // NSAppKitVersionNumber10_7 + + +// --------------------------------------------------------------------------- + + +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_ShutdownJoysticks(); + + +namespace +{ + +struct FrameBufferParameters +{ + float pixelScale; + + float shiftX; + float shiftY; + + float width; + float height; +}; + +FrameBufferParameters s_frameBufferParameters; + + +const int ARGC_MAX = 64; + +int s_argc; +char* s_argv[ARGC_MAX]; + +TArray s_argvStorage; + + +bool s_nativeMouse = true; + +// TODO: remove this magic! +size_t s_skipMouseMoves; + +NSCursor* s_cursor; + + +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 + + +// from iokit_joystick.cpp +void I_ProcessJoysticks(); + + +void I_GetEvent() +{ + [[NSRunLoop mainRunLoop] 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() +{ +#ifdef NSAppKitVersionNumber10_7 + return NSAppKitVersionNumber >= NSAppKitVersionNumber10_7; +#else // !NSAppKitVersionNumber10_7 + return false; +#endif // NSAppKitVersionNumber10_7 +} + +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 (IsHiDPISupported() && NSNormalWindowLevel == [window level]) + ? [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 convertPointFromBase:windowPos]; + + const CGFloat frameHeight = GetRealContentViewSize(window).height; + + const CGFloat posX = ( viewPos.x - s_frameBufferParameters.shiftX) / s_frameBufferParameters.pixelScale; + const CGFloat posY = (frameHeight - viewPos.y - s_frameBufferParameters.shiftY) / s_frameBufferParameters.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; + } + + 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; + } + + 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); +} + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +@interface FullscreenWindow : NSWindow +{ + +} + +- (bool)canBecomeKeyWindow; + +- (void)close; + +@end + + +@implementation FullscreenWindow + +- (bool)canBecomeKeyWindow +{ + return true; +} + + +- (void)close +{ + [super close]; + + I_ShutdownJoysticks(); + + [NSApp terminate:self]; +} + +@end + + +// --------------------------------------------------------------------------- + + +@interface FullscreenView : NSOpenGLView +{ + +} + +- (void)resetCursorRects; + +@end + + +@implementation FullscreenView + +- (void)resetCursorRects +{ + [super resetCursorRects]; + [self addCursorRect: [self bounds] + cursor: s_cursor]; +} + +@end + + +// --------------------------------------------------------------------------- + + +@interface ApplicationDelegate : NSResponder +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + +#endif +{ +@private + FullscreenWindow* m_window; + + bool m_openGLInitialized; + int m_multisample; +} + +- (id)init; +- (void)dealloc; + +- (void)keyDown:(NSEvent*)theEvent; +- (void)keyUp:(NSEvent*)theEvent; + +- (void)applicationDidBecomeActive:(NSNotification*)aNotification; +- (void)applicationWillResignActive:(NSNotification*)aNotification; + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification; + +- (void)applicationWillTerminate:(NSNotification*)aNotification; + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; + +- (int)multisample; +- (void)setMultisample:(int)multisample; + +- (void)initializeOpenGL; +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height; + +- (void)processEvents:(NSTimer*)timer; + +- (void)invalidateCursorRects; + +- (void)setMainWindowVisible:(bool)visible; + +@end + + +static ApplicationDelegate* s_applicationDelegate; + + +@implementation ApplicationDelegate + +- (id)init +{ + self = [super init]; + + m_openGLInitialized = false; + m_multisample = 0; + + return self; +} + +- (void)dealloc +{ + [m_window release]; + + [super dealloc]; +} + + +- (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 mainRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; + + exit(SDL_main(s_argc, s_argv)); +} + + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename +{ + ZD_UNUSED(theApplication); + + if (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)applicationWillTerminate:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + // Hide window as nothing will be rendered at this point + [m_window orderOut:nil]; +} + + +- (int)multisample +{ + return m_multisample; +} + +- (void)setMultisample:(int)multisample +{ + m_multisample = multisample; +} + + +- (void)initializeOpenGL +{ + if (m_openGLInitialized) + { + return; + } + + // Create window + + m_window = [[FullscreenWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) + styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + [m_window setOpaque:YES]; + [m_window makeFirstResponder:self]; + [m_window setAcceptsMouseMovedEvents:YES]; + + // Create OpenGL context and view + + NSOpenGLPixelFormatAttribute attributes[16]; + size_t i = 0; + + attributes[i++] = NSOpenGLPFADoubleBuffer; + attributes[i++] = NSOpenGLPFAColorSize; + attributes[i++] = 32; + attributes[i++] = NSOpenGLPFADepthSize; + attributes[i++] = 24; + attributes[i++] = NSOpenGLPFAStencilSize; + attributes[i++] = 8; + + if (m_multisample) + { + attributes[i++] = NSOpenGLPFAMultisample; + attributes[i++] = NSOpenGLPFASampleBuffers; + attributes[i++] = 1; + attributes[i++] = NSOpenGLPFASamples; + attributes[i++] = m_multisample; + } + + attributes[i] = 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]; + + if (IsHiDPISupported()) + { + [glView setWantsBestResolutionOpenGLSurface:YES]; + } + + [m_window setContentView:glView]; + + m_openGLInitialized = true; +} + +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height +{ + [self initializeOpenGL]; + + CGLContextObj context = CGLGetCurrentContext(); + NSView* view = [m_window contentView]; + + if (fullscreen) + { + NSScreen* screen = [m_window screen]; + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = IsHiDPISupported() + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast< float >(width ); + const float pixelScaleFactorY = displayHeight / static_cast< float >(height); + + s_frameBufferParameters.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); + + s_frameBufferParameters.width = width * s_frameBufferParameters.pixelScale; + s_frameBufferParameters.height = height * s_frameBufferParameters.pixelScale; + + s_frameBufferParameters.shiftX = (displayWidth - s_frameBufferParameters.width ) / 2.0f; + s_frameBufferParameters.shiftY = (displayHeight - s_frameBufferParameters.height) / 2.0f; + + [m_window setLevel:NSMainMenuWindowLevel + 1]; + [m_window setStyleMask:NSBorderlessWindowMask]; + [m_window setHidesOnDeactivate:YES]; + [m_window setFrame:displayRect display:YES]; + [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; + } + else + { + s_frameBufferParameters.pixelScale = 1.0f; + + s_frameBufferParameters.width = static_cast< float >(width ); + s_frameBufferParameters.height = static_cast< float >(height); + + s_frameBufferParameters.shiftX = 0.0f; + s_frameBufferParameters.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = IsHiDPISupported() + ? [view convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + [m_window setLevel:NSNormalWindowLevel]; + [m_window setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask]; + [m_window setHidesOnDeactivate:NO]; + [m_window setContentSize:windowSize]; + [m_window center]; + } + + const NSSize viewSize = GetRealContentViewSize(m_window); + + glViewport(0, 0, viewSize.width, viewSize.height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + CGLFlushDrawable(context); + + static NSString* const TITLE_STRING = + [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + [m_window setTitle:TITLE_STRING]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } +} + + +- (void)processEvents:(NSTimer*)timer +{ + ZD_UNUSED(timer); + + 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; + } + + [NSApp sendEvent:event]; + } + + [NSApp updateWindows]; +} + + +- (void)invalidateCursorRects +{ + [m_window invalidateCursorRectsForView:[m_window contentView]]; +} + + +- (void)setMainWindowVisible:(bool)visible +{ + if (visible) + { + [m_window orderFront:nil]; + } + else + { + [m_window orderOut:nil]; + } +} + +@end + + +// --------------------------------------------------------------------------- + + +void I_SetMainWindowVisible(bool visible) +{ + [s_applicationDelegate setMainWindowVisible:visible]; + + SetNativeMouse(!visible); +} + + +// --------------------------------------------------------------------------- + + +bool I_SetCursor(FTexture* cursorpic) +{ + 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]; + 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)]; + } + + [s_applicationDelegate invalidateCursorRects]; + + return true; +} + + +// --------------------------------------------------------------------------- + + +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() +{ + 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); + + return 0; +} + +void SDL_Quit() +{ + if (NULL != s_applicationDelegate) + { + [NSApp setDelegate:nil]; + [NSApp deactivate]; + + [s_applicationDelegate release]; + s_applicationDelegate = NULL; + } +} + + +char* SDL_GetError() +{ + static char empty[] = {0}; + return empty; +} + + +char* SDL_VideoDriverName(char* namebuf, int maxlen) +{ + return strncpy(namebuf, "Native OpenGL", maxlen); +} + +const SDL_VideoInfo* SDL_GetVideoInfo() +{ + // 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)); + + const NSRect displayRect = [[NSScreen mainScreen] frame]; + + videoInfo.current_w = displayRect.size.width; + videoInfo.current_h = displayRect.size.height; + videoInfo.vfmt = &pixelFormat; + + return &videoInfo; +} + +SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) +{ + ZD_UNUSED(format); + ZD_UNUSED(flags); + + static std::vector< SDL_Rect* > 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, 600); + DEFINE_RESOLUTION(1024, 640); + DEFINE_RESOLUTION(1024, 768); + DEFINE_RESOLUTION(1152, 720); + DEFINE_RESOLUTION(1280, 720); + DEFINE_RESOLUTION(1280, 800); + DEFINE_RESOLUTION(1280, 960); + DEFINE_RESOLUTION(1280, 1024); + DEFINE_RESOLUTION(1366, 768); + DEFINE_RESOLUTION(1400, 1050); + DEFINE_RESOLUTION(1440, 900); + DEFINE_RESOLUTION(1600, 900); + DEFINE_RESOLUTION(1600, 1200); + DEFINE_RESOLUTION(1680, 1050); + DEFINE_RESOLUTION(1920, 1080); + DEFINE_RESOLUTION(1920, 1200); + DEFINE_RESOLUTION(2048, 1536); + DEFINE_RESOLUTION(2560, 1440); + DEFINE_RESOLUTION(2560, 1600); + DEFINE_RESOLUTION(2880, 1800); + +#undef DEFINE_RESOLUTION + + resolutions.push_back(NULL); + } + + return &resolutions[0]; +} + + +//static GLAuxilium::Texture2D* s_softwareTexture; +static GLuint s_frameBufferTexture = 0; + +static const Uint16 BYTES_PER_PIXEL = 4; + +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; + result.colorkey = 0; + result.alpha = 0xFF; + + return &result; +} + + +SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) +{ + [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & flags) width:width height:height]; + + static SDL_Surface result; + + const bool isSoftwareRenderer = !(SDL_OPENGL & flags); + + if (isSoftwareRenderer) + { + if (NULL != result.pixels) + { + free(result.pixels); + } + + if (0 != s_frameBufferTexture) + { + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &s_frameBufferTexture); + s_frameBufferTexture = 0; + } + } + + result.flags = flags; + result.format = GetPixelFormat(); + result.w = width; + result.h = height; + result.pitch = width * BYTES_PER_PIXEL; + result.pixels = isSoftwareRenderer ? malloc(width * height * BYTES_PER_PIXEL) : NULL; + 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) +{ + ZD_UNUSED(title); + ZD_UNUSED(icon); + + // Window title is set in SDL_SetVideoMode() +} + +static void ResetSoftwareViewport() +{ + // For an unknown reason the following call to glClear() is needed + // to avoid drawing of garbage in fullscreen mode + // when game video resolution's aspect ratio is different from display one + + GLint viewport[2]; + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport); + + glViewport(0, 0, viewport[0], viewport[1]); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(s_frameBufferParameters.shiftX, s_frameBufferParameters.shiftY, + s_frameBufferParameters.width, s_frameBufferParameters.height); +} + +int SDL_WM_ToggleFullScreen(SDL_Surface* surface) +{ + if (surface->flags & SDL_FULLSCREEN) + { + surface->flags &= ~SDL_FULLSCREEN; + } + else + { + surface->flags |= SDL_FULLSCREEN; + } + + [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & surface->flags) + width:surface->w + height:surface->h]; + ResetSoftwareViewport(); + + return 1; +} + + +void SDL_GL_SwapBuffers() +{ + [[NSOpenGLContext currentContext] flushBuffer]; +} + +int SDL_LockSurface(SDL_Surface* surface) +{ + ZD_UNUSED(surface); + + return 0; +} + +void SDL_UnlockSurface(SDL_Surface* surface) +{ + ZD_UNUSED(surface); +} + +int SDL_BlitSurface(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect) +{ + ZD_UNUSED(src); + ZD_UNUSED(srcrect); + ZD_UNUSED(dst); + ZD_UNUSED(dstrect); + + return 0; +} + + +static void SetupSoftwareRendering(SDL_Surface* screen) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, screen->w, screen->h, 0.0, -1.0, 1.0); + + ResetSoftwareViewport(); + + glEnable(GL_TEXTURE_2D); + + glGenTextures(1, &s_frameBufferTexture); + glBindTexture(GL_TEXTURE_2D, s_frameBufferTexture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +int SDL_Flip(SDL_Surface* screen) +{ + assert(NULL != screen); + + if (0 == s_frameBufferTexture) + { + SetupSoftwareRendering(screen); + } + + const int width = screen->w; + const int height = screen->h; + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + width, height, 0, GL_RGBA, 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(1.0f, 0.0f); + glVertex2f(width, 0.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(width, height); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(0.0f, height); + glEnd(); + + glFlush(); + + SDL_GL_SwapBuffers(); + + return 0; +} + +int SDL_SetPalette(SDL_Surface* surface, int flags, SDL_Color* colors, int firstcolor, int ncolors) +{ + ZD_UNUSED(surface); + ZD_UNUSED(flags); + ZD_UNUSED(colors); + ZD_UNUSED(firstcolor); + ZD_UNUSED(ncolors); + + return 0; +} + +} // extern "C" + +#ifdef main +#undef main +#endif // main + +static void CheckOSVersion() +{ + static const char* const PARAMETER_NAME = "kern.osrelease"; + + size_t size = 0; + + if (-1 == sysctlbyname(PARAMETER_NAME, NULL, &size, NULL, 0)) + { + return; + } + + char* version = static_cast(alloca(size)); + + if (-1 == sysctlbyname(PARAMETER_NAME, version, &size, NULL, 0)) + { + return; + } + + if (strcmp(version, "10.0") < 0) + { + CFOptionFlags responseFlags; + CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, + CFSTR("Unsupported version of OS X"), CFSTR("You need OS X 10.6 or higher running on Intel platform in order to play."), + NULL, NULL, NULL, &responseFlags); + + exit(EXIT_FAILURE); + } +} + +int main(int argc, char** argv) +{ + CheckOSVersion(); + + gettimeofday(&s_startTicks, NULL); + + for (int i = 0; i <= argc; ++i) + { + const char* const argument = argv[i]; + + if (NULL == argument || '\0' == argument[0]) + { + continue; + } + + s_argvStorage.Push(argument); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + } + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + [NSApplication sharedApplication]; + [NSBundle loadNibNamed:@"zdoom" owner:NSApp]; + + s_applicationDelegate = [ApplicationDelegate new]; + [NSApp setDelegate:s_applicationDelegate]; + + [NSApp run]; + + [pool release]; + + return EXIT_SUCCESS; +} diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp new file mode 100644 index 000000000..add1b1bd7 --- /dev/null +++ b/src/cocoa/i_joystick.cpp @@ -0,0 +1,776 @@ +/* + ** i_joystick.cpp + ** + **--------------------------------------------------------------------------- + ** 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 "m_joy.h" + +#include "HID_Utilities_External.h" + +#include "d_event.h" +#include "doomdef.h" +#include "templates.h" + + +namespace +{ + +FString ToFString( const CFStringRef string ) +{ + if ( NULL == string ) + { + return FString(); + } + + 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 ); +} + + +class IOKitJoystick : public IJoystickConfig +{ +public: + explicit IOKitJoystick( IOHIDDeviceRef device ); + virtual ~IOKitJoystick(); + + virtual FString GetName(); + virtual float GetSensitivity(); + 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 bool IsSensitivityDefault(); + 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 Update(); + +private: + IOHIDDeviceRef m_device; + + float m_sensitivity; + + struct AxisInfo + { + char name[ 64 ]; + + float value; + + 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; + + + 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 ); + +}; + + +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 ) +{ + IOHIDElementRef element = HIDGetFirstDeviceElement( device, kHIDElementTypeInput ); + + while ( NULL != element ) + { + const uint32_t usagePage = IOHIDElementGetUsagePage( element ); + + if ( kHIDPage_GenericDesktop == usagePage ) + { + const uint32_t usage = IOHIDElementGetUsage( element ); + + 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 ) + { + AxisInfo axis; + memset( &axis, 0, sizeof( axis ) ); + + 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 ); + } + } + else if ( kHIDPage_Button == usagePage ) + { + m_buttons.Push( element ); + + HIDQueueElement( m_device, element ); + } + + element = HIDGetNextDeviceElement( element, kHIDElementTypeInput ); + } + + SetDefaultConfig(); +} + +IOKitJoystick::~IOKitJoystick() +{ + M_SaveJoystickConfig( this ); +} + + +FString IOKitJoystick::GetName() +{ + FString result; + + result += ToFString( IOHIDDevice_GetManufacturer( m_device ) ); + result += " "; + result += ToFString( IOHIDDevice_GetProduct( m_device ) ); + + return result; +} + + +float IOKitJoystick::GetSensitivity() +{ + return m_sensitivity; +} + +void IOKitJoystick::SetSensitivity( float scale ) +{ + m_sensitivity = scale; +} + + +int IOKitJoystick::GetNumAxes() +{ + return static_cast< int >( m_axes.Size() ); +} + +#define IS_AXIS_VALID ( static_cast< unsigned int >( axis ) < m_axes.Size() ) + +float IOKitJoystick::GetAxisDeadZone( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].deadZone : 0.0f; +} + +EJoyAxis IOKitJoystick::GetAxisMap( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].gameAxis : JOYAXIS_None; +} + +const char* IOKitJoystick::GetAxisName( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].name : "Invalid"; +} + +float IOKitJoystick::GetAxisScale( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].sensitivity : 0.0f; +} + +void IOKitJoystick::SetAxisDeadZone( int axis, float deadZone ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].deadZone = clamp( deadZone, 0.0f, 1.0f ); + } +} + +void IOKitJoystick::SetAxisMap( int axis, EJoyAxis gameAxis ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].gameAxis = ( gameAxis > JOYAXIS_None && gameAxis < NUM_JOYAXIS ) + ? gameAxis + : JOYAXIS_None; + } +} + +void IOKitJoystick::SetAxisScale( int axis, float scale ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].sensitivity = scale; + } +} + + +bool IOKitJoystick::IsSensitivityDefault() +{ + return DEFAULT_SENSITIVITY == m_sensitivity; +} + +bool IOKitJoystick::IsAxisDeadZoneDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].deadZone == m_axes[ axis ].defaultDeadZone ) + : true; +} + +bool IOKitJoystick::IsAxisMapDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].gameAxis == m_axes[ axis ].defaultGameAxis ) + : true; +} + +bool IOKitJoystick::IsAxisScaleDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].sensitivity == m_axes[ axis ].defaultSensitivity ) + : true; +} + +#undef IS_AXIS_VALID + +void IOKitJoystick::SetDefaultConfig() +{ + m_sensitivity = DEFAULT_SENSITIVITY; + + const size_t axisCount = m_axes.Size(); + + for ( size_t i = 0; i < axisCount; ++i ) + { + m_axes[i].deadZone = DEFAULT_DEADZONE; + m_axes[i].sensitivity = DEFAULT_SENSITIVITY; + m_axes[i].gameAxis = JOYAXIS_None; + } + + // Two axes? Horizontal is yaw and vertical is forward. + + if ( 2 == axisCount) + { + m_axes[0].gameAxis = JOYAXIS_Yaw; + m_axes[1].gameAxis = JOYAXIS_Forward; + } + + // Three axes? First two are movement, third is yaw. + + 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 ) + { + 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 ) + { + 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 ) + { + m_axes[i].defaultDeadZone = m_axes[i].deadZone; + m_axes[i].defaultSensitivity = m_axes[i].sensitivity; + m_axes[i].defaultGameAxis = m_axes[i].gameAxis; + } +} + + +FString IOKitJoystick::GetIdentifier() +{ + char identifier[ 32 ] = {0}; + + snprintf( identifier, sizeof( identifier ), "VID_%04lx_PID_%04lx", + IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) ); + + return FString( identifier ); +} + + +void IOKitJoystick::AddAxes( float axes[ NUM_JOYAXIS ] ) const +{ + for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + { + const EJoyAxis axis = m_axes[i].gameAxis; + + if ( JOYAXIS_None == axis ) + { + continue; + } + + axes[ axis ] -= m_axes[i].value; + } +} + + +void IOKitJoystick::Update() +{ + IOHIDValueRef value = NULL; + + while ( HIDGetEvent( m_device, &value ) && NULL != value ) + { + ProcessAxis( value ) || ProcessButton( value ) || ProcessPOV( value ); + + CFRelease( value ); + } +} + + +bool IOKitJoystick::ProcessAxis( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + { + if ( element != m_axes[i].element ) + { + 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 ); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessButton( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_buttons.Size(); i < count; ++i ) + { + if ( element != m_buttons[i] ) + { + continue; + } + + const int newButton = IOHIDValueGetIntegerValue( value ) & 1; + const int oldButton = ~newButton; + + Joy_GenerateButtonEvents( oldButton, newButton, 1, + static_cast< int >( KEY_FIRSTJOYBUTTON + i ) ); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessPOV( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_POVs.Size(); i < count; ++i ) + { + if ( element != m_POVs[i] ) + { + continue; + } + + const CFIndex direction = IOHIDValueGetIntegerValue( value ); + + // Default values is for Up/North + int oldButtons = 0; + int newButtons = 1; + int numButtons = 1; + int baseButton = KEY_JOYPOV1_UP; + + switch ( direction ) + { + case 0: // N + break; + + case 1: // NE + newButtons = 3; + numButtons = 2; + break; + + case 2: // E + baseButton = KEY_JOYPOV1_RIGHT; + break; + + case 3: // SE + newButtons = 3; + numButtons = 2; + baseButton = KEY_JOYPOV1_RIGHT; + break; + + case 4: // S + baseButton = KEY_JOYPOV1_DOWN; + break; + + case 5: // SW + newButtons = 3; + numButtons = 2; + baseButton = KEY_JOYPOV1_DOWN; + break; + + case 6: // W + baseButton = KEY_JOYPOV1_LEFT; + break; + + case 7: // NW + newButtons = 9; // UP and LEFT + numButtons = 4; + break; + + default: + // release all four directions + oldButtons = 15; + newButtons = 0; + numButtons = 4; + break; + } + + Joy_GenerateButtonEvents( oldButtons, newButtons, numButtons, + static_cast< int >( baseButton + i * 4 ) ); + } + + return false; +} + + +// --------------------------------------------------------------------------- + + +class IOKitJoystickManager +{ +public: + IOKitJoystickManager(); + ~IOKitJoystickManager(); + + void GetJoysticks( TArray< IJoystickConfig* >& 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 ); + + void ReleaseJoysticks(); + + void EnableCallbacks(); + void DisableCallbacks(); + +}; + + +IOKitJoystickManager::IOKitJoystickManager() +{ + Rescan(); +} + +IOKitJoystickManager::~IOKitJoystickManager() +{ + ReleaseJoysticks(); + DisableCallbacks(); + + HIDReleaseDeviceList(); +} + + +void IOKitJoystickManager::GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const +{ + const size_t joystickCount = m_joysticks.Size(); + + joysticks.Resize( joystickCount ); + + for ( size_t i = 0; i < joystickCount; ++i ) + { + M_LoadJoystickConfig( m_joysticks[i] ); + + joysticks[i] = m_joysticks[i]; + } +} + +void IOKitJoystickManager::AddAxes( float axes[ NUM_JOYAXIS ] ) const +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + m_joysticks[i]->AddAxes( axes ); + } +} + + +void IOKitJoystickManager::Update() +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + m_joysticks[i]->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(); +} + + +void IOKitJoystickManager::OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device ) +{ + event_t event; + + memset( &event, 0, sizeof( event ) ); + event.type = EV_DeviceChange; + + D_PostEvent( &event ); +} + + +void IOKitJoystickManager::ReleaseJoysticks() +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + delete m_joysticks[i]; + } + + m_joysticks.Clear(); +} + + +void IOKitJoystickManager::EnableCallbacks() +{ + if ( NULL == gIOHIDManagerRef ) + { + return; + } + + IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, OnDeviceChanged, this ); + IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, OnDeviceChanged, this ); + IOHIDManagerScheduleWithRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); +} + +void IOKitJoystickManager::DisableCallbacks() +{ + if ( NULL == gIOHIDManagerRef ) + { + return; + } + + IOHIDManagerUnscheduleFromRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, NULL, NULL ); + IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, NULL, NULL ); +} + + +IOKitJoystickManager* s_joystickManager; + + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +void I_StartupJoysticks() +{ + s_joystickManager = new IOKitJoystickManager; +} + +void I_ShutdownJoysticks() +{ + delete s_joystickManager; +} + +void I_GetJoysticks( TArray< IJoystickConfig* >& sticks ) +{ + if ( NULL != s_joystickManager ) + { + s_joystickManager->GetJoysticks( sticks ); + } +} + +void I_GetAxes( float axes[ NUM_JOYAXIS ] ) +{ + for ( size_t i = 0; i < NUM_JOYAXIS; ++i ) + { + axes[i] = 0.0f; + } + + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->AddAxes( axes ); + } +} + +IJoystickConfig* I_UpdateDeviceList() +{ + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->Rescan(); + } + + return NULL; +} + + +// --------------------------------------------------------------------------- + + +void I_ProcessJoysticks() +{ + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->Update(); + } +} diff --git a/src/cocoa/i_timer.cpp b/src/cocoa/i_timer.cpp new file mode 100644 index 000000000..29d3f8865 --- /dev/null +++ b/src/cocoa/i_timer.cpp @@ -0,0 +1,191 @@ + +#include +#include +#include + +#include + +#include "basictypes.h" +#include "basicinlines.h" +#include "doomdef.h" +#include "i_system.h" +#include "templates.h" + + +unsigned int I_MSTime() +{ + return SDL_GetTicks(); +} + +unsigned int I_FPSTime() +{ + return SDL_GetTicks(); +} + + +bool g_isTicFrozen; + + +namespace +{ + +timespec GetNextTickTime() +{ + static const long MILLISECONDS_IN_SECOND = 1000; + static const long MICROSECONDS_IN_SECOND = 1000 * MILLISECONDS_IN_SECOND; + static const long NANOSECONDS_IN_SECOND = 1000 * MICROSECONDS_IN_SECOND; + + static timespec ts = {}; + + if (__builtin_expect((0 == ts.tv_sec), 0)) + { + timeval tv; + gettimeofday(&tv, NULL); + + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = (tv.tv_usec + MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND; + } + else + { + ts.tv_nsec += (MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND; + } + + if (ts.tv_nsec >= NANOSECONDS_IN_SECOND) + { + ts.tv_sec++; + ts.tv_nsec -= NANOSECONDS_IN_SECOND; + } + + return ts; +} + + +pthread_cond_t s_timerEvent; +pthread_mutex_t s_timerMutex; +pthread_t s_timerThread; + +bool s_timerInitialized; +bool s_timerExitRequested; + +uint32_t s_ticStart; +uint32_t s_ticNext; + +uint32_t s_timerStart; +uint32_t s_timerNext; + +int s_tics; + + +void* TimerThreadFunc(void*) +{ + assert(s_timerInitialized); + assert(!s_timerExitRequested); + + while (true) + { + if (s_timerExitRequested) + { + break; + } + + const timespec timeToNextTick = GetNextTickTime(); + + pthread_mutex_lock(&s_timerMutex); + pthread_cond_timedwait(&s_timerEvent, &s_timerMutex, &timeToNextTick); + + if (!g_isTicFrozen) + { + __sync_add_and_fetch(&s_tics, 1); + } + + s_timerStart = SDL_GetTicks(); + s_timerNext = Scale(Scale(s_timerStart, TICRATE, 1000) + 1, 1000, TICRATE); + + pthread_cond_broadcast(&s_timerEvent); + pthread_mutex_unlock(&s_timerMutex); + } + + return NULL; +} + +int GetTimeThreaded(bool saveMS) +{ + if (saveMS) + { + s_ticStart = s_timerStart; + s_ticNext = s_timerNext; + } + + return s_tics; +} + +int WaitForTicThreaded(int prevTic) +{ + assert(!g_isTicFrozen); + + while (s_tics <= prevTic) + { + pthread_mutex_lock(&s_timerMutex); + pthread_cond_wait(&s_timerEvent, &s_timerMutex); + pthread_mutex_unlock(&s_timerMutex); + } + + return s_tics; +} + +void FreezeTimeThreaded(bool frozen) +{ + g_isTicFrozen = frozen; +} + +} // unnamed namespace + + +fixed_t I_GetTimeFrac(uint32* ms) +{ + const uint32_t now = SDL_GetTicks(); + + if (NULL != ms) + { + *ms = s_ticNext; + } + + const uint32_t step = s_ticNext - s_ticStart; + + return 0 == step + ? FRACUNIT + : clamp( (now - s_ticStart) * FRACUNIT / step, 0, FRACUNIT); +} + + +void I_InitTimer () +{ + assert(!s_timerInitialized); + s_timerInitialized = true; + + pthread_cond_init (&s_timerEvent, NULL); + pthread_mutex_init(&s_timerMutex, NULL); + + pthread_create(&s_timerThread, NULL, TimerThreadFunc, NULL); + + I_GetTime = GetTimeThreaded; + I_WaitForTic = WaitForTicThreaded; + I_FreezeTime = FreezeTimeThreaded; +} + +void I_ShutdownTimer () +{ + if (!s_timerInitialized) + { + // This might happen if Cancel button was pressed + // in the IWAD selector window + return; + } + + s_timerExitRequested = true; + + pthread_join(s_timerThread, NULL); + + pthread_mutex_destroy(&s_timerMutex); + pthread_cond_destroy (&s_timerEvent); +} diff --git a/src/cocoa/zdoom-info.plist b/src/cocoa/zdoom-info.plist new file mode 100644 index 000000000..8371b0555 --- /dev/null +++ b/src/cocoa/zdoom-info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + zdoom.icns + CFBundleIdentifier + org.zdoom.zdoom + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ZDoom + CFBundlePackageType + APPL + CFBundleShortVersionString + Version 2.8.0 + CFBundleSignature + ???? + LSApplicationCategoryType + public.app-category.action-games + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + CFBundleDocumentTypes + + + CFBundleTypeName + Doom Resource File + CFBundleTypeRole + Viewer + CFBundleTypeExtensions + + wad + pk3 + zip + pk7 + 7z + + + + NSPrincipalClass + NSApplication + + diff --git a/src/cocoa/zdoom.icns b/src/cocoa/zdoom.icns new file mode 100644 index 0000000000000000000000000000000000000000..decb4c5ab4054a1ec0a67990d88f8dd89bc982d9 GIT binary patch literal 196176 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 ^mYw-ZTq{;6VDK=D6 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

>gRkX`I67s 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%0A1(#TI3w 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 literal 0 HcmV?d00001 diff --git a/src/cocoa/zdoom.xib b/src/cocoa/zdoom.xib new file mode 100644 index 000000000..56099dc4b --- /dev/null +++ b/src/cocoa/zdoom.xib @@ -0,0 +1,874 @@ + + + + 1060 + 11C74 + 851 + 1138.23 + 567.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 851 + + + YES + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + Main Menu + + YES + + + ZDoom + + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + ZDoom + + YES + + + About ZDoom + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Preferencesā€¦ + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Services + + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 2147483647 + + + + + + Hide ZDoom + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Quit ZDoom + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Edit + + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Delete + + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + + + + Window + + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bring All to Front + + 2147483647 + + + + + _NSWindowsMenu + + + + _NSMainMenu + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performZoom: + + + + 240 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + cut: + + + + 738 + + + + paste: + + + + 739 + + + + redo: + + + + 742 + + + + undo: + + + + 746 + + + + copy: + + + + 752 + + + + delete: + + + + 753 + + + + selectAll: + + + + 755 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 371 + + + + + 681 + + + YES + + + + + + 682 + + + YES + + + + + + + + + + + + + 683 + + + + + 684 + + + + + 685 + + + + + 686 + + + + + 687 + + + + + 688 + + + + + 690 + + + + + 691 + + + + + + + YES + + YES + -3.IBPluginDependency + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 681.IBPluginDependency + 682.IBEditorWindowLastContentRect + 682.IBPluginDependency + 683.IBPluginDependency + 684.IBPluginDependency + 685.IBPluginDependency + 686.IBPluginDependency + 687.IBPluginDependency + 688.IBPluginDependency + 690.IBPluginDependency + 691.IBPluginDependency + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{651, 262}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{514, 335}, {220, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{11, 977}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{487, 217}, {195, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + {{607, 182}, {151, 153}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + + YES + + + + + YES + + + YES + + + + 842 + + + + YES + + NSObject + + IBFrameworkSource + Print.framework/Headers/PDEPluginInterface.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ZDoom.xcodeproj + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + + + From 177112603dd4d146a8e33879ef2a742e53a9eaee Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sun, 3 Aug 2014 12:38:56 +0300 Subject: [PATCH 009/313] Added support for Cocoa back-end in CMake build system --- src/CMakeLists.txt | 79 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87c3f4dfa..a21da98b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,8 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON ) +option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" OFF ) + if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) endif( CMAKE_SIZEOF_VOID_P MATCHES "8" ) @@ -211,7 +213,9 @@ else( WIN32 ) if( NOT SDL_FOUND ) message( SEND_ERROR "SDL is required for building." ) endif( NOT SDL_FOUND ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" ) + if( NOT APPLE OR NOT OSX_COCOA_BACKEND ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" ) + endif( NOT APPLE OR NOT OSX_COCOA_BACKEND ) include_directories( "${SDL_INCLUDE_DIR}" ) find_path( FPU_CONTROL_DIR fpu_control.h ) @@ -555,23 +559,62 @@ set( PLAT_WIN32_SOURCES win32/i_system.cpp win32/st_start.cpp win32/win32video.cpp ) -set( PLAT_SDL_SOURCES +set( PLAT_SDL_SYSTEM_SOURCES sdl/crashcatcher.c sdl/hardware.cpp sdl/i_cd.cpp - sdl/i_cursor.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 ) +set( PLAT_SDL_INPUT_SOURCES + sdl/i_cursor.cpp + sdl/i_input.cpp + sdl/i_joystick.cpp + sdl/i_timer.cpp ) set( PLAT_MAC_SOURCES - sdl/SDLMain.m sdl/iwadpicker_cocoa.mm sdl/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/i_backend_cocoa.mm + cocoa/i_joystick.cpp + cocoa/i_timer.cpp + cocoa/zdoom.icns ) + +if( APPLE ) + if( OSX_COCOA_BACKEND ) + find_program( IBTOOL ibtool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + if( ${IBTOOL} STREQUAL "IBTOOL-NOTFOUND" ) + message( SEND_ERROR "ibtool can not be found to compile xib files." ) + endif( ${IBTOOL} STREQUAL "IBTOOL-NOTFOUND" ) + + set( NIB_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/zdoom.nib" ) + add_custom_command( OUTPUT "${NIB_FILE}" + COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text + --compile "${NIB_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/zdoom.xib" + COMMENT "Compiling zdoom.xib" ) + + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_COCOA_SOURCES} "${NIB_FILE}" "${FMOD_LIBRARY}" ) + + set_source_files_properties( "${NIB_FILE}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) + else( OSX_COCOA_BACKEND ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_INPUT_SOURCES} "${FMOD_LIBRARY}" ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/SDLMain.m ) + 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 ) +endif( APPLE ) + if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) @@ -773,7 +816,7 @@ set( NOT_COMPILED_SOURCE_FILES asm_x86_64/tmap3.s ) -add_executable( zdoom WIN32 +add_executable( zdoom WIN32 MACOSX_BUNDLE ${HEADER_FILES} ${NOT_COMPILED_SOURCE_FILES} __autostart.cpp @@ -1165,6 +1208,25 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( SSE_MATTERS ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) +if( APPLE ) + set_target_properties(zdoom PROPERTIES + LINK_FLAGS "-framework Cocoa -framework IOKit -framework OpenGL" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/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" ) + find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" + COMMAND grep "libfmodex.dylib (compat" + COMMAND head -n1 + COMMAND awk "{print $1}" + OUTPUT_VARIABLE FMOD_LINK + OUTPUT_STRIP_TRAILING_WHITESPACE ) + add_custom_command( TARGET zdoom POST_BUILD + COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/libfmodex.dylib "$" + COMMENT "Relinking FMOD Ex" ) +endif( APPLE ) + source_group("Assembly Files\\ia32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/asm_ia32/.+") source_group("Assembly Files\\x86_64" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/asm_x86_64/.+") source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+") @@ -1172,6 +1234,7 @@ 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/.+") From 4fb1e7517c8586cbd6db4ea935ac2bd191ebc520 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:39:42 +0300 Subject: [PATCH 010/313] Added support for VSync option in Cocoa back-end --- src/sdl/sdlvideo.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 92741d325..c869545ab 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -15,6 +15,10 @@ #include +#ifdef __APPLE__ +#include +#endif // __APPLE__ + // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- @@ -43,6 +47,8 @@ public: friend class SDLVideo; + virtual void SetVSync (bool vsync); + private: PalEntry SourcePalette[256]; BYTE GammaTable[3][256]; @@ -82,6 +88,7 @@ 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 ------------------------------------------------- @@ -326,6 +333,7 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) } memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdateColors (); + SetVSync (vid_vsync); } SDLFB::~SDLFB () @@ -535,6 +543,19 @@ bool SDLFB::IsFullscreen () return (Screen->flags & SDL_FULLSCREEN) != 0; } +void SDLFB::SetVSync (bool vsync) +{ +#ifdef __APPLE__ + if (CGLContextObj context = CGLGetCurrentContext()) + { + // Apply vsync for native backend only (where OpenGL context is set) + + const GLint value = vsync ? 1 : 0; + CGLSetParameter(context, kCGLCPSwapInterval, &value); + } +#endif // __APPLE__ +} + ADD_STAT (blit) { FString out; From 313173aa32fb869bb038ad73f48beabf8b9e6201 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 3 Aug 2014 12:40:14 +0300 Subject: [PATCH 011/313] Added support for clipboard operations on OS X --- src/sdl/i_system.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index ca5ffa2e2..d65dbc6c8 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -78,6 +78,10 @@ #undef GC #endif +#ifdef __APPLE__ +#include +#endif // __APPLE__ + EXTERN_CVAR (String, language) extern "C" @@ -609,6 +613,10 @@ int I_FindAttr (findstate_t *fileinfo) return 0; } +#ifdef __APPLE__ +static PasteboardRef s_clipboard; +#endif // __APPLE__ + // Clipboard support requires GTK+ // TODO: GTK+ uses UTF-8. We don't, so some conversions would be appropriate. void I_PutInClipboard (const char *str) @@ -629,6 +637,23 @@ void I_PutInClipboard (const char *str) } */ } +#elif defined __APPLE__ + if (NULL == s_clipboard) + { + PasteboardCreate(kPasteboardClipboard, &s_clipboard); + } + + PasteboardClear(s_clipboard); + PasteboardSynchronize(s_clipboard); + + const CFDataRef textData = CFDataCreate(kCFAllocatorDefault, + reinterpret_cast(str), strlen(str)); + + if (NULL != textData) + { + PasteboardPutItemFlavor(s_clipboard, PasteboardItemID(1), + CFSTR("public.utf8-plain-text"), textData, 0); + } #endif } @@ -650,6 +675,61 @@ FString I_GetFromClipboard (bool use_primary_selection) } } } +#elif defined __APPLE__ + FString result; + + if (NULL == s_clipboard) + { + PasteboardCreate(kPasteboardClipboard, &s_clipboard); + } + + PasteboardSynchronize(s_clipboard); + + ItemCount itemCount = 0; + PasteboardGetItemCount(s_clipboard, &itemCount); + + if (0 == itemCount) + { + return FString(); + } + + PasteboardItemID itemID; + + if (0 != PasteboardGetItemIdentifier(s_clipboard, 1, &itemID)) + { + return FString(); + } + + CFArrayRef flavorTypeArray; + + if (0 != PasteboardCopyItemFlavors(s_clipboard, itemID, &flavorTypeArray)) + { + return FString(); + } + + const CFIndex flavorCount = CFArrayGetCount(flavorTypeArray); + + for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex) + { + const CFStringRef flavorType = static_cast( + CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex)); + + if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) + { + CFDataRef flavorData; + + if (0 == PasteboardCopyItemFlavorData(s_clipboard, itemID, flavorType, &flavorData)) + { + result += reinterpret_cast(CFDataGetBytePtr(flavorData)); + } + + CFRelease(flavorData); + } + } + + CFRelease(flavorTypeArray); + + return result; #endif return ""; } From 1fcde912984c9a5628ca01737b24c9787153c1de Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 8 Aug 2014 09:25:35 +0200 Subject: [PATCH 012/313] - BlueShadow's GetArmorInfo submission. --- src/p_acs.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2ca6fe909..1646ab7b7 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -120,6 +120,16 @@ FRandom pr_acs ("ACS"); #define SDF_ABSANGLE 1 #define SDF_PERMANENT 2 +// GetArmorInfo +enum +{ + ARMORINFO_CLASSNAME, + ARMORINFO_SAVEAMOUNT, + ARMORINFO_SAVEPERCENT, + ARMORINFO_MAXABSORB, + ARMORINFO_MAXFULLABSORB, +}; + struct CallReturn { CallReturn(int pc, ScriptFunction *func, FBehavior *module, SDWORD *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) @@ -4349,6 +4359,7 @@ enum EACSFunctions ACSF_GetActorPowerupTics, ACSF_ChangeActorAngle, ACSF_ChangeActorPitch, // 80 + ACSF_GetArmorInfo, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -4822,6 +4833,38 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const return 0; } + case ACSF_GetArmorInfo: + { + if (activator == NULL || activator->player == NULL) return 0; + + ABasicArmor * equippedarmor = (ABasicArmor *) activator->FindInventory(NAME_BasicArmor); + + if (equippedarmor && equippedarmor->Amount != 0) + { + switch(args[0]) + { + case ARMORINFO_CLASSNAME: + return GlobalACSStrings.AddString(equippedarmor->ArmorType.GetChars(), stack, stackdepth); + + case ARMORINFO_SAVEAMOUNT: + return equippedarmor->MaxAmount; + + case ARMORINFO_SAVEPERCENT: + return equippedarmor->SavePercent; + + case ARMORINFO_MAXABSORB: + return equippedarmor->MaxAbsorb; + + case ARMORINFO_MAXFULLABSORB: + return equippedarmor->MaxFullAbsorb; + + default: + return 0; + } + } + return args[0] == ARMORINFO_CLASSNAME ? GlobalACSStrings.AddString("None", stack, stackdepth) : 0; + } + case ACSF_SpawnSpotForced: return DoSpawnSpot(args[0], args[1], args[2], args[3], true); From 82e8c514e993910dcd351f4227a7c8d5c42aefa8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 9 Aug 2014 13:20:52 +0300 Subject: [PATCH 013/313] Renamed SDL specific source file with GUI related code --- src/CMakeLists.txt | 2 +- src/sdl/{i_cursor.cpp => i_gui.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/sdl/{i_cursor.cpp => i_gui.cpp} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a21da98b5..72a7c7f01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,7 +569,7 @@ set( PLAT_SDL_SYSTEM_SOURCES sdl/sdlvideo.cpp sdl/st_start.cpp ) set( PLAT_SDL_INPUT_SOURCES - sdl/i_cursor.cpp + sdl/i_gui.cpp sdl/i_input.cpp sdl/i_joystick.cpp sdl/i_timer.cpp ) diff --git a/src/sdl/i_cursor.cpp b/src/sdl/i_gui.cpp similarity index 100% rename from src/sdl/i_cursor.cpp rename to src/sdl/i_gui.cpp From 18c9caf68d764759d35182dbf86b4bfbc873862f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 9 Aug 2014 13:32:32 +0300 Subject: [PATCH 014/313] Enhanced Cocoa version of IWAD picker window Added ability to specify custom command line parameters Added ability to browse for user files Improved handling of restart console command Improved layout for window --- src/cocoa/i_backend_cocoa.mm | 17 ++- src/sdl/i_gui.cpp | 5 + src/sdl/iwadpicker_cocoa.mm | 235 ++++++++++++++++++++++++++++++++--- 3 files changed, 239 insertions(+), 18 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index d4c353edd..675fde182 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -144,6 +144,8 @@ char* s_argv[ARGC_MAX]; TArray s_argvStorage; +bool s_restartedFromWADPicker; + bool s_nativeMouse = true; @@ -966,7 +968,9 @@ static ApplicationDelegate* s_applicationDelegate; { ZD_UNUSED(theApplication); - if (0 == [filename length] || s_argc + 2 >= ARGC_MAX) + if (s_restartedFromWADPicker + || 0 == [filename length] + || s_argc + 2 >= ARGC_MAX) { return FALSE; } @@ -1724,8 +1728,15 @@ int main(int argc, char** argv) continue; } - s_argvStorage.Push(argument); - s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + 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]; diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index c4202a6b3..2ecdcdb74 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -71,3 +71,8 @@ bool I_SetCursor(FTexture *cursorpic) } return true; } + +void I_SetMainWindowVisible(bool visible) +{ + +} diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 3b414d5e8..7a6b38623 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -33,9 +33,17 @@ ** */ +#include "cmdlib.h" #include "d_main.h" #include "version.h" +#include "c_cvars.h" +#include "m_argv.h" +#include "m_misc.h" +#include "gameconfigfile.h" #include +#include + +CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG); enum { @@ -107,6 +115,45 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; @end +static NSDictionary* GetKnownFileTypes() +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + @"-file" , @"wad", + @"-file" , @"pk3", + @"-file" , @"zip", + @"-file" , @"pk7", + @"-file" , @"7z", + @"-deh" , @"deh", + @"-bex" , @"bex", + @"-exec" , @"cfg", + @"-playdemo", @"lmp", + nil]; +} + +static NSArray* GetKnownExtensions() +{ + return [GetKnownFileTypes() allKeys]; +} + +@interface NSMutableString(AppendKnownFileType) +- (void)appendKnownFileType:(NSString *)filePath; +@end + +@implementation NSMutableString(AppendKnownFileType) +- (void)appendKnownFileType:(NSString *)filePath +{ + NSString* extension = [[filePath pathExtension] lowercaseString]; + NSString* parameter = [GetKnownFileTypes() objectForKey:extension]; + + if (nil == parameter) + { + return; + } + + [self appendFormat:@"%@ \"%@\" ", parameter, filePath]; +} +@end + // So we can listen for button actions and such we need to have an Obj-C class. @interface IWADPicker : NSObject { @@ -114,13 +161,18 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; NSWindow *window; NSButton *okButton; NSButton *cancelButton; + NSButton *browseButton; + NSTextField *parametersTextField; bool cancelled; } - (void)buttonPressed:(id) sender; +- (void)browseButtonPressed:(id) sender; - (void)doubleClicked:(id) sender; - (void)makeLabel:(NSTextField *)label withString:(const char*) str; - (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad; +- (NSString*)commandLineParameters; +- (void)menuActionSent:(NSNotification*)notification; @end @implementation IWADPicker @@ -134,6 +186,52 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [app stopModal]; } +- (void)browseButtonPressed:(id) sender +{ + NSOpenPanel* openPanel = [NSOpenPanel openPanel]; + [openPanel setAllowsMultipleSelection:YES]; + [openPanel setCanChooseFiles:YES]; + [openPanel setCanChooseDirectories:YES]; + [openPanel setResolvesAliases:YES]; + [openPanel setAllowedFileTypes:GetKnownExtensions()]; + + if (NSOKButton == [openPanel runModal]) + { + NSArray* files = [openPanel URLs]; + NSMutableString* parameters = [NSMutableString string]; + + for (NSUInteger i = 0, ei = [files count]; i < ei; ++i) + { + NSString* filePath = [[files objectAtIndex:i] path]; + BOOL isDirectory = false; + + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory] && isDirectory) + { + [parameters appendFormat:@"-file \"%@\" ", filePath]; + } + else + { + [parameters appendKnownFileType:filePath]; + } + } + + if ([parameters length] > 0) + { + NSString* newParameters = [parametersTextField stringValue]; + + if ([newParameters length] > 0 + && NO == [newParameters hasSuffix:@" "]) + { + newParameters = [newParameters stringByAppendingString:@" "]; + } + + newParameters = [newParameters stringByAppendingString:parameters]; + + [parametersTextField setStringValue: newParameters]; + } + } +} + - (void)doubleClicked:(id) sender { if ([sender clickedRow] >= 0) @@ -159,20 +257,18 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; cancelled = false; app = [NSApplication sharedApplication]; - id windowTitle = [NSString stringWithFormat:@GAMESIG " %s: Select an IWAD to use", GetVersionString()]; + id windowTitle = [NSString stringWithFormat:@"%s %s", GAMENAME, GetVersionString()]; NSRect frame = NSMakeRect(0, 0, 440, 450); window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]; [window setTitle:windowTitle]; - NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(22, 379, 412, 50)]; - [self makeLabel:description withString:"ZDoom found more than one IWAD\nSelect from the list below to determine which one to use:"]; + NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 384, 402, 50)]; + [self makeLabel:description withString:GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"]; [[window contentView] addSubview:description]; [description release]; - // Commented out version would account for an additional parameters box. - //NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 103, 412, 288)]; - NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 50, 412, 341)]; + NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 135, 402, 256)]; NSTableView *iwadTable = [[NSTableView alloc] initWithFrame:[iwadScroller bounds]]; IWADTableData *tableData = [[IWADTableData alloc] init:wads num:numwads]; for(int i = 0;i < NUM_COLUMNS;i++) @@ -200,11 +296,12 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [iwadTable release]; [iwadScroller release]; - /*NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(17, 78, 144, 17)]; - [self makeLabel:additionalParametersLabel:"Additional Parameters"]; + NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 108, 144, 17)]; + [self makeLabel:additionalParametersLabel withString:"Additional Parameters:"]; [[window contentView] addSubview:additionalParametersLabel]; - NSTextField *additionalParameters = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 360, 22)]; - [[window contentView] addSubview:additionalParameters];*/ + parametersTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 402, 54)]; + [parametersTextField setStringValue:[NSString stringWithUTF8String:osx_additional_parameters]]; + [[window contentView] addSubview:parametersTextField]; // Doesn't look like the SDL version implements this so lets not show it. /*NSButton *dontAsk = [[NSButton alloc] initWithFrame:NSMakeRect(18, 18, 178, 18)]; @@ -213,39 +310,147 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [dontAsk setState:(showwin ? NSOffState : NSOnState)]; [[window contentView] addSubview:dontAsk];*/ - okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 12, 96, 32)]; - [okButton setTitle:[NSString stringWithUTF8String:"OK"]]; + okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 8, 96, 32)]; + [okButton setTitle:@"OK"]; [okButton setBezelStyle:NSRoundedBezelStyle]; [okButton setAction:@selector(buttonPressed:)]; [okButton setTarget:self]; [okButton setKeyEquivalent:@"\r"]; [[window contentView] addSubview:okButton]; - cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 12, 96, 32)]; - [cancelButton setTitle:[NSString stringWithUTF8String:"Cancel"]]; + cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 8, 96, 32)]; + [cancelButton setTitle:@"Cancel"]; [cancelButton setBezelStyle:NSRoundedBezelStyle]; [cancelButton setAction:@selector(buttonPressed:)]; [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]; + [browseButton setAction:@selector(browseButtonPressed:)]; + [browseButton setTarget:self]; + [[window contentView] addSubview:browseButton]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self selector:@selector(menuActionSent:) name:NSMenuDidSendActionNotification object:nil]; [window center]; [app runModalForWindow:window]; + [center removeObserver:self name:NSMenuDidSendActionNotification object:nil]; + [window release]; [okButton release]; [cancelButton release]; + [browseButton release]; return cancelled ? -1 : [iwadTable selectedRow]; } +- (NSString*)commandLineParameters +{ + return [parametersTextField stringValue]; +} + +- (void)menuActionSent:(NSNotification*)notification +{ + NSDictionary* userInfo = [notification userInfo]; + NSMenuItem* menuItem = [userInfo valueForKey:@"MenuItem"]; + + if ( @selector(terminate:) == [menuItem action] ) + { + exit(0); + } +} + @end + +EXTERN_CVAR(String, defaultiwad) + +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)]; + + NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 3]; + [arguments addObject:@"-wad_picker_restart"]; + [arguments addObject:@"-iwad"]; + [arguments addObject:[NSString stringWithUTF8String:iwadPath]]; + + for (int i = 1; i < commandLineParametersCount; ++i) + { + NSString* currentParameter = [NSString stringWithUTF8String:Args->GetArg(i)]; + [arguments addObject:currentParameter]; + } + + wordexp_t expansion; + + if (0 == wordexp([parameters UTF8String], &expansion, 0)) + { + for (size_t i = 0; i < expansion.we_wordc; ++i) + { + NSString* argumentString = [NSString stringWithCString:expansion.we_wordv[i] + encoding:NSUTF8StringEncoding]; + [arguments addObject:argumentString]; + } + + wordfree(&expansion); + } + + [NSTask launchedTaskWithLaunchPath:executablePath arguments:arguments]; + + _exit(0); // to avoid atexit()'s functions + } + @catch (NSException* e) + { + NSLog(@"Cannot restart: %@", [e reason]); + } + + [pool release]; +} + +void I_SetMainWindowVisible(bool visible); + // Simple wrapper so we can call this from outside. int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + I_SetMainWindowVisible(false); + IWADPicker *picker = [IWADPicker alloc]; int ret = [picker pickIWad:wads num:numwads showWindow:showwin defaultWad:defaultiwad]; - [picker release]; + + I_SetMainWindowVisible(true); + + NSString* parametersToAppend = [picker commandLineParameters]; + osx_additional_parameters = [parametersToAppend UTF8String]; + + if (ret >= 0) + { + if (0 != [parametersToAppend length]) + { + RestartWithParameters(wads[ret].Path, parametersToAppend); + } + } + + [pool release]; + return ret; } From efdfeeec908357db63de431bacb264ccada5d507 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 9 Aug 2014 15:51:39 +0300 Subject: [PATCH 015/313] Distinguish SDL and Cocoa back-ends in startup log --- src/cocoa/i_backend_cocoa.mm | 9 +++++++++ src/sdl/i_gui.cpp | 5 +++++ src/sdl/i_main.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 675fde182..d7e46dfe0 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1310,6 +1310,15 @@ bool I_SetCursor(FTexture* cursorpic) // --------------------------------------------------------------------------- +const char* I_GetBackEndName() +{ + return "Native Cocoa"; +} + + +// --------------------------------------------------------------------------- + + extern "C" { diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index 2ecdcdb74..37fc750cb 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -76,3 +76,8 @@ 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 3e199a165..7a18fb05f 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -239,6 +239,8 @@ static void unprotect_rtext() void I_StartupJoysticks(); void I_ShutdownJoysticks(); +const char* I_GetBackEndName(); + int main (int argc, char **argv) { #if !defined (__APPLE__) @@ -248,8 +250,8 @@ int main (int argc, char **argv) } #endif // !__APPLE__ - printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n", - GetVersionString(), GetGitTime(), __DATE__); + printf(GAMENAME" %s - %s - %s version\nCompiled on %s\n", + GetVersionString(), GetGitTime(), I_GetBackEndName(), __DATE__); seteuid (getuid ()); std::set_new_handler (NewFailure); From 406ee9234aee08b65450dacc57ffc027fc15c562 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 9 Aug 2014 16:27:07 +0300 Subject: [PATCH 016/313] Added two SDL functions needed for GZDoom --- src/cocoa/i_backend_cocoa.mm | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index d7e46dfe0..8d6f53b4c 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1476,8 +1476,13 @@ SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) return &resolutions[0]; } +int SDL_ShowCursor(int) +{ + // Does nothing + return 0; +} + -//static GLAuxilium::Texture2D* s_softwareTexture; static GLuint s_frameBufferTexture = 0; static const Uint16 BYTES_PER_PIXEL = 4; @@ -1597,6 +1602,19 @@ void SDL_GL_SwapBuffers() [[NSOpenGLContext currentContext] flushBuffer]; } +int SDL_GL_SetAttribute(SDL_GLattr attr, int value) +{ + if (SDL_GL_MULTISAMPLESAMPLES == attr) + { + [s_applicationDelegate setMultisample:value]; + } + + // Not interested in other attributes + + return 0; +} + + int SDL_LockSurface(SDL_Surface* surface) { ZD_UNUSED(surface); From f8dfdbd4a45ce6fd303f7ed519fc351685502120 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 10 Aug 2014 11:12:35 +0300 Subject: [PATCH 017/313] Extracted fullscreen and windowed modes handling to separate methods --- src/cocoa/i_backend_cocoa.mm | 107 +++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 8d6f53b4c..f219d48ae 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -878,7 +878,6 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) - (int)multisample; - (void)setMultisample:(int)multisample; -- (void)initializeOpenGL; - (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height; - (void)processEvents:(NSTimer*)timer; @@ -1077,70 +1076,78 @@ static ApplicationDelegate* s_applicationDelegate; m_openGLInitialized = true; } +- (void)fullscreenWithWidth:(int)width height:(int)height +{ + NSScreen* screen = [m_window screen]; + + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = IsHiDPISupported() + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast< float >(width ); + const float pixelScaleFactorY = displayHeight / static_cast< float >(height); + + s_frameBufferParameters.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); + + s_frameBufferParameters.width = width * s_frameBufferParameters.pixelScale; + s_frameBufferParameters.height = height * s_frameBufferParameters.pixelScale; + + s_frameBufferParameters.shiftX = (displayWidth - s_frameBufferParameters.width ) / 2.0f; + s_frameBufferParameters.shiftY = (displayHeight - s_frameBufferParameters.height) / 2.0f; + + [m_window setLevel:NSMainMenuWindowLevel + 1]; + [m_window setStyleMask:NSBorderlessWindowMask]; + [m_window setHidesOnDeactivate:YES]; + [m_window setFrame:displayRect display:YES]; + [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; +} + +- (void)windowedWithWidth:(int)width height:(int)height +{ + s_frameBufferParameters.pixelScale = 1.0f; + + s_frameBufferParameters.width = static_cast< float >(width ); + s_frameBufferParameters.height = static_cast< float >(height); + + s_frameBufferParameters.shiftX = 0.0f; + s_frameBufferParameters.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = IsHiDPISupported() + ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + [m_window setLevel:NSNormalWindowLevel]; + [m_window setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask]; + [m_window setHidesOnDeactivate:NO]; + [m_window setContentSize:windowSize]; + [m_window center]; +} + - (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height { [self initializeOpenGL]; - CGLContextObj context = CGLGetCurrentContext(); - NSView* view = [m_window contentView]; - if (fullscreen) { - NSScreen* screen = [m_window screen]; - const NSRect screenFrame = [screen frame]; - const NSRect displayRect = IsHiDPISupported() - ? [screen convertRectToBacking:screenFrame] - : screenFrame; - - const float displayWidth = displayRect.size.width; - const float displayHeight = displayRect.size.height; - - const float pixelScaleFactorX = displayWidth / static_cast< float >(width ); - const float pixelScaleFactorY = displayHeight / static_cast< float >(height); - - s_frameBufferParameters.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - - s_frameBufferParameters.width = width * s_frameBufferParameters.pixelScale; - s_frameBufferParameters.height = height * s_frameBufferParameters.pixelScale; - - s_frameBufferParameters.shiftX = (displayWidth - s_frameBufferParameters.width ) / 2.0f; - s_frameBufferParameters.shiftY = (displayHeight - s_frameBufferParameters.height) / 2.0f; - - [m_window setLevel:NSMainMenuWindowLevel + 1]; - [m_window setStyleMask:NSBorderlessWindowMask]; - [m_window setHidesOnDeactivate:YES]; - [m_window setFrame:displayRect display:YES]; - [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; + [self fullscreenWithWidth:width height:height]; } else { - s_frameBufferParameters.pixelScale = 1.0f; - - s_frameBufferParameters.width = static_cast< float >(width ); - s_frameBufferParameters.height = static_cast< float >(height); - - s_frameBufferParameters.shiftX = 0.0f; - s_frameBufferParameters.shiftY = 0.0f; - - const NSSize windowPixelSize = NSMakeSize(width, height); - const NSSize windowSize = IsHiDPISupported() - ? [view convertSizeFromBacking:windowPixelSize] - : windowPixelSize; - - [m_window setLevel:NSNormalWindowLevel]; - [m_window setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask]; - [m_window setHidesOnDeactivate:NO]; - [m_window setContentSize:windowSize]; - [m_window center]; + [self windowedWithWidth:width height:height]; } - + const NSSize viewSize = GetRealContentViewSize(m_window); glViewport(0, 0, viewSize.width, viewSize.height); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - - CGLFlushDrawable(context); + + CGLFlushDrawable(CGLGetCurrentContext()); static NSString* const TITLE_STRING = [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; From 2649b4c26afdbb587535f76193b03b1e6a34057a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 10 Aug 2014 11:22:20 +0300 Subject: [PATCH 018/313] Moved render buffer options definition to own header file This is needed for GZDoom --- src/cocoa/i_backend_cocoa.mm | 43 +++++++++++-------------------- src/cocoa/i_rbopts.h | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 src/cocoa/i_rbopts.h diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index f219d48ae..3bb926a61 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -63,6 +63,7 @@ #include "textures.h" #include "v_video.h" #include "version.h" +#include "i_rbopts.h" #undef Class @@ -92,6 +93,7 @@ // --------------------------------------------------------------------------- +RenderBufferOptions rbOpts; CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -123,20 +125,6 @@ void I_ShutdownJoysticks(); namespace { -struct FrameBufferParameters -{ - float pixelScale; - - float shiftX; - float shiftY; - - float width; - float height; -}; - -FrameBufferParameters s_frameBufferParameters; - - const int ARGC_MAX = 64; int s_argc; @@ -619,8 +607,8 @@ void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) const CGFloat frameHeight = GetRealContentViewSize(window).height; - const CGFloat posX = ( viewPos.x - s_frameBufferParameters.shiftX) / s_frameBufferParameters.pixelScale; - const CGFloat posY = (frameHeight - viewPos.y - s_frameBufferParameters.shiftY) / s_frameBufferParameters.pixelScale; + 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); @@ -1091,13 +1079,13 @@ static ApplicationDelegate* s_applicationDelegate; const float pixelScaleFactorX = displayWidth / static_cast< float >(width ); const float pixelScaleFactorY = displayHeight / static_cast< float >(height); - s_frameBufferParameters.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); + rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - s_frameBufferParameters.width = width * s_frameBufferParameters.pixelScale; - s_frameBufferParameters.height = height * s_frameBufferParameters.pixelScale; + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; - s_frameBufferParameters.shiftX = (displayWidth - s_frameBufferParameters.width ) / 2.0f; - s_frameBufferParameters.shiftY = (displayHeight - s_frameBufferParameters.height) / 2.0f; + rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; + rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; [m_window setLevel:NSMainMenuWindowLevel + 1]; [m_window setStyleMask:NSBorderlessWindowMask]; @@ -1108,13 +1096,13 @@ static ApplicationDelegate* s_applicationDelegate; - (void)windowedWithWidth:(int)width height:(int)height { - s_frameBufferParameters.pixelScale = 1.0f; + rbOpts.pixelScale = 1.0f; - s_frameBufferParameters.width = static_cast< float >(width ); - s_frameBufferParameters.height = static_cast< float >(height); + rbOpts.width = static_cast< float >(width ); + rbOpts.height = static_cast< float >(height); - s_frameBufferParameters.shiftX = 0.0f; - s_frameBufferParameters.shiftY = 0.0f; + rbOpts.shiftX = 0.0f; + rbOpts.shiftY = 0.0f; const NSSize windowPixelSize = NSMakeSize(width, height); const NSSize windowSize = IsHiDPISupported() @@ -1580,8 +1568,7 @@ static void ResetSoftwareViewport() glViewport(0, 0, viewport[0], viewport[1]); glClear(GL_COLOR_BUFFER_BIT); - glViewport(s_frameBufferParameters.shiftX, s_frameBufferParameters.shiftY, - s_frameBufferParameters.width, s_frameBufferParameters.height); + glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); } int SDL_WM_ToggleFullScreen(SDL_Surface* surface) diff --git a/src/cocoa/i_rbopts.h b/src/cocoa/i_rbopts.h new file mode 100644 index 000000000..c1a9d9d4d --- /dev/null +++ b/src/cocoa/i_rbopts.h @@ -0,0 +1,50 @@ +/* + ** 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; +}; + +extern RenderBufferOptions rbOpts; + +#endif // SRC_COCOA_I_RBOPTS_H_INCLUDED From 96a3e0f7298b75d89b01aa7b4c1118db8817b13c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 10 Aug 2014 12:30:19 +0300 Subject: [PATCH 019/313] Set more descriptive name for SDL source files list in CMake file --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 72a7c7f01..4a1a26867 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -568,7 +568,7 @@ set( PLAT_SDL_SYSTEM_SOURCES sdl/i_system.cpp sdl/sdlvideo.cpp sdl/st_start.cpp ) -set( PLAT_SDL_INPUT_SOURCES +set( PLAT_SDL_SPECIAL_SOURCES sdl/i_gui.cpp sdl/i_input.cpp sdl/i_joystick.cpp @@ -607,7 +607,7 @@ if( APPLE ) set_source_files_properties( "${NIB_FILE}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) else( OSX_COCOA_BACKEND ) - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_INPUT_SOURCES} "${FMOD_LIBRARY}" ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} "${FMOD_LIBRARY}" ) set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/SDLMain.m ) endif( OSX_COCOA_BACKEND ) From 259466c3d462f55a7f8b07abfe850c619d0076ab Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Aug 2014 09:39:38 +0200 Subject: [PATCH 020/313] fixed time printing. - In ZDoom the timer runs a bit too fast because roundoff errors make 35 tics only last 0.98 seconds. None of the internal timing has been changed, only the places where a time value is printed it will get adjusted for this discrepancy. --- src/cmdlib.h | 21 +++++++++++++++++++++ src/g_shared/sbarinfo_commands.cpp | 5 ++++- src/g_shared/shared_hud.cpp | 8 ++++---- src/g_shared/shared_sbar.cpp | 4 ++-- src/g_strife/strife_sbar.cpp | 20 ++++++++++++-------- src/statistics.cpp | 4 ++-- src/wi_stuff.cpp | 20 +++++++++++--------- 7 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/cmdlib.h b/src/cmdlib.h index e3a9d3d08..66747415a 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -62,4 +62,25 @@ struct FFileList void ScanDirectory(TArray &list, const char *dirpath); + +//========================================================================== +// +// Functions to compensate for a tic being a bit short. +// Since ZDoom uses a milliseconds timer for game timing +// 35 tics are actually only 0.98 seconds. +// For real time display this needs to be adjusted +// +//========================================================================== + +inline int AdjustTics(int tics) +{ + return (tics * 98) / 100; +} + +inline int Tics2Seconds(int tics) +{ + return (tics * 98) / (100 * TICRATE); +} + + #endif diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 629c31cb4..c9011e851 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -886,8 +886,11 @@ class CommandDrawString : public SBarInfoCommand } break; case TIME: - str.Format("%02d:%02d:%02d", (level.time/TICRATE)/3600, ((level.time/TICRATE)%3600)/60, (level.time/TICRATE)%60); + { + int sec = Tics2Seconds(level.time); + str.Format("%02d:%02d:%02d", sec / 3600, (sec % 3600) / 60, sec % 60); break; + } case LOGTEXT: str = statusBar->CPlayer->LogText; break; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 3806e5f9d..2daeff7a8 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -867,7 +867,7 @@ static void DrawTime() : (hud_showtime < 6 ? level.time : level.totaltime); - const int timeSeconds = timeTicks / TICRATE; + const int timeSeconds = Tics2Seconds(timeTicks); hours = timeSeconds / 3600; minutes = (timeSeconds % 3600) / 60; @@ -994,7 +994,7 @@ void DrawHUD() if (am_showtotaltime) { - seconds = level.totaltime / TICRATE; + seconds = Tics2Seconds(level.totaltime); mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60); DrawHudText(SmallFont, hudcolor_ttim, printstr, hudwidth-length, bottom, FRACUNIT); bottom -= fonth; @@ -1004,14 +1004,14 @@ void DrawHUD() { if (level.clusterflags&CLUSTER_HUB) { - seconds = level.time /TICRATE; + seconds = Tics2Seconds(level.time); mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60); DrawHudText(SmallFont, hudcolor_time, printstr, hudwidth-length, bottom, FRACUNIT); bottom -= fonth; } // Single level time for hubs - seconds= level.maptime /TICRATE; + seconds= Tics2Seconds(level.maptime); mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60); DrawHudText(SmallFont, hudcolor_ltim, printstr, hudwidth-length, bottom, FRACUNIT); } diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 6b2f609b7..89921e3a2 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1306,8 +1306,8 @@ void DBaseStatusBar::Draw (EHudState state) } else if (automapactive) { - int y, time = level.time / TICRATE, height; - int totaltime = level.totaltime / TICRATE; + int y, time = Tics2Seconds(level.time), height; + int totaltime = Tics2Seconds(level.totaltime); EColorRange highlight = (gameinfo.gametype & GAME_DoomChex) ? CR_UNTRANSLATED : CR_YELLOW; diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp index 1a355b90a..89659e61d 100644 --- a/src/g_strife/strife_sbar.cpp +++ b/src/g_strife/strife_sbar.cpp @@ -586,29 +586,33 @@ private: screen->DrawTexture (Images[back], left, top, DTA_CleanNoMove, true, DTA_Alpha, FRACUNIT*3/4, TAG_DONE); screen->DrawTexture (Images[bars], left, top, DTA_CleanNoMove, true, TAG_DONE); + switch (CurrentPop) { case POP_Log: + { + int seconds = Tics2Seconds(level.time); // Draw the latest log message. - mysnprintf (buff, countof(buff), "%02d:%02d:%02d", - (level.time/TICRATE)/3600, - ((level.time/TICRATE)%3600)/60, - (level.time/TICRATE)%60); + mysnprintf(buff, countof(buff), "%02d:%02d:%02d", + seconds / 3600, + (seconds % 3600) / 60, + (seconds) % 60); - screen->DrawText (SmallFont2, CR_UNTRANSLATED, left+210*xscale, top+8*yscale, buff, + screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff, DTA_CleanNoMove, true, TAG_DONE); if (CPlayer->LogText != NULL) { - FBrokenLines *lines = V_BreakLines (SmallFont2, 272, CPlayer->LogText); + FBrokenLines *lines = V_BreakLines(SmallFont2, 272, CPlayer->LogText); for (i = 0; lines[i].Width >= 0; ++i) { - screen->DrawText (SmallFont2, CR_UNTRANSLATED, left+24*xscale, top+(18+i*12)*yscale, + screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 24 * xscale, top + (18 + i * 12)*yscale, lines[i].Text, DTA_CleanNoMove, true, TAG_DONE); } - V_FreeBrokenLines (lines); + V_FreeBrokenLines(lines); } break; + } case POP_Keys: // List the keys the player has. diff --git a/src/statistics.cpp b/src/statistics.cpp index acd8440e8..7ed6e7f8e 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -420,7 +420,7 @@ static void StoreLevelStats() LevelData[i].killcount = level.killed_monsters; LevelData[i].totalsecrets = level.total_secrets; LevelData[i].secretcount = level.found_secrets; - LevelData[i].leveltime = level.maptime; + LevelData[i].leveltime = AdjustTics(level.maptime); // Check for living monsters. On some maps it can happen // that the counter misses some. @@ -490,7 +490,7 @@ void STAT_ChangeLevel(const char *newl) } infostring.Format("%4d/%4d, %3d/%3d, %2d", statvals[0], statvals[1], statvals[2], statvals[3], validlevels); - FSessionStatistics *es = StatisticsEntry(sl, infostring, level.totaltime); + FSessionStatistics *es = StatisticsEntry(sl, infostring, AdjustTics(level.totaltime)); for(unsigned i = 0; i < LevelData.Size(); i++) { diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index e60fee303..2ab93f090 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1460,7 +1460,7 @@ void WI_drawDeathmatchStats () // Draw game time y += height + CleanYfac; - int seconds = plrs[me].stime / TICRATE; + int seconds = Tics2Seconds(plrs[me].stime); int hours = seconds / 3600; int minutes = (seconds % 3600) / 60; seconds = seconds % 60; @@ -1817,9 +1817,9 @@ void WI_updateStats () cnt_kills[0] = plrs[me].skills; cnt_items[0] = plrs[me].sitems; cnt_secret[0] = plrs[me].ssecret; - cnt_time = plrs[me].stime / TICRATE; + cnt_time = Tics2Seconds(plrs[me].stime); cnt_par = wbs->partime / TICRATE; - cnt_total_time = wbs->totaltime / TICRATE; + cnt_total_time = Tics2Seconds(wbs->totaltime); } if (sp_state == 2) @@ -1882,19 +1882,21 @@ void WI_updateStats () cnt_total_time += 3; } - if (!gameinfo.intermissioncounter || cnt_time >= plrs[me].stime / TICRATE) - cnt_time = plrs[me].stime / TICRATE; + int sec = Tics2Seconds(plrs[me].stime); + if (!gameinfo.intermissioncounter || cnt_time >= sec) + cnt_time = sec; - if (!gameinfo.intermissioncounter || cnt_total_time >= wbs->totaltime / TICRATE) - cnt_total_time = wbs->totaltime / TICRATE; + int tsec = Tics2Seconds(wbs->totaltime); + if (!gameinfo.intermissioncounter || cnt_total_time >= tsec) + cnt_total_time = tsec; if (!gameinfo.intermissioncounter || cnt_par >= wbs->partime / TICRATE) { cnt_par = wbs->partime / TICRATE; - if (cnt_time >= plrs[me].stime / TICRATE) + if (cnt_time >= sec) { - cnt_total_time = wbs->totaltime / TICRATE; + cnt_total_time = tsec; S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); sp_state++; } From f0eccb9d1541ea1fefcc82cdd02a0b7b2dc73fbe Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Aug 2014 10:08:49 +0200 Subject: [PATCH 021/313] - BlueShadow's submission for keeping the max save amount of BasicArmorPickups around when picking up armor bonuses. --- src/g_shared/a_armor.cpp | 8 ++++++++ src/g_shared/a_pickups.h | 1 + src/p_acs.cpp | 4 ++++ src/version.h | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index 6b90a6a78..a745197c2 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -25,6 +25,11 @@ void ABasicArmor::Serialize (FArchive &arc) { Super::Serialize (arc); arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType; + + if (SaveVersion >= 4511) + { + arc << ActualSaveAmount; + } } //=========================================================================== @@ -69,6 +74,7 @@ AInventory *ABasicArmor::CreateCopy (AActor *other) copy->Icon = Icon; copy->BonusCount = BonusCount; copy->ArmorType = ArmorType; + copy->ActualSaveAmount = ActualSaveAmount; GoAwayAndDie (); return copy; } @@ -268,6 +274,7 @@ bool ABasicArmorPickup::Use (bool pickup) armor->MaxAbsorb = MaxAbsorb; armor->MaxFullAbsorb = MaxFullAbsorb; armor->ArmorType = this->GetClass()->TypeName; + armor->ActualSaveAmount = SaveAmount; return true; } @@ -360,6 +367,7 @@ bool ABasicArmorBonus::Use (bool pickup) armor->MaxAbsorb = MaxAbsorb; armor->ArmorType = this->GetClass()->TypeName; armor->MaxFullAbsorb = MaxFullAbsorb; + armor->ActualSaveAmount = MaxSaveAmount; } armor->Amount = MIN(armor->Amount + saveAmount, MaxSaveAmount + armor->BonusCount); diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 22d1e009f..72548776a 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -423,6 +423,7 @@ public: int MaxFullAbsorb; int BonusCount; FNameNoInit ArmorType; + int ActualSaveAmount; }; // BasicArmorPickup replaces the armor you have. diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 1646ab7b7..e0967d400 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -128,6 +128,7 @@ enum ARMORINFO_SAVEPERCENT, ARMORINFO_MAXABSORB, ARMORINFO_MAXFULLABSORB, + ARMORINFO_ACTUALSAVEAMOUNT, }; struct CallReturn @@ -4858,6 +4859,9 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const case ARMORINFO_MAXFULLABSORB: return equippedarmor->MaxFullAbsorb; + case ARMORINFO_ACTUALSAVEAMOUNT: + return equippedarmor->ActualSaveAmount; + default: return 0; } diff --git a/src/version.h b/src/version.h index 6d24c3fe8..af376938c 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 4510 +#define SAVEVER 4511 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 48163de8e2485116e3007cdb0245bc975e8eed80 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Aug 2014 11:47:14 +0200 Subject: [PATCH 022/313] must include doomdef.h in cmdlib.h to get TICRATE. --- src/cmdlib.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmdlib.h b/src/cmdlib.h index 66747415a..f6f0e6ad8 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -5,6 +5,7 @@ #include "doomtype.h" +#include "doomdef.h" #include #include From f482dc094df50a6bf4ae5608dbb564a18bc864fd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Aug 2014 12:27:04 +0200 Subject: [PATCH 023/313] fixed: R_PointOnSideSlow had precision issues with very short lines. When this function was originally written there was no possibility of fractional vertex coordinates so it threw away the fractional parts of the node's directional vector (which in the original nodes was always 0.) Now, with UDMF and high precision vertices this no longer works and the loss of significant parts of their value caused this code to produce erroneous results if the linedefs were only a few map units long and using fractional positions. --- src/p_maputl.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 17fc97d2f..3a4a4c6a0 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -434,8 +434,8 @@ static int R_PointOnSideSlow (fixed_t x, fixed_t y, node_t *node) // add on a 386/486, but it certainly isn't on anything newer than that. fixed_t dx; fixed_t dy; - fixed_t left; - fixed_t right; + double left; + double right; if (!node->dx) { @@ -466,8 +466,9 @@ static int R_PointOnSideSlow (fixed_t x, fixed_t y, node_t *node) return 0; } - left = FixedMul ( node->dy>>FRACBITS , dx ); - right = FixedMul ( dy , node->dx>>FRACBITS ); + // we must use doubles here because the fixed point code will produce errors due to loss of precision for extremely short linedefs. + left = (double)node->dy * (double)dx; + right = (double)dy * (double)node->dx; if (right < left) { From b461000795f3c52beed13f90498c2cf80d1a839e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 13 Aug 2014 22:46:08 +0300 Subject: [PATCH 024/313] Fixed crash on opening 7z/LZMA archives, x64 only With Position Independent Code (PIC) enabled on x64, non-MSVC compilers use wrong inline assembly for cpuid --- lzma/C/CpuArch.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lzma/C/CpuArch.c b/lzma/C/CpuArch.c index 425d18923..d6ab3f7f8 100644 --- a/lzma/C/CpuArch.c +++ b/lzma/C/CpuArch.c @@ -70,9 +70,9 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) *c = c2; *d = d2; - #elif __PIC__ + #elif defined __PIC__ && defined __i386__ - /* GCC or Clang WITH position-independent code generation */ + /* GCC or Clang WITH position-independent code generation, i386 only */ __asm__ __volatile__ ( "xchgl %%ebx, %1\n" @@ -86,7 +86,7 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) #else - /* GCC or Clang WITHOUT position-independent code generation */ + /* GCC or Clang WITHOUT position-independent code generation, or x86_64 */ __asm__ __volatile__ ( "cpuid" From 9479a89b667af93028e1ea7690431a15f0f48b25 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 16 Aug 2014 13:55:05 +0300 Subject: [PATCH 025/313] Fixed crash on Release targets --- src/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a1a26867..2ed4d0f00 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -431,9 +431,10 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( PROFILE ) set( REL_CXX_FLAGS "-fno-rtti" ) - if( NOT PROFILE ) + if( NOT PROFILE AND NOT APPLE ) + # On OS X frame pointers are required for exception handling, at least with Clang set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" ) - endif( NOT PROFILE ) + endif( NOT PROFILE AND NOT APPLE ) set( CMAKE_CXX_FLAGS_RELEASE "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}" ) set( CMAKE_CXX_FLAGS_MINSIZEREL "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" ) From 07d7f690e8bb83f87e48d30f2842ec7b28d795db Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 21 Aug 2014 20:01:05 +1200 Subject: [PATCH 026/313] Non-wall sprites need their original depth checks --- src/r_things.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/r_things.cpp b/src/r_things.cpp index 092e799dc..c0475ee75 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2076,9 +2076,22 @@ void R_DrawSprite (vissprite_t *spr) r1 = MAX (ds->x1, x1); r2 = MIN (ds->x2, x2); + fixed_t neardepth, fardepth; + if (!spr->bWallSprite) + { + if (ds->sz1 < ds->sz2) + { + neardepth = ds->sz1, fardepth = ds->sz2; + } + else + { + neardepth = ds->sz2, fardepth = ds->sz1; + } + } // Check if sprite is in front of draw seg: - if (DMulScale32(spr->gy - ds->curline->v1->y, ds->curline->v2->x - ds->curline->v1->x, - ds->curline->v1->x - spr->gx, ds->curline->v2->y - ds->curline->v1->y) <= 0) + if ((!spr->bWallSprite && neardepth > spr->depth) || ((spr->bWallSprite || fardepth > spr->depth) && + DMulScale32(spr->gy - ds->curline->v1->y, ds->curline->v2->x - ds->curline->v1->x, + ds->curline->v1->x - spr->gx, ds->curline->v2->y - ds->curline->v1->y) <= 0)) { // seg is behind sprite, so draw the mid texture if it has one if (ds->maskedtexturecol != -1 || ds->bFogBoundary) From df0d3543a85076168fa73b26486ac9a105e69a7d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 21 Aug 2014 13:01:12 +0200 Subject: [PATCH 027/313] - fixed: *ALL* original ceiling crushers, not just type 49, require a distance of 8 to the floor for the destination height. For the silent types this required a new action special, Ceiling_CrushAndRaiseSilentDist. This change only affects the XLAT mapping, the Hexen format types behave as before. - removed the redundant internal ceilCrushAndRaiseDist ceiling movement type. It was precisely the same as ceilCrushAndRaise in all details. --- src/actionspecials.h | 1 + src/cmdlib.h | 5 +++-- src/p_ceiling.cpp | 8 ++------ src/p_lnspec.cpp | 10 ++++++++-- src/p_spec.h | 1 - wadsrc/static/xlat/base.txt | 22 +++++++++++----------- wadsrc/static/xlat/strife.txt | 10 +++++----- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/actionspecials.h b/src/actionspecials.h index 1377def82..752ef77eb 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -102,6 +102,7 @@ DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Up, 102, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Down, 103, -1, -1, 2) +DEFINE_SPECIAL(Ceiling_CrushAndRaiseSilentDist, 104, 3, 5, 5) DEFINE_SPECIAL(Light_ForceLightning, 109, 1, 1, 1) DEFINE_SPECIAL(Light_RaiseByValue, 110, 2, 2, 2) diff --git a/src/cmdlib.h b/src/cmdlib.h index f6f0e6ad8..6df893580 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -6,6 +6,7 @@ #include "doomtype.h" #include "doomdef.h" +#include "m_fixed.h" #include #include @@ -75,12 +76,12 @@ void ScanDirectory(TArray &list, const char *dirpath); inline int AdjustTics(int tics) { - return (tics * 98) / 100; + return Scale(tics, 98, 100); } inline int Tics2Seconds(int tics) { - return (tics * 98) / (100 * TICRATE); + return Scale(tics, 98, (100 * TICRATE)); } diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 426bdf507..d0c54761a 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -133,7 +133,6 @@ void DCeiling::Tick () switch (m_Type) { case ceilCrushAndRaise: - case ceilCrushAndRaiseDist: m_Direction = -1; m_Speed = m_Speed1; if (!SN_IsMakingLoopingSound (m_Sector)) @@ -165,7 +164,6 @@ void DCeiling::Tick () switch (m_Type) { case ceilCrushAndRaise: - case ceilCrushAndRaiseDist: case ceilCrushRaiseAndStay: m_Speed = m_Speed2; m_Direction = 1; @@ -195,7 +193,6 @@ void DCeiling::Tick () switch (m_Type) { case ceilCrushAndRaise: - case ceilCrushAndRaiseDist: case ceilLowerAndCrush: case ceilLowerAndCrushDist: if (m_Speed1 == FRACUNIT && m_Speed2 == FRACUNIT) @@ -257,7 +254,6 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, switch (type) { case ceilCrushAndRaise: - case ceilCrushAndRaiseDist: case ceilCrushRaiseAndStay: ceiling->m_TopHeight = sec->ceilingplane.d; case ceilLowerAndCrush: @@ -267,7 +263,7 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, { targheight += 8*FRACUNIT; } - else if (type == ceilLowerAndCrushDist || type == ceilCrushAndRaiseDist) + else if (type == ceilCrushAndRaise) { targheight += height; } @@ -509,7 +505,7 @@ bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, // Reactivate in-stasis ceilings...for certain types. // This restarts a crusher after it has been stopped - if (type == DCeiling::ceilCrushAndRaise || type == DCeiling::ceilCrushAndRaiseDist) + if (type == DCeiling::ceilCrushAndRaise) { P_ActivateInStasisCeiling (tag); } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 7cb5ca7e6..14bcd430c 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -641,7 +641,7 @@ FUNC(LS_Ceiling_CrushAndRaiseA) FUNC(LS_Ceiling_CrushAndRaiseDist) // Ceiling_CrushAndRaiseDist (tag, dist, speed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaiseDist, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4)); } FUNC(LS_Ceiling_CrushAndRaiseSilentA) @@ -650,6 +650,12 @@ FUNC(LS_Ceiling_CrushAndRaiseSilentA) return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4)); } +FUNC(LS_Ceiling_CrushAndRaiseSilentDist) +// Ceiling_CrushAndRaiseSilentDist (tag, dist, upspeed, damage, crushtype) +{ + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4)); +} + FUNC(LS_Ceiling_RaiseToNearest) // Ceiling_RaiseToNearest (tag, speed) { @@ -3261,7 +3267,7 @@ lnSpecFunc LineSpecials[256] = /* 101 */ LS_NOP, // Scroll_Texture_Right /* 102 */ LS_NOP, // Scroll_Texture_Up /* 103 */ LS_NOP, // Scroll_Texture_Down - /* 104 */ LS_NOP, + /* 104 */ LS_Ceiling_CrushAndRaiseSilentDist, /* 105 */ LS_NOP, /* 106 */ LS_NOP, /* 107 */ LS_NOP, diff --git a/src/p_spec.h b/src/p_spec.h index dc0eb3646..c9bb6eded 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -628,7 +628,6 @@ public: ceilLowerInstant, ceilRaiseInstant, ceilCrushAndRaise, - ceilCrushAndRaiseDist, ceilLowerAndCrush, ceilLowerAndCrushDist, ceilCrushRaiseAndStay, diff --git a/wadsrc/static/xlat/base.txt b/wadsrc/static/xlat/base.txt index 11b9fd315..856e7a505 100644 --- a/wadsrc/static/xlat/base.txt +++ b/wadsrc/static/xlat/base.txt @@ -5,7 +5,7 @@ include "xlat/defines.i" 3 = WALK, Door_Close (tag, D_SLOW) 4 = WALK|MONST, Door_Raise (tag, D_SLOW, VDOORWAIT) 5 = WALK, Floor_RaiseToLowestCeiling (tag, F_SLOW) - 6 = WALK, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) + 6 = WALK, Ceiling_CrushAndRaiseDist (tag, 8, C_NORMAL, 10) 7 = USE, Stairs_BuildUpDoom (tag, ST_SLOW, 8) 8 = WALK, Stairs_BuildUpDoom (tag, ST_SLOW, 8) 9 = USE, Floor_Donut (tag, DORATE, DORATE) @@ -24,7 +24,7 @@ include "xlat/defines.i" 22 = WALK, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) 23 = USE, Floor_LowerToLowest (tag, F_SLOW) 24 = SHOOT, Floor_RaiseToLowestCeiling (tag, F_SLOW) - 25 = WALK, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) + 25 = WALK, Ceiling_CrushAndRaiseDist (tag, C_SLOW, C_SLOW, 10) 26 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, BCard | CardIsSkull, tag) 27 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, YCard | CardIsSkull, tag) 28 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, RCard | CardIsSkull, tag) @@ -72,11 +72,11 @@ include "xlat/defines.i" 70 = USE|REP, Floor_LowerToHighest (tag, F_FAST, 136) 71 = USE, Floor_LowerToHighest (tag, F_FAST, 136) 72 = WALK|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) - 73 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) + 73 = WALK|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_SLOW, 10) 74 = WALK|REP, Ceiling_CrushStop (tag) 75 = WALK|REP, Door_Close (tag, D_SLOW) 76 = WALK|REP, Door_CloseWaitOpen (tag, D_SLOW, 240) - 77 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) + 77 = WALK|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_NORMAL, 10) 78 = USE|REP, Floor_TransferNumeric (tag) // <- BOOM special 79 = WALK|REP, Light_ChangeToValue (tag, 35) 80 = WALK|REP, Light_MaxNeighbor (tag) @@ -140,7 +140,7 @@ include "xlat/defines.i" 138 = USE|REP, Light_ChangeToValue (tag, 255) 139 = USE|REP, Light_ChangeToValue (tag, 35) 140 = USE, Floor_RaiseByValueTimes8 (tag, F_SLOW, 64) -141 = WALK, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) +141 = WALK, Ceiling_CrushAndRaiseSilentDist (tag, 8, C_SLOW, 10) /****** The following are all new to BOOM ******/ @@ -152,7 +152,7 @@ include "xlat/defines.i" 147 = WALK|REP, Floor_RaiseByValueTimes8 (tag, F_SLOW, 64) 148 = WALK|REP, Plat_UpByValueStayTx (tag, P_SLOW/2, 3) 149 = WALK|REP, Plat_UpByValueStayTx (tag, P_SLOW/2, 4) -150 = WALK|REP, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) +150 = WALK|REP, Ceiling_CrushAndRaiseSilentDist (tag, 8, C_SLOW, 10) 151 = WALK|REP, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW) 152 = WALK|REP, Ceiling_LowerToFloor (tag, C_SLOW) 153 = WALK, Floor_TransferTrigger (tag) @@ -166,8 +166,8 @@ include "xlat/defines.i" 161 = USE, Floor_RaiseByValue (tag, F_SLOW, 24) 162 = USE, Plat_PerpetualRaiseLip (tag, P_SLOW, PLATWAIT, 0) 163 = USE, Plat_Stop (tag) -164 = USE, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) -165 = USE, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) +164 = USE, Ceiling_CrushAndRaiseDist (tag, 8, C_NORMAL, 10) +165 = USE, Ceiling_CrushAndRaiseSilentDist (tag, 8, C_SLOW, 10) 166 = USE, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW, 1998) 167 = USE, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 168 = USE, Ceiling_CrushStop (tag) @@ -185,9 +185,9 @@ include "xlat/defines.i" 180 = USE|REP, Floor_RaiseByValue (tag, F_SLOW, 24) 181 = USE|REP, Plat_PerpetualRaiseLip (tag, P_SLOW, PLATWAIT, 0) 182 = USE|REP, Plat_Stop (tag) -183 = USE|REP, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) -184 = USE|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) -185 = USE|REP, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) +183 = USE|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_NORMAL, 10) +184 = USE|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_SLOW, 10) +185 = USE|REP, Ceiling_CrushAndRaiseSilentDist (tag, 8, C_SLOW, 10) 186 = USE|REP, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW, 1998) 187 = USE|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 188 = USE|REP, Ceiling_CrushStop (tag) diff --git a/wadsrc/static/xlat/strife.txt b/wadsrc/static/xlat/strife.txt index 159933c80..755ace5ad 100644 --- a/wadsrc/static/xlat/strife.txt +++ b/wadsrc/static/xlat/strife.txt @@ -90,7 +90,7 @@ RetailOnly = 121 3 = WALK, Door_Close (tag, D_SLOW) 4 = WALK|MONST, Door_Raise (tag, D_SLOW, VDOORWAIT) 5 = WALK, Floor_RaiseToLowestCeiling (tag, F_SLOW) - 6 = WALK, Ceiling_CrushAndRaiseA (tag, C_FAST, C_FAST, 10) + 6 = WALK, Ceiling_CrushAndRaiseDist (tag, 8, C_FAST, 10) 8 = WALK, Stairs_BuildUpDoom (tag, ST_SLOW, 8) 10 = WALK|MONST, Plat_DownWaitUpStayLip (tag, P_FAST, PLATWAIT, 0) 12 = WALK, Light_MaxNeighbor (tag) @@ -99,7 +99,7 @@ RetailOnly = 121 17 = WALK, Light_StrobeDoom (tag, 5, 35) 19 = WALK, Floor_LowerToHighest (tag, F_SLOW, 128) 22 = WALK, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) - 25 = WALK, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 0) + 25 = WALK, Ceiling_CrushAndRaiseDist (tag, 8, C_SLOW, 0) 30 = WALK, Floor_RaiseByTexture (tag, F_SLOW) 35 = WALK, Light_ChangeToValue (tag, 35) 36 = WALK, Floor_LowerToHighest (tag, F_FAST, 128) @@ -128,7 +128,7 @@ RetailOnly = 121 124 = WALK, Teleport_EndGame () 125 = MONWALK, Teleport (0, tag) 130 = WALK, Floor_RaiseToNearest (tag, F_FAST) -141 = WALK, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 0) +141 = WALK, Ceiling_CrushAndRaiseSilentDist (tag, 8, C_SLOW, 10) 174 = WALK, ACS_ExecuteAlways (0, 0, 174, tag) 183 = WALK, ACS_ExecuteAlways (0, 0, 183, tag) 178 = WALK, Generic_Stairs (tag, ST_FAST, 16, 0, 0) @@ -146,11 +146,11 @@ RetailOnly = 121 216 = WALK|REP, ACS_ExecuteAlways (0, 0, 216, tag) 90 = WALK|REP, Door_Raise (tag, D_SLOW, VDOORWAIT) 72 = WALK|REP, Ceiling_LowerAndCrushDist (tag, C_SLOW, 10) - 73 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 0) + 73 = WALK|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_SLOW, 0) 74 = WALK|REP, Ceiling_CrushStop (tag) 75 = WALK|REP, Door_Close (tag, D_SLOW) 76 = WALK|REP, Door_CloseWaitOpen (tag, D_SLOW, 240) - 77 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_FAST, C_FAST, 10) + 77 = WALK|REP, Ceiling_CrushAndRaiseDist (tag, 8, C_FAST, 10) 79 = WALK|REP, Light_ChangeToValue (tag, 35) 80 = WALK|REP, Light_MaxNeighbor (tag) 81 = WALK|REP, Light_ChangeToValue (tag, 255) From eebd5c9d4da705d6fed48cafd21b38a90c54ac7b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 21 Aug 2014 13:38:47 +0200 Subject: [PATCH 028/313] - fixed bad edit in linedef translation. --- wadsrc/static/xlat/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/xlat/base.txt b/wadsrc/static/xlat/base.txt index 856e7a505..9607841d5 100644 --- a/wadsrc/static/xlat/base.txt +++ b/wadsrc/static/xlat/base.txt @@ -24,7 +24,7 @@ include "xlat/defines.i" 22 = WALK, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) 23 = USE, Floor_LowerToLowest (tag, F_SLOW) 24 = SHOOT, Floor_RaiseToLowestCeiling (tag, F_SLOW) - 25 = WALK, Ceiling_CrushAndRaiseDist (tag, C_SLOW, C_SLOW, 10) + 25 = WALK, Ceiling_CrushAndRaiseDist (tag, 8, C_SLOW, 10) 26 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, BCard | CardIsSkull, tag) 27 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, YCard | CardIsSkull, tag) 28 = USE|REP, Door_LockedRaise (0, D_SLOW, VDOORWAIT, RCard | CardIsSkull, tag) From 5364116354303317922427a8f12764b0846c49ca Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Aug 2014 13:24:15 +0200 Subject: [PATCH 029/313] - fixed: APROP_Friendly did not manage monster counting correctly. --- src/p_acs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e0967d400..235a47933 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3747,16 +3747,16 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) break; case APROP_Friendly: + if (actor->CountsAsKill()) level.total_monsters--; if (value) { - if (actor->CountsAsKill()) level.total_monsters--; actor->flags |= MF_FRIENDLY; } else { actor->flags &= ~MF_FRIENDLY; - if (actor->CountsAsKill()) level.total_monsters++; } + if (actor->CountsAsKill()) level.total_monsters++; break; From 51d7340288e477d8f44af7992ec2fee439105f6e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 23 Aug 2014 16:35:05 +0300 Subject: [PATCH 030/313] Fixed crash on music volume change when no track is played using FluidSynth device --- src/sound/music_midistream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index ba0077903..15f4caec8 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -540,7 +540,7 @@ bool MIDIStreamer::IsPlaying() void MIDIStreamer::MusicVolumeChanged() { - if (MIDI->FakeVolume()) + if (MIDI != NULL && MIDI->FakeVolume()) { float realvolume = clamp(snd_musicvolume * relative_volume, 0.f, 1.f); Volume = clamp((DWORD)(realvolume * 65535.f), 0, 65535); @@ -622,7 +622,7 @@ void MIDIStreamer::FluidSettingStr(const char *setting, const char *value) void MIDIStreamer::OutputVolume (DWORD volume) { - if (MIDI->FakeVolume()) + if (MIDI != NULL && MIDI->FakeVolume()) { NewVolume = volume; VolumeChanged = true; From 1d2aa3df0c6881c74e1ef350b8c9c19c5fc561c4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 25 Aug 2014 10:51:50 +0200 Subject: [PATCH 031/313] fixed: The wait console command waited one tic too many because it got 1 added to it twice instead of only once. --- src/c_dispatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 105d8c0d3..65e017bdc 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -723,7 +723,7 @@ void AddCommandString (char *cmd, int keynum) // Note that deferred commands lose track of which key // (if any) they were pressed from. *brkpt = ';'; - new DWaitingCommand (brkpt, tics+1); + new DWaitingCommand (brkpt, tics); } return; } From 8f238f8d3269b579a2d46efbffd4f03095643dd2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 27 Aug 2014 11:06:12 +0200 Subject: [PATCH 032/313] - fixed: the Revenant's frame duration in its missile state were wrong. --- wadsrc/static/actors/doom/revenant.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wadsrc/static/actors/doom/revenant.txt b/wadsrc/static/actors/doom/revenant.txt index 5f0a78691..5ef97789b 100644 --- a/wadsrc/static/actors/doom/revenant.txt +++ b/wadsrc/static/actors/doom/revenant.txt @@ -39,8 +39,8 @@ ACTOR Revenant 66 SKEL I 6 A_SkelFist Goto See Missile: - SKEL J 1 BRIGHT A_FaceTarget - SKEL J 9 BRIGHT A_FaceTarget + SKEL J 0 BRIGHT A_FaceTarget + SKEL J 10 BRIGHT A_FaceTarget SKEL K 10 A_SkelMissile SKEL K 10 A_FaceTarget Goto See From 49382a2a146544e95f4278fb824b3531aae1202c Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 4 Sep 2014 19:34:41 -0400 Subject: [PATCH 033/313] - Added detection for The Adventures of Sqaure (based off MTrop's submission). - IWADINFO no longer requires a mapinfo to be specified. --- src/g_level.h | 2 +- src/g_mapinfo.cpp | 6 +++--- wadsrc/static/iwadinfo.txt | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/g_level.h b/src/g_level.h index 0cd366798..05290f48b 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -525,7 +525,7 @@ level_info_t *CheckLevelRedirect (level_info_t *info); FString CalcMapName (int episode, int level); -void G_ParseMapInfo (const char *basemapinfo); +void G_ParseMapInfo (FString basemapinfo); void G_ClearSnapshots (void); void P_RemoveDefereds (); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index b7aa77e1c..5a451dfaa 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1886,7 +1886,7 @@ static void ClearMapinfo() // //========================================================================== -void G_ParseMapInfo (const char *basemapinfo) +void G_ParseMapInfo (FString basemapinfo) { int lump, lastlump = 0; level_info_t gamedefaults; @@ -1895,7 +1895,7 @@ void G_ParseMapInfo (const char *basemapinfo) atterm(ClearMapinfo); // Parse the default MAPINFO for the current game. This lump *MUST* come from zdoom.pk3. - if (basemapinfo != NULL) + if (basemapinfo.IsNotEmpty()) { FMapInfoParser parse; level_info_t defaultinfo; @@ -1903,7 +1903,7 @@ void G_ParseMapInfo (const char *basemapinfo) if (Wads.GetLumpFile(baselump) > 0) { I_FatalError("File %s is overriding core lump %s.", - Wads.GetWadFullName(Wads.GetLumpFile(baselump)), basemapinfo); + Wads.GetWadFullName(Wads.GetLumpFile(baselump)), basemapinfo.GetChars()); } parse.ParseMapInfo(baselump, gamedefaults, defaultinfo); } diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index a2f8ef6c0..088e74466 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -1,5 +1,23 @@ // Must be sorted in identification order (easiest to recognize first!) +IWad +{ + Name = "The Adventures of Square" + Game = "Doom" + Config = "Square" + MustContain = "SQU-IWAD", "E1A1" + BannerColors = "ff ff ff", "80 00 80" +} + +IWad +{ + Name = "The Adventures of Square (Square-ware)" + Game = "Doom" + Config = "Square" + MustContain = "SQU-SWE1", "E1A1" + BannerColors = "ff ff ff", "80 00 80" +} + IWad { Name = "Harmony" @@ -361,4 +379,5 @@ Names "harm1.wad" "hacx.wad" "hacx2.wad" + "square1.pk3" } From 45a88780b0db0b2da5839bbd5078236db7fd498c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 6 Sep 2014 10:18:33 +0300 Subject: [PATCH 034/313] Fixed build with GCC 4.x / Xcode 3.2.x --- src/sdl/iwadpicker_cocoa.mm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 7a6b38623..92eb47e40 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -33,6 +33,9 @@ ** */ +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass + #include "cmdlib.h" #include "d_main.h" #include "version.h" @@ -40,6 +43,9 @@ #include "m_argv.h" #include "m_misc.h" #include "gameconfigfile.h" + +#undef Class + #include #include From e60d181e874bc1d04ef4f5a0ba2d19027987b08f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 6 Sep 2014 10:22:37 +0300 Subject: [PATCH 035/313] Removed redundant #include's that broke build of native back-end --- src/cocoa/i_backend_cocoa.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 3bb926a61..b8b2babeb 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -48,8 +48,6 @@ // Avoid collision between DObject class and Objective-C #define Class ObjectClass -#include "basictypes.h" -#include "basicinlines.h" #include "bitmap.h" #include "c_console.h" #include "c_dispatch.h" From cfd24f438f0d1f1bc84882d3e2bf9b072bb0881e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Sep 2014 13:02:05 +0200 Subject: [PATCH 036/313] - jpalomo's A_Saw flags submission. --- src/g_doom/a_doomweaps.cpp | 34 ++++++++++++++++++------------ wadsrc/static/actors/constants.txt | 2 ++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 368d7ce8b..424e691e7 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -107,6 +107,8 @@ enum SAW_Flags SF_RANDOMLIGHTHIT = 4, SF_NOUSEAMMOMISS = 8, SF_NOUSEAMMO = 16, + SF_NOPULLIN = 32, + SF_NOTURN = 64, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) @@ -187,23 +189,27 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM); // turn to face target - angle = R_PointToAngle2 (self->x, self->y, - linetarget->x, linetarget->y); - if (angle - self->angle > ANG180) + if (!(Flags & SF_NOTURN)) { - if (angle - self->angle < (angle_t)(-ANG90/20)) - self->angle = angle + ANG90/21; + angle = R_PointToAngle2(self->x, self->y, + linetarget->x, linetarget->y); + if (angle - self->angle > ANG180) + { + if (angle - self->angle < (angle_t)(-ANG90 / 20)) + self->angle = angle + ANG90 / 21; + else + self->angle -= ANG90 / 20; + } else - self->angle -= ANG90/20; + { + if (angle - self->angle > ANG90 / 20) + self->angle = angle - ANG90 / 21; + else + self->angle += ANG90 / 20; + } } - else - { - if (angle - self->angle > ANG90/20) - self->angle = angle - ANG90/21; - else - self->angle += ANG90/20; - } - self->flags |= MF_JUSTATTACKED; + if (!(Flags & SF_NOPULLIN)) + self->flags |= MF_JUSTATTACKED; } // diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 0332d09a3..d5358e5af 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -14,6 +14,8 @@ const int SF_RANDOMLIGHTHIT = 4; const int SF_RANDOMLIGHTBOTH = 6; const int SF_NOUSEAMMOMISS = 8; const int SF_NOUSEAMMO = 16; +const int SF_NOPULLIN = 32; +const int SF_NOTURN = 64; // Flags for A_CustomMissile const int CMF_AIMOFFSET = 1; From 580e580c429885a34d2770fd8a10426510e0eef1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Sep 2014 22:54:56 +0200 Subject: [PATCH 037/313] - added option to set a sector's tag via compatibility.txt (needed by GZDoom) --- src/compatibility.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 7339a0f19..c2153fbd7 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -81,6 +81,7 @@ enum CP_SECTORFLOOROFFSET, CP_SETWALLYSCALE, CP_SETTHINGZ, + CP_SETTAG, }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -307,6 +308,15 @@ void ParseCompatibility() sc.MustGetFloat(); CompatParams.Push(FLOAT2FIXED(sc.Float)); } + else if (sc.Compare("setsectortag")) + { + if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size(); + CompatParams.Push(CP_SETTAG); + sc.MustGetNumber(); + CompatParams.Push(sc.Number); + sc.MustGetNumber(); + CompatParams.Push(sc.Number); + } else { sc.UnGet(); @@ -520,6 +530,14 @@ void SetCompatibilityParams() i += 3; break; } + case CP_SETTAG: + { + if ((unsigned)CompatParams[i + 1] < (unsigned)numsectors) + { + sectors[CompatParams[i + 1]].tag = CompatParams[i + 2]; + } + break; + } } } } From 5e34b78451869558b38e8f08fba8102da40d2a0a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Sep 2014 23:25:27 +0200 Subject: [PATCH 038/313] - missed a line. --- src/compatibility.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index c2153fbd7..351c2672a 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -536,6 +536,7 @@ void SetCompatibilityParams() { sectors[CompatParams[i + 1]].tag = CompatParams[i + 2]; } + i += 3; break; } } From 0223b7f4601e0018c8479394108ee0f86dae8244 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 12 Sep 2014 00:39:36 -0400 Subject: [PATCH 039/313] - Fixed: InInventory didn't work quite right with multiple items. --- src/g_shared/sbarinfo_commands.cpp | 43 ++++++------------------------ 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index c9011e851..26dfa0d96 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3383,43 +3383,16 @@ class CommandInInventory : public SBarInfoCommandFlowControl AInventory *invItem[2] = { statusBar->CPlayer->mo->FindInventory(item[0]), statusBar->CPlayer->mo->FindInventory(item[1]) }; if (invItem[0] != NULL && amount[0] > 0 && invItem[0]->Amount < amount[0]) invItem[0] = NULL; if (invItem[1] != NULL && amount[1] > 0 && invItem[1]->Amount < amount[1]) invItem[1] = NULL; - if(invItem[1] != NULL && conditionAnd) + + if (item[1]) { - if((invItem[0] != NULL && invItem[1] != NULL) && !negate) - { - SetTruth(true, block, statusBar); - return; - } - else if((invItem[0] == NULL || invItem[1] == NULL) && negate) - { - SetTruth(true, block, statusBar); - return; - } + if (conditionAnd) + SetTruth((invItem[0] && invItem[1]) != negate, block, statusBar); + else + SetTruth((invItem[0] || invItem[1]) != negate, block, statusBar); } - else if(invItem[1] != NULL && !conditionAnd) - { - if((invItem[0] != NULL || invItem[1] != NULL) && !negate) - { - SetTruth(true, block, statusBar); - return; - } - else if((invItem[0] == NULL && invItem[1] == NULL) && negate) - { - SetTruth(true, block, statusBar); - return; - } - } - else if((invItem[0] != NULL) && !negate) - { - SetTruth(true, block, statusBar); - return; - } - else if((invItem[0] == NULL) && negate) - { - SetTruth(true, block, statusBar); - return; - } - SetTruth(false, block, statusBar); + else + SetTruth((invItem[0] != NULL) != negate, block, statusBar); } protected: bool conditionAnd; From 27ebfa783e99559ce855a6c1feeb83ff9446d5ac Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 12 Sep 2014 00:49:09 -0400 Subject: [PATCH 040/313] - Added IfHealth based on Blue Shadow's patch. --- src/g_shared/sbarinfo_commands.cpp | 45 ++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 26dfa0d96..ce94b2bc2 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3431,6 +3431,46 @@ class CommandAlpha : public SBarInfoMainBlock //////////////////////////////////////////////////////////////////////////////// +class CommandIfHealth : public SBarInfoCommandFlowControl +{ + public: + CommandIfHealth(SBarInfo *script) : SBarInfoCommandFlowControl(script), + negate(false), percentage(false) + { + } + + void Parse(FScanner &sc, bool fullScreenOffsets) + { + if (sc.CheckToken(TK_Identifier)) + { + if (sc.Compare("not")) + negate = true; + else + sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String); + } + + sc.MustGetToken(TK_IntConst); + percentage = sc.CheckToken('%'); + hpamount = sc.Number; + + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + + int phealth = percentage ? statusBar->CPlayer->mo->health * 100 / statusBar->CPlayer->mo->GetMaxHealth() : statusBar->CPlayer->mo->health; + + SetTruth((phealth >= hpamount) ^ negate, block, statusBar); + } + protected: + bool negate; + bool percentage; + int hpamount; +}; + +//////////////////////////////////////////////////////////////////////////////// + static const char *SBarInfoCommandNames[] = { "drawimage", "drawnumber", "drawswitchableimage", @@ -3440,7 +3480,7 @@ static const char *SBarInfoCommandNames[] = "gamemode", "playerclass", "playertype", "aspectratio", "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", - "weaponammo", "ininventory", "alpha", + "weaponammo", "ininventory", "alpha", "ifhealth", NULL }; @@ -3453,7 +3493,7 @@ enum SBarInfoCommands SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_PLAYERTYPE, SBARINFO_ASPECTRATIO, SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, - SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, + SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH, }; SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) @@ -3486,6 +3526,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_WEAPONAMMO: return new CommandWeaponAmmo(script); case SBARINFO_ININVENTORY: return new CommandInInventory(script); case SBARINFO_ALPHA: return new CommandAlpha(script); + case SBARINFO_IFHEALTH: return new CommandIfHealth(script); } sc.ScriptError("Unknown command '%s'.\n", sc.String); From 0fd84bc8536cbaeb3cd72559c5873e7aa7539491 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 12 Sep 2014 18:35:45 +0300 Subject: [PATCH 041/313] Added ability to turn off hi-res backing surface rendering on Retina displays High resolution backing surface is enabled by default and controlled via vid_hidpi CVAR When it is set to false one pixel per point scale is used --- src/cocoa/i_backend_cocoa.mm | 160 ++++++++++++++++++++++++----------- src/cocoa/i_rbopts.h | 2 + 2 files changed, 111 insertions(+), 51 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index b8b2babeb..dfb357740 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -86,13 +86,15 @@ - (NSRect)convertRectToBacking:(NSRect)aRect; @end -#endif // NSAppKitVersionNumber10_7 +#endif // !NSAppKitVersionNumber10_7 // --------------------------------------------------------------------------- RenderBufferOptions rbOpts; +EXTERN_CVAR(Bool, vid_hidpi) + 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) @@ -585,7 +587,7 @@ NSSize GetRealContentViewSize(const NSWindow* const window) // 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 (IsHiDPISupported() && NSNormalWindowLevel == [window level]) + return (vid_hidpi && NSNormalWindowLevel == [window level]) ? [view convertSizeToBacking:frameSize] : frameSize; } @@ -840,10 +842,16 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) #endif { @private - FullscreenWindow* m_window; - - bool m_openGLInitialized; + FullscreenWindow* m_window; + int m_multisample; + + int m_width; + int m_height; + bool m_fullscreen; + bool m_hiDPI; + + bool m_openGLInitialized; } - (id)init; @@ -864,7 +872,8 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) - (int)multisample; - (void)setMultisample:(int)multisample; -- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height; +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI; +- (void)useHiDPI:(bool)hiDPI; - (void)processEvents:(NSTimer*)timer; @@ -883,9 +892,15 @@ static ApplicationDelegate* s_applicationDelegate; - (id)init { self = [super init]; - + + m_multisample = 0; + + m_width = -1; + m_height = -1; + m_fullscreen = false; + m_hiDPI = false; + m_openGLInitialized = false; - m_multisample = 0; return self; } @@ -1052,35 +1067,30 @@ static ApplicationDelegate* s_applicationDelegate; pixelFormat:pixelFormat]; [[glView openGLContext] makeCurrentContext]; - if (IsHiDPISupported()) - { - [glView setWantsBestResolutionOpenGLSurface:YES]; - } - [m_window setContentView:glView]; m_openGLInitialized = true; } -- (void)fullscreenWithWidth:(int)width height:(int)height +- (void)switchToFullscreen { NSScreen* screen = [m_window screen]; const NSRect screenFrame = [screen frame]; - const NSRect displayRect = IsHiDPISupported() + 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< float >(width ); - const float pixelScaleFactorY = displayHeight / static_cast< float >(height); + const float pixelScaleFactorX = displayWidth / static_cast(m_width ); + const float pixelScaleFactorY = displayHeight / static_cast(m_height); rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - rbOpts.width = width * rbOpts.pixelScale; - rbOpts.height = height * rbOpts.pixelScale; + rbOpts.width = m_width * rbOpts.pixelScale; + rbOpts.height = m_height * rbOpts.pixelScale; rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; @@ -1092,18 +1102,18 @@ static ApplicationDelegate* s_applicationDelegate; [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; } -- (void)windowedWithWidth:(int)width height:(int)height +- (void)switchToWindowed { rbOpts.pixelScale = 1.0f; - rbOpts.width = static_cast< float >(width ); - rbOpts.height = static_cast< float >(height); + rbOpts.width = static_cast(m_width ); + rbOpts.height = static_cast(m_height); rbOpts.shiftX = 0.0f; rbOpts.shiftY = 0.0f; - const NSSize windowPixelSize = NSMakeSize(width, height); - const NSSize windowSize = IsHiDPISupported() + const NSSize windowPixelSize = NSMakeSize(m_width, m_height); + const NSSize windowSize = vid_hidpi ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] : windowPixelSize; @@ -1114,19 +1124,40 @@ static ApplicationDelegate* s_applicationDelegate; [m_window center]; } -- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI { - [self initializeOpenGL]; - - if (fullscreen) + if (fullscreen == m_fullscreen + && width == m_width + && height == m_height + && hiDPI == m_hiDPI) { - [self fullscreenWithWidth:width height:height]; + return; + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; + + [self initializeOpenGL]; + + if (IsHiDPISupported()) + { + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:m_hiDPI]; + } + + if (m_fullscreen) + { + [self switchToFullscreen]; } else { - [self windowedWithWidth:width height:height]; + [self switchToWindowed]; } + rbOpts.dirty = true; + const NSSize viewSize = GetRealContentViewSize(m_window); glViewport(0, 0, viewSize.width, viewSize.height); @@ -1145,6 +1176,19 @@ static ApplicationDelegate* s_applicationDelegate; } } +- (void)useHiDPI:(bool)hiDPI +{ + if (!m_openGLInitialized) + { + return; + } + + [self changeVideoResolution:m_fullscreen + width:m_width + height:m_height + useHiDPI:hiDPI]; +} + - (void)processEvents:(NSTimer*)timer { @@ -1230,6 +1274,22 @@ static ApplicationDelegate* s_applicationDelegate; // --------------------------------------------------------------------------- +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (IsHiDPISupported()) + { + [s_applicationDelegate useHiDPI:self]; + } + else if (0 != self) + { + self = 0; + } +} + + +// --------------------------------------------------------------------------- + + void I_SetMainWindowVisible(bool visible) { [s_applicationDelegate setMainWindowVisible:visible]; @@ -1508,7 +1568,10 @@ static SDL_PixelFormat* GetPixelFormat() SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) { - [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & flags) width:width height:height]; + [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & flags) + width:width + height:height + useHiDPI:vid_hidpi]; static SDL_Surface result; @@ -1554,21 +1617,6 @@ void SDL_WM_SetCaption(const char* title, const char* icon) // Window title is set in SDL_SetVideoMode() } -static void ResetSoftwareViewport() -{ - // For an unknown reason the following call to glClear() is needed - // to avoid drawing of garbage in fullscreen mode - // when game video resolution's aspect ratio is different from display one - - GLint viewport[2]; - glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport); - - glViewport(0, 0, viewport[0], viewport[1]); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); -} - int SDL_WM_ToggleFullScreen(SDL_Surface* surface) { if (surface->flags & SDL_FULLSCREEN) @@ -1582,8 +1630,8 @@ int SDL_WM_ToggleFullScreen(SDL_Surface* surface) [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & surface->flags) width:surface->w - height:surface->h]; - ResetSoftwareViewport(); + height:surface->h + useHiDPI:vid_hidpi]; return 1; } @@ -1638,8 +1686,6 @@ static void SetupSoftwareRendering(SDL_Surface* screen) glLoadIdentity(); glOrtho(0.0, screen->w, screen->h, 0.0, -1.0, 1.0); - ResetSoftwareViewport(); - glEnable(GL_TEXTURE_2D); glGenTextures(1, &s_frameBufferTexture); @@ -1660,12 +1706,24 @@ int SDL_Flip(SDL_Surface* screen) { SetupSoftwareRendering(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; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, - width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, screen->pixels); + width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, screen->pixels); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/src/cocoa/i_rbopts.h b/src/cocoa/i_rbopts.h index c1a9d9d4d..40a9ff17a 100644 --- a/src/cocoa/i_rbopts.h +++ b/src/cocoa/i_rbopts.h @@ -43,6 +43,8 @@ struct RenderBufferOptions float width; float height; + + bool dirty; }; extern RenderBufferOptions rbOpts; From a2c81f1ca9f067c3ac557b5fab079e62f07f3fb0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 12 Sep 2014 20:59:23 +0200 Subject: [PATCH 042/313] -fixed the file validity checks in MapData::GetChecksum. They need to be done per lump, because MapData::Seek can alter the FileReader being used for a specific lump. Even worse, the FileReader will be NULL when the function as it was is called for a map inside a Zip-file. --- src/p_setup.cpp | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index a6dbca868..6b5d9403f 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -537,34 +537,31 @@ void MapData::GetChecksum(BYTE cksum[16]) { MD5Context md5; - if (file != NULL) + if (isText) { - if (isText) + Seek(ML_TEXTMAP); + if (file != NULL) md5.Update(file, Size(ML_TEXTMAP)); + } + else + { + if (Size(ML_LABEL) != 0) { - Seek(ML_TEXTMAP); - md5.Update(file, Size(ML_TEXTMAP)); - } - else - { - if (Size(ML_LABEL) != 0) - { - Seek(ML_LABEL); - md5.Update(file, Size(ML_LABEL)); - } - Seek(ML_THINGS); - md5.Update(file, Size(ML_THINGS)); - Seek(ML_LINEDEFS); - md5.Update(file, Size(ML_LINEDEFS)); - Seek(ML_SIDEDEFS); - md5.Update(file, Size(ML_SIDEDEFS)); - Seek(ML_SECTORS); - md5.Update(file, Size(ML_SECTORS)); - } - if (HasBehavior) - { - Seek(ML_BEHAVIOR); - md5.Update(file, Size(ML_BEHAVIOR)); + Seek(ML_LABEL); + if (file != NULL) md5.Update(file, Size(ML_LABEL)); } + Seek(ML_THINGS); + if (file != NULL) md5.Update(file, Size(ML_THINGS)); + Seek(ML_LINEDEFS); + if (file != NULL) md5.Update(file, Size(ML_LINEDEFS)); + Seek(ML_SIDEDEFS); + if (file != NULL) md5.Update(file, Size(ML_SIDEDEFS)); + Seek(ML_SECTORS); + if (file != NULL) md5.Update(file, Size(ML_SECTORS)); + } + if (HasBehavior) + { + Seek(ML_BEHAVIOR); + if (file != NULL) md5.Update(file, Size(ML_BEHAVIOR)); } md5.Final(cksum); } From a0f507d18f5d04695b66bc35e3412bcf8a82e25e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 09:51:49 +0200 Subject: [PATCH 043/313] - fixed: The crosshair setting code checked for existence of lumps, not textures and missed anything defined in a TEXTURES lump. --- src/g_shared/shared_sbar.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 89921e3a2..2a18801a1 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -146,7 +146,6 @@ void ST_LoadCrosshair(bool alwaysload) { int num = 0; char name[16], size; - int lump; if (!crosshairforce && players[consoleplayer].camera != NULL && @@ -179,18 +178,20 @@ void ST_LoadCrosshair(bool alwaysload) num = -num; } size = (SCREENWIDTH < 640) ? 'S' : 'B'; + mysnprintf (name, countof(name), "XHAIR%c%d", size, num); - if ((lump = Wads.CheckNumForName (name, ns_graphics)) == -1) + FTextureID texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly); + if (!texid.isValid()) { mysnprintf (name, countof(name), "XHAIR%c1", size); - if ((lump = Wads.CheckNumForName (name, ns_graphics)) == -1) + texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly); + if (!texid.isValid()) { - strcpy (name, "XHAIRS1"); + texid = TexMan.CheckForTexture("XHAIRS1", FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly); } - num = 1; } CrosshairNum = num; - CrosshairImage = TexMan[TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch)]; + CrosshairImage = TexMan[texid]; } //--------------------------------------------------------------------------- From 1e82d72349fa6df6f5a07ee39779c3b361962b3a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 09:56:21 +0200 Subject: [PATCH 044/313] - fixed: G_FinishTravel must clear the MF2_BLASTED flag off the player pawn because this flag will result in damage from contact with other objects in the map. --- src/g_level.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_level.cpp b/src/g_level.cpp index 69bee5de1..9437ced70 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1169,6 +1169,7 @@ void G_FinishTravel () pawn->lastenemy = NULL; pawn->player->mo = pawn; pawn->player->camera = pawn; + pawn->flags2 &= ~MF2_BLASTED; DObject::StaticPointerSubstitution (oldpawn, pawn); oldpawn->Destroy(); pawndup->Destroy (); From 46ec364443d681fedfdccf89f7cbe8db967f3a91 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 10:08:22 +0200 Subject: [PATCH 045/313] - fixed: When validating the crouch sprite it was assumed that both the regular and the crouch sprite were having full rotations. --- src/p_user.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/p_user.cpp b/src/p_user.cpp index cd111838d..fbe24cc83 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -629,7 +629,16 @@ void APlayerPawn::BeginPlay () FString crouchspritename = sprites[crouchsprite].name; int spritenorm = Wads.CheckNumForName(normspritename + "A1", ns_sprites); + if (spritenorm==-1) + { + spritenorm = Wads.CheckNumForName(normspritename + "A0", ns_sprites); + } + int spritecrouch = Wads.CheckNumForName(crouchspritename + "A1", ns_sprites); + if (spritecrouch==-1) + { + spritecrouch = Wads.CheckNumForName(crouchspritename + "A0", ns_sprites); + } if (spritenorm==-1 || spritecrouch ==-1) { From 2be3b776d88c79dfc3fecd32e9f9c96dd0d83db1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 10:15:09 +0200 Subject: [PATCH 046/313] - fixed: A_CustomMissile should not jump to the See state for dead monsters when using CMF_CHECKTARGETDEAD. --- 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 048d4d47f..b1c9e9065 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1028,7 +1028,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) else if (flags & CMF_CHECKTARGETDEAD) { // Target is dead and the attack shall be aborted. - if (self->SeeState != NULL) self->SetState(self->SeeState); + if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); } } From b6f829979afeec70c6231317b99b7282f9811f0b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Sep 2014 11:16:23 +0300 Subject: [PATCH 047/313] Cleaned up software rendering internals in Cocoa back-end --- src/cocoa/i_backend_cocoa.mm | 103 +++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index dfb357740..cd7bea109 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -770,6 +770,9 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) D_PostEvent(&event); } + +const Uint16 BYTES_PER_PIXEL = 4; + } // unnamed namespace @@ -844,6 +847,9 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) @private FullscreenWindow* m_window; + uint8_t* m_softwareRenderingBuffer; + GLuint m_softwareRenderingTexture; + int m_multisample; int m_width; @@ -875,6 +881,9 @@ void ProcessMouseWheelEvent(NSEvent* theEvent) - (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI; - (void)useHiDPI:(bool)hiDPI; +- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height; +- (void*)softwareRenderingBuffer; + - (void)processEvents:(NSTimer*)timer; - (void)invalidateCursorRects; @@ -893,6 +902,11 @@ static ApplicationDelegate* s_applicationDelegate; { self = [super init]; + m_window = nil; + + m_softwareRenderingBuffer = NULL; + m_softwareRenderingTexture = 0; + m_multisample = 0; m_width = -1; @@ -907,6 +921,11 @@ static ApplicationDelegate* s_applicationDelegate; - (void)dealloc { + delete[] m_softwareRenderingBuffer; + + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_softwareRenderingTexture); + [m_window release]; [super dealloc]; @@ -1190,6 +1209,38 @@ static ApplicationDelegate* s_applicationDelegate; } +- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height +{ + if (0 == m_softwareRenderingTexture) + { + glEnable(GL_TEXTURE_2D); + + glGenTextures(1, &m_softwareRenderingTexture); + glBindTexture(GL_TEXTURE_2D, m_softwareRenderingTexture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, 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); @@ -1536,10 +1587,6 @@ int SDL_ShowCursor(int) } -static GLuint s_frameBufferTexture = 0; - -static const Uint16 BYTES_PER_PIXEL = 4; - static SDL_PixelFormat* GetPixelFormat() { static SDL_PixelFormat result; @@ -1574,30 +1621,19 @@ SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) useHiDPI:vid_hidpi]; static SDL_Surface result; - - const bool isSoftwareRenderer = !(SDL_OPENGL & flags); - - if (isSoftwareRenderer) + + if (!(SDL_OPENGL & flags)) { - if (NULL != result.pixels) - { - free(result.pixels); - } - - if (0 != s_frameBufferTexture) - { - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &s_frameBufferTexture); - s_frameBufferTexture = 0; - } + [s_applicationDelegate setupSoftwareRenderingWithWidth:width + height:height]; } - + result.flags = flags; result.format = GetPixelFormat(); result.w = width; result.h = height; result.pitch = width * BYTES_PER_PIXEL; - result.pixels = isSoftwareRenderer ? malloc(width * height * BYTES_PER_PIXEL) : NULL; + result.pixels = [s_applicationDelegate softwareRenderingBuffer]; result.refcount = 1; result.clip_rect.x = 0; @@ -1678,35 +1714,10 @@ int SDL_BlitSurface(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_R } -static void SetupSoftwareRendering(SDL_Surface* screen) -{ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, screen->w, screen->h, 0.0, -1.0, 1.0); - - glEnable(GL_TEXTURE_2D); - - glGenTextures(1, &s_frameBufferTexture); - glBindTexture(GL_TEXTURE_2D, s_frameBufferTexture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - int SDL_Flip(SDL_Surface* screen) { assert(NULL != screen); - if (0 == s_frameBufferTexture) - { - SetupSoftwareRendering(screen); - } - if (rbOpts.dirty) { glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); From 7286ee96263cacc0059b7c5607fb4c7439105e77 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Sep 2014 11:23:38 +0300 Subject: [PATCH 048/313] Renamed application delegate to controller --- src/cocoa/i_backend_cocoa.mm | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index cd7bea109..32754a262 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -839,7 +839,7 @@ const Uint16 BYTES_PER_PIXEL = 4; // --------------------------------------------------------------------------- -@interface ApplicationDelegate : NSResponder +@interface ApplicationController : NSResponder #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 #endif @@ -893,10 +893,10 @@ const Uint16 BYTES_PER_PIXEL = 4; @end -static ApplicationDelegate* s_applicationDelegate; +static ApplicationController* appCtrl; -@implementation ApplicationDelegate +@implementation ApplicationController - (id)init { @@ -1329,7 +1329,7 @@ CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (IsHiDPISupported()) { - [s_applicationDelegate useHiDPI:self]; + [appCtrl useHiDPI:self]; } else if (0 != self) { @@ -1343,7 +1343,7 @@ CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) void I_SetMainWindowVisible(bool visible) { - [s_applicationDelegate setMainWindowVisible:visible]; + [appCtrl setMainWindowVisible:visible]; SetNativeMouse(!visible); } @@ -1405,7 +1405,7 @@ bool I_SetCursor(FTexture* cursorpic) hotSpot:NSMakePoint(0.0f, 0.0f)]; } - [s_applicationDelegate invalidateCursorRects]; + [appCtrl invalidateCursorRects]; return true; } @@ -1492,13 +1492,13 @@ int SDL_Init(Uint32 flags) void SDL_Quit() { - if (NULL != s_applicationDelegate) + if (NULL != appCtrl) { [NSApp setDelegate:nil]; [NSApp deactivate]; - [s_applicationDelegate release]; - s_applicationDelegate = NULL; + [appCtrl release]; + appCtrl = NULL; } } @@ -1615,17 +1615,17 @@ static SDL_PixelFormat* GetPixelFormat() SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) { - [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & flags) - width:width - height:height - useHiDPI:vid_hidpi]; - + [appCtrl changeVideoResolution:(SDL_FULLSCREEN & flags) + width:width + height:height + useHiDPI:vid_hidpi]; + static SDL_Surface result; if (!(SDL_OPENGL & flags)) { - [s_applicationDelegate setupSoftwareRenderingWithWidth:width - height:height]; + [appCtrl setupSoftwareRenderingWithWidth:width + height:height]; } result.flags = flags; @@ -1633,7 +1633,7 @@ SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) result.w = width; result.h = height; result.pitch = width * BYTES_PER_PIXEL; - result.pixels = [s_applicationDelegate softwareRenderingBuffer]; + result.pixels = [appCtrl softwareRenderingBuffer]; result.refcount = 1; result.clip_rect.x = 0; @@ -1664,10 +1664,10 @@ int SDL_WM_ToggleFullScreen(SDL_Surface* surface) surface->flags |= SDL_FULLSCREEN; } - [s_applicationDelegate changeVideoResolution:(SDL_FULLSCREEN & surface->flags) - width:surface->w - height:surface->h - useHiDPI:vid_hidpi]; + [appCtrl changeVideoResolution:(SDL_FULLSCREEN & surface->flags) + width:surface->w + height:surface->h + useHiDPI:vid_hidpi]; return 1; } @@ -1682,7 +1682,7 @@ int SDL_GL_SetAttribute(SDL_GLattr attr, int value) { if (SDL_GL_MULTISAMPLESAMPLES == attr) { - [s_applicationDelegate setMultisample:value]; + [appCtrl setMultisample:value]; } // Not interested in other attributes @@ -1832,8 +1832,8 @@ int main(int argc, char** argv) [NSApplication sharedApplication]; [NSBundle loadNibNamed:@"zdoom" owner:NSApp]; - s_applicationDelegate = [ApplicationDelegate new]; - [NSApp setDelegate:s_applicationDelegate]; + appCtrl = [ApplicationController new]; + [NSApp setDelegate:appCtrl]; [NSApp run]; From f0e9fde33662a6ab65ca0ed6ec6ef611bb8f4b7d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 10:40:56 +0200 Subject: [PATCH 049/313] - fixed: In Raven games, don't chase after monsters in the titlemap or when actor has a goal. --- src/p_enemy.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index bdc72b0c8..00e2a3dc2 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1600,7 +1600,10 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) if (!(gameinfo.gametype & (GAME_DoomStrifeChex)) && !multiplayer && - players[0].health <= 0) + players[0].health <= 0 && + actor->goal == NULL && + gamestate != GS_TITLELEVEL + ) { // Single player game and player is dead; look for monsters return P_LookForMonsters (actor); } From 87472bf002128a1c3ee2fe1355535b9356c5188b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Sep 2014 11:44:24 +0300 Subject: [PATCH 050/313] Use the same binary architecture to restart from IWAD picker --- src/sdl/iwadpicker_cocoa.mm | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 92eb47e40..51e0ea0c8 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -376,6 +376,19 @@ static NSArray* GetKnownExtensions() EXTERN_CVAR(String, defaultiwad) +static NSString* GetArchitectureString() +{ +#ifdef __i386__ + return @"i386"; +#elif defined __x86_64__ + return @"x86_64"; +#elif defined __ppc__ + return @"ppc"; +#elif defined __ppc64__ + return @"ppc64"; +#endif +} + static void RestartWithParameters(const char* iwadPath, NSString* parameters) { assert(nil != parameters); @@ -393,8 +406,12 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) assert(commandLineParametersCount > 0); NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)]; + NSString* architecture = GetArchitectureString(); - NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 3]; + NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 6]; + [arguments addObject:@"-arch"]; + [arguments addObject:architecture]; + [arguments addObject:executablePath]; [arguments addObject:@"-wad_picker_restart"]; [arguments addObject:@"-iwad"]; [arguments addObject:[NSString stringWithUTF8String:iwadPath]]; @@ -419,7 +436,7 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) wordfree(&expansion); } - [NSTask launchedTaskWithLaunchPath:executablePath arguments:arguments]; + [NSTask launchedTaskWithLaunchPath:@"/usr/bin/arch" arguments:arguments]; _exit(0); // to avoid atexit()'s functions } From 50a829720125a8ee4a865094a075591b3c03e9a4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 11:00:25 +0200 Subject: [PATCH 051/313] - fixed: G_ChangeLevel must resolve warptrans map links before getting the next level's levelinfo. --- src/g_level.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 9437ced70..9671b81cc 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -319,6 +319,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) bool wantFast; int i; + deathmatch = 1; G_ClearHubInfo(); if (!savegamerestore) { @@ -499,7 +500,9 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill } else if (strncmp(levelname, "enDSeQ", 6) != 0) { - nextinfo = FindLevelInfo (levelname, false); + FString reallevelname = levelname; + CheckWarpTransMap(reallevelname, true); + nextinfo = FindLevelInfo (reallevelname, false); if (nextinfo != NULL) { level_info_t *nextredir = nextinfo->CheckLevelRedirect(); @@ -507,8 +510,12 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill { nextinfo = nextredir; } + nextlevel = nextinfo->MapName; + } + else + { + nextlevel = levelname; } - nextlevel = nextinfo->MapName; } else { From 2ada3fe00eb07330c0a1a15bad4a3a9f51e24463 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 11:40:06 +0200 Subject: [PATCH 052/313] - remove debug stuff. --- src/g_level.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 9671b81cc..d84a48776 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -319,7 +319,6 @@ void G_InitNew (const char *mapname, bool bTitleLevel) bool wantFast; int i; - deathmatch = 1; G_ClearHubInfo(); if (!savegamerestore) { From de68361f27bcfbc4613e5291ed3872f93441f333 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 11:54:19 +0200 Subject: [PATCH 053/313] - fixed: When a sprite is being renamed by Dehacked it also needs to be changed in the list of original sprite names, otherwise any subsequent attempt to use the altered sprite in a frame definition will fail. --- src/d_dehacked.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index e6376a589..d14015ca9 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2154,6 +2154,13 @@ static int PatchText (int oldSize) { strncpy (deh.PlayerSprite, newStr, 4); } + for (unsigned ii = 0; ii < OrgSprNames.Size(); ii++) + { + if (!stricmp(OrgSprNames[ii].c, oldStr)) + { + strcpy(OrgSprNames[ii].c, newStr); + } + } // If this sprite is used by a pickup, then the DehackedPickup sprite map // needs to be updated too. for (i = 0; (size_t)i < countof(DehSpriteMappings); ++i) From f9741f1047468085851bae08370ac40b0f0b3770 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 12:20:06 +0200 Subject: [PATCH 054/313] - added a compatibility option for Hexen's MAP04 to change the z-position of 4 quartz flasks that get lowered into a pool of lava but were incorrectly positioned 48 map units above the floor. --- wadsrc/static/compatibility.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 1c2307ec0..30c54c556 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -198,6 +198,14 @@ E2B5D1400279335811C1C1C0B437D9C8 // Deathknights of the Dark Citadel, map54 clearlineflags 1069 0x200 } +CBDE77E3ACB4B166D53C1812E5C72F54 // Hexen IWAD map04 +{ + setthingz 49 0 + setthingz 50 0 + setthingz 51 0 + setthingz 52 0 +} + 3F249EDD62A3A08F53A6C53CB4C7ABE5 // Artica 3 map01 { clearlinespecial 66 From ee6e87d94bb5de1531d8c0614fa6fd2549ef7132 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 13 Sep 2014 12:38:16 +0200 Subject: [PATCH 055/313] - use a separate counter from AActor::special1 to count weapon use with some Hexen weapons. special1 is used for some other purposes as well, and when using a separate counter it can be reset to 0 when changing weapons, preventing counting errors. --- src/actor.h | 1 + src/g_hexen/a_clericmace.cpp | 2 +- src/g_hexen/a_clericstaff.cpp | 6 +++--- src/g_hexen/a_fighteraxe.cpp | 2 +- src/g_hexen/a_fighterhammer.cpp | 12 ++++++------ src/g_hexen/a_fighterplayer.cpp | 8 ++++---- src/p_mobj.cpp | 4 ++++ src/p_pspr.cpp | 1 + src/version.h | 2 +- 9 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/actor.h b/src/actor.h index 783eb5408..bf4b92457 100644 --- a/src/actor.h +++ b/src/actor.h @@ -857,6 +857,7 @@ public: int special1; // Special info int special2; // Special info + int weaponspecial; // Special info for weapons. int health; BYTE movedir; // 0-7 SBYTE visdir; diff --git a/src/g_hexen/a_clericmace.cpp b/src/g_hexen/a_clericmace.cpp index 0e43f3df9..79b5df340 100644 --- a/src/g_hexen/a_clericmace.cpp +++ b/src/g_hexen/a_clericmace.cpp @@ -56,7 +56,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CMaceAttack) } } // didn't find any creatures, so try to strike any walls - player->mo->special1 = 0; + player->mo->weaponspecial = 0; angle = player->mo->angle; slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget); diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp index 36f0c7686..aa8b94d74 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -164,7 +164,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffMissileSlither) DEFINE_ACTION_FUNCTION(AActor, A_CStaffInitBlink) { - self->special1 = (pr_blink()>>1)+20; + self->weaponspecial = (pr_blink()>>1)+20; } //============================================================================ @@ -177,10 +177,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheckBlink) { if (self->player && self->player->ReadyWeapon) { - if (!--self->special1) + if (!--self->weaponspecial) { P_SetPsprite (self->player, ps_weapon, self->player->ReadyWeapon->FindState ("Blink")); - self->special1 = (pr_blink()+50)>>2; + self->weaponspecial = (pr_blink()+50)>>2; } else { diff --git a/src/g_hexen/a_fighteraxe.cpp b/src/g_hexen/a_fighteraxe.cpp index f80af876b..c1e396810 100644 --- a/src/g_hexen/a_fighteraxe.cpp +++ b/src/g_hexen/a_fighteraxe.cpp @@ -253,7 +253,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FAxeAttack) } } // didn't find any creatures, so try to strike any walls - pmo->special1 = 0; + pmo->weaponspecial = 0; angle = pmo->angle; slope = P_AimLineAttack (pmo, angle, MELEERANGE, &linetarget); diff --git a/src/g_hexen/a_fighterhammer.cpp b/src/g_hexen/a_fighterhammer.cpp index 85bbb3aae..42aae31fb 100644 --- a/src/g_hexen/a_fighterhammer.cpp +++ b/src/g_hexen/a_fighterhammer.cpp @@ -57,7 +57,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack) { P_ThrustMobj (linetarget, angle, power); } - pmo->special1 = false; // Don't throw a hammer + pmo->weaponspecial = false; // Don't throw a hammer goto hammerdone; } } @@ -73,7 +73,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack) { P_ThrustMobj(linetarget, angle, power); } - pmo->special1 = false; // Don't throw a hammer + pmo->weaponspecial = false; // Don't throw a hammer goto hammerdone; } } @@ -83,11 +83,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack) slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE, &linetarget, 0, ALF_CHECK3D); if (P_LineAttack (pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, PClass::FindClass ("HammerPuff"), true) != NULL) { - pmo->special1 = false; + pmo->weaponspecial = false; } else { - pmo->special1 = true; + pmo->weaponspecial = true; } hammerdone: // Don't spawn a hammer if the player doesn't have enough mana @@ -95,7 +95,7 @@ hammerdone: !player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire ? AWeapon::AltFire : AWeapon::PrimaryFire, false, true)) { - pmo->special1 = false; + pmo->weaponspecial = false; } return; } @@ -116,7 +116,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerThrow) return; } - if (!player->mo->special1) + if (!player->mo->weaponspecial) { return; } diff --git a/src/g_hexen/a_fighterplayer.cpp b/src/g_hexen/a_fighterplayer.cpp index 1b051a608..5df183901 100644 --- a/src/g_hexen/a_fighterplayer.cpp +++ b/src/g_hexen/a_fighterplayer.cpp @@ -66,7 +66,7 @@ static bool TryPunch(APlayerPawn *pmo, angle_t angle, int damage, fixed_t power) slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE, &linetarget); if (linetarget != NULL) { - if (++pmo->special1 >= 3) + if (++pmo->weaponspecial >= 3) { damage <<= 1; power *= 3; @@ -117,9 +117,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_FPunchAttack) if (TryPunch(pmo, pmo->angle + i*(ANG45/16), damage, power) || TryPunch(pmo, pmo->angle - i*(ANG45/16), damage, power)) { // hit something - if (pmo->special1 >= 3) + if (pmo->weaponspecial >= 3) { - pmo->special1 = 0; + pmo->weaponspecial = 0; P_SetPsprite (player, ps_weapon, player->ReadyWeapon->FindState ("Fire2")); S_Sound (pmo, CHAN_VOICE, "*fistgrunt", 1, ATTN_NORM); } @@ -127,7 +127,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FPunchAttack) } } // didn't find any creatures, so try to strike any walls - pmo->special1 = 0; + pmo->weaponspecial = 0; AActor *linetarget; int slope = P_AimLineAttack (pmo, pmo->angle, MELEERANGE, &linetarget); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index cdc511d11..50461c43a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -206,6 +206,10 @@ void AActor::Serialize (FArchive &arc) { arc << flags7; } + if (SaveVersion >= 4511) + { + arc << weaponspecial; + } arc << special1 << special2 << health diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 49f8bcb84..e477d9751 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -211,6 +211,7 @@ void P_BringUpWeapon (player_t *player) // make sure that the previous weapon's flash state is terminated. // When coming here from a weapon drop it may still be active. P_SetPsprite(player, ps_flash, NULL); + player->mo->weaponspecial = 0; } diff --git a/src/version.h b/src/version.h index af376938c..c8917212d 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 4511 +#define SAVEVER 4512 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 1e3e94f8ad6073c6c148d7719668905eb668fd58 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 13 Sep 2014 15:46:32 +0300 Subject: [PATCH 056/313] Fixed crash Apple's HID Utilities in x86_64 build Only a small part of HID Utilities version 5.3, 2014-02-28 was merged in because of various issues it has https://developer.apple.com/library/mac/samplecode/HID_Config_Save/ --- src/cocoa/IOHIDDevice_.c | 194 ++++++++++++++++++++++++++------------- src/cocoa/IOHIDDevice_.h | 44 ++++----- 2 files changed, 153 insertions(+), 85 deletions(-) diff --git a/src/cocoa/IOHIDDevice_.c b/src/cocoa/IOHIDDevice_.c index c1e2d12cf..ab35c21e0 100755 --- a/src/cocoa/IOHIDDevice_.c +++ b/src/cocoa/IOHIDDevice_.c @@ -1,6 +1,6 @@ // File: IOHIDDevice_.c // Abstract: convieance functions for IOHIDDeviceGetProperty -// Version: 2.0 +// 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 @@ -59,8 +59,17 @@ #pragma mark - local (static) function prototypes //----------------------------------------------------- -static Boolean IOHIDDevice_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long *outValue); -static void IOHIDDevice_SetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long inValue); +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 @@ -120,12 +129,12 @@ CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the vendor ID for this device +// Returns: uint32_t - the vendor ID for this device // -long IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey), &result); +uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey), &result); return (result); } // IOHIDDevice_GetVendorID @@ -137,12 +146,12 @@ long IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the VendorIDSource for this device +// Returns: uint32_t - the VendorIDSource for this device // -long IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDSourceKey), &result); +uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDSourceKey), &result); return (result); } // IOHIDDevice_GetVendorIDSource @@ -154,12 +163,12 @@ long IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the product ID for this device +// Returns: uint32_t - the product ID for this device // -long IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey), &result); +uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey), &result); return (result); } // IOHIDDevice_GetProductID @@ -171,12 +180,12 @@ long IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: CFStringRef - the VersionNumber for this device +// Returns: uint32_t - the VersionNumber for this device // -long IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVersionNumberKey), &result); +uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVersionNumberKey), &result); return (result); } // IOHIDDevice_GetVersionNumber @@ -236,12 +245,12 @@ CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: CFStringRef - the CountryCode for this device +// Returns: uint32_t - the CountryCode for this device // -long IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDCountryCodeKey), &result); +uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDCountryCodeKey), &result); return (result); } // IOHIDDevice_GetCountryCode @@ -253,12 +262,12 @@ long IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the location ID for this device +// Returns: uint32_t - the location ID for this device // -long IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey), &result); +uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey), &result); return (result); } // IOHIDDevice_GetLocationID @@ -275,7 +284,7 @@ long IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef) { uint32_t result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), (long *) &result); + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), &result); return (result); } // IOHIDDevice_GetUsage @@ -291,8 +300,8 @@ uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef) { // uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePageKey), &result); + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePageKey), &result); return (result); } // IOHIDDevice_GetUsagePage @@ -320,12 +329,12 @@ CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: CFStringRef - the PrimaryUsage for this device +// Returns: uint32_t - the PrimaryUsage for this device // uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey), &result); + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey), &result); return (result); } // IOHIDDevice_GetPrimaryUsage @@ -337,12 +346,12 @@ uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: CFStringRef - the PrimaryUsagePage for this device +// Returns: uint32_t - the PrimaryUsagePage for this device // uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey), &result); + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey), &result); return (result); } // IOHIDDevice_GetPrimaryUsagePage @@ -354,12 +363,12 @@ uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the MaxInputReportSize for this device +// Returns: uint32_t - the MaxInputReportSize for this device // -long IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDMaxInputReportSizeKey), &result); +uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxInputReportSizeKey), &result); return (result); } // IOHIDDevice_GetMaxInputReportSize @@ -371,12 +380,12 @@ long IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the MaxOutput for this device +// Returns: uint32_t - the MaxOutput for this device // -long IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey), &result); +uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey), &result); return (result); } // IOHIDDevice_GetMaxOutputReportSize @@ -388,12 +397,12 @@ long IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the MaxFeatureReportSize for this device +// Returns: uint32_t - the MaxFeatureReportSize for this device // -long IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey), &result); +uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey), &result); return (result); } // IOHIDDevice_GetMaxFeatureReportSize @@ -405,14 +414,14 @@ long IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // -// Returns: long - the ReportInterval for this device +// Returns: uint32_t - the ReportInterval for this device // #ifndef kIOHIDReportIntervalKey #define kIOHIDReportIntervalKey "ReportInterval" #endif -long IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { - long result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDReportIntervalKey), &result); +uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDReportIntervalKey), &result); return (result); } // IOHIDDevice_GetReportInterval @@ -429,7 +438,7 @@ long IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef) { IOHIDQueueRef result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (long *) &result); + (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (void *) &result); if ( result ) { assert( IOHIDQueueGetTypeID() == CFGetTypeID(result) ); } @@ -450,7 +459,7 @@ IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef) { // void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef) { - IOHIDDevice_SetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (long) inQueueRef); + IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), inQueueRef); } //************************************************************************* @@ -466,7 +475,7 @@ void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueue IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef) { IOHIDTransactionRef result = 0; - (void) IOHIDDevice_GetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (long *) &result); + (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (void *) &result); return (result); } // IOHIDDevice_GetTransaction @@ -483,7 +492,7 @@ IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef) // void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef) { - IOHIDDevice_SetLongProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (long) inTransactionRef); + IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), inTransactionRef); } //***************************************************** @@ -492,9 +501,9 @@ void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactio //************************************************************************* // -// IOHIDDevice_GetLongProperty( inIOHIDDeviceRef, inKey, outValue ) +// IOHIDDevice_GetUInt32Property( inIOHIDDeviceRef, inKey, outValue ) // -// Purpose: convieance function to return a long property of a device +// Purpose: convieance function to return a uint32_t property of a device // // Inputs: inIOHIDDeviceRef - the device // inKey - CFString for the @@ -503,7 +512,7 @@ void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactio // outValue - the device // -static Boolean IOHIDDevice_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long *outValue) { +static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t *outValue) { Boolean result = FALSE; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); @@ -519,11 +528,66 @@ static Boolean IOHIDDevice_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFSt } return (result); -} // IOHIDDevice_GetLongProperty +} // IOHIDDevice_GetUInt32Property //************************************************************************* // -// IOHIDDevice_SetLongProperty( inIOHIDDeviceRef, inKey, inValue ) +// 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 // @@ -533,12 +597,16 @@ static Boolean IOHIDDevice_GetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFSt // Returns: nothing // -static void IOHIDDevice_SetLongProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, long inValue) { +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_SetLongProperty +} // IOHIDDevice_SetPtrProperty //***************************************************** diff --git a/src/cocoa/IOHIDDevice_.h b/src/cocoa/IOHIDDevice_.h index 739352844..eebccd667 100755 --- a/src/cocoa/IOHIDDevice_.h +++ b/src/cocoa/IOHIDDevice_.h @@ -1,6 +1,6 @@ // File: IOHIDDevice_.h // Abstract: convieance functions for IOHIDDeviceGetProperty -// Version: 2.0 +// 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 @@ -40,7 +40,7 @@ // STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// Copyright (C) 2014 Apple Inc. All Rights Reserved. // //***************************************************** #ifndef __IOHIDDevice__ @@ -120,10 +120,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the vendor ID for this device + // Returns: uint32_t - the vendor ID for this device // - extern long IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -133,10 +133,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the VendorIDSource for this device + // Returns: uint32_t - the VendorIDSource for this device // - extern long IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -146,10 +146,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the product ID for this device + // Returns: uint32_t - the product ID for this device // - extern long IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -159,10 +159,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the VersionNumber for this device + // Returns: uint32_t - the VersionNumber for this device // - extern long IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -211,10 +211,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the CountryCode for this device + // Returns: uint32_t - the CountryCode for this device // - extern long IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -224,10 +224,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the location ID for this device + // Returns: uint32_t - the location ID for this device // - extern long IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -302,10 +302,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the MaxInputReportSize for this device + // Returns: uint32_t - the MaxInputReportSize for this device // - extern long IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -315,10 +315,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the MaxOutputReportSize for this device + // Returns: uint32_t - the MaxOutputReportSize for this device // - extern long IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -328,10 +328,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the MaxFeatureReportSize for this device + // Returns: uint32_t - the MaxFeatureReportSize for this device // - extern long IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // @@ -341,10 +341,10 @@ extern "C" { // // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device // - // Returns: long - the ReportInterval for this device + // Returns: uint32_t - the ReportInterval for this device // - extern long IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef); + extern uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef); //************************************************************************* // From 1529ff7730858118e1315885dc854013ce62e0e5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 14 Sep 2014 09:51:55 +0300 Subject: [PATCH 057/313] Fixed compilation warning in HID name formatting --- src/cocoa/i_joystick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index add1b1bd7..5a1c92e8c 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -376,7 +376,7 @@ FString IOKitJoystick::GetIdentifier() { char identifier[ 32 ] = {0}; - snprintf( identifier, sizeof( identifier ), "VID_%04lx_PID_%04lx", + snprintf( identifier, sizeof( identifier ), "VID_%04x_PID_%04x", IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) ); return FString( identifier ); From 1b13e6be983c4befbdb2d78d050eb2da4753f03e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 15 Sep 2014 11:24:48 +0200 Subject: [PATCH 058/313] -. fixed:S_LoadSound passed the wrong value for the lump size parameter to LoadSoundVoc. --- src/s_sound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 32a245675..035b71c74 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1321,7 +1321,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) // If the sound is voc, use the custom loader. if (strncmp ((const char *)sfxstart, "Creative Voice File", 19) == 0) { - sfx->data = GSnd->LoadSoundVoc(sfxstart, len); + sfx->data = GSnd->LoadSoundVoc(sfxstart, size); } // If the sound is raw, just load it as such. // Otherwise, try the sound as DMX format. From 4be9a716361c119c4d81eb1571f58420ca333b75 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 15 Sep 2014 11:32:40 +0200 Subject: [PATCH 059/313] - fixed: The VOC reader didn't advance the read index correctly. --- src/sound/i_sound.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index c95b0221f..07cd43a4b 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -494,6 +494,7 @@ SoundHandle SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length) break; default: break; } + i += blocksize; } } From 94123d5ef40e6843beb27a653f5e01dd3181a375 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 15 Sep 2014 12:59:07 -0400 Subject: [PATCH 060/313] - Applied anonymous patch to fix uncapped stuttering in SDL backend. --- src/sdl/i_system.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 9b3027a7d..8fea36c37 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -123,14 +123,13 @@ void I_EndRead(void) static DWORD TicStart; -static DWORD TicNext; static DWORD BaseTime; static int TicFrozen; // Signal based timer. static Semaphore timerWait; static int tics; -static DWORD sig_start, sig_next; +static DWORD sig_start; void I_SelectTimer(); @@ -169,7 +168,6 @@ int I_GetTimePolled (bool saveMS) if (saveMS) { TicStart = tm; - TicNext = Scale((Scale (tm, TICRATE, 1000) + 1), 1000, TICRATE); } return Scale(tm - BaseTime, TICRATE, 1000); } @@ -179,7 +177,6 @@ int I_GetTimeSignaled (bool saveMS) if (saveMS) { TicStart = sig_start; - TicNext = sig_next; } return tics; } @@ -250,7 +247,6 @@ void I_HandleAlarm (int sig) if(!TicFrozen) tics++; sig_start = SDL_GetTicks(); - sig_next = Scale((Scale (sig_start, TICRATE, 1000) + 1), 1000, TICRATE); SEMAPHORE_SIGNAL(timerWait) } @@ -293,15 +289,14 @@ void I_SelectTimer() fixed_t I_GetTimeFrac (uint32 *ms) { DWORD now = SDL_GetTicks (); - if (ms) *ms = TicNext; - DWORD step = TicNext - TicStart; - if (step == 0) + if (ms) *ms = TicStart + (1000 / TICRATE); + if (TicStart == 0) { return FRACUNIT; } else { - fixed_t frac = clamp ((now - TicStart)*FRACUNIT/step, 0, FRACUNIT); + fixed_t frac = clamp ((now - TicStart)*FRACUNIT*TICRATE/1000, 0, FRACUNIT); return frac; } } From 1c96039d7a1146a50b52335e0af8623f97f98415 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Mon, 15 Sep 2014 19:46:43 +0200 Subject: [PATCH 061/313] - Fixed miscompilation with Clang 3.5.0. The optimizer miscompiles the function FBehavior::LoadScriptsDirectory and causes random crashes when zdoom is run with wads containing scripts. As said in the comment, I just hope that the Clang devs fix it for the next patching release, ie 3.5.1. --- src/p_acs.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 235a47933..405d60320 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2306,6 +2306,12 @@ void FBehavior::LoadScriptsDirectory () default: break; } + +// [EP] Clang 3.5.0 optimizer miscompiles this function and causes random +// crashes in the program. I hope that Clang 3.5.x will fix this. +#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ >= 5 + asm("" : "+g" (NumScripts)); +#endif for (i = 0; i < NumScripts; ++i) { Scripts[i].Flags = 0; From 864adde92ef98245551d2ab01ac222f332b36b0b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 16 Sep 2014 08:42:27 +0200 Subject: [PATCH 062/313] - typo for savegame versioning of AActor::weaponspecial: it was introduced in savegame version 4512, not 4511. --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 50461c43a..d035e0117 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -206,7 +206,7 @@ void AActor::Serialize (FArchive &arc) { arc << flags7; } - if (SaveVersion >= 4511) + if (SaveVersion >= 4512) { arc << weaponspecial; } From 7a62459c00c6ffe8ff500ba20a5c55321b52f93b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 18 Sep 2014 17:58:06 -0500 Subject: [PATCH 063/313] Allow SXF_SETMASTER to work on non-monsters. --- src/thingdef/thingdef_codeptr.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index b1c9e9065..c443c57d9 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1843,6 +1843,10 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) // If this is a missile or something else set the target to the originator mo->target = originator ? originator : self; } + if (flags & SIXF_SETMASTER) + { + mo->master = originator; + } if (flags & SIXF_TRANSFERSCALE) { mo->scaleX = self->scaleX; From 5c4ad9be68a09a08a1c328f346faebb5fc169e12 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Sep 2014 14:15:31 -0500 Subject: [PATCH 064/313] Added Blue Shadow's A_SpawnItemEx changes: (1/2) SXF_TRANSFERALPHA and SXF_TRANSFERRENDERSTYLE --- src/thingdef/thingdef_codeptr.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c443c57d9..f25a0818e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1755,6 +1755,8 @@ enum SIX_Flags SIXF_TRANSFERSPECIAL = 1 << 15, SIXF_CLEARCALLERSPECIAL = 1 << 16, SIXF_TRANSFERSTENCILCOL = 1 << 17, + SIXF_TRANSFERALPHA = 1 << 18, + SIXF_TRANSFERRENDERSTYLE = 1 << 19, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1875,6 +1877,14 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { mo->fillcolor = self->fillcolor; } + if (flags & SIXF_TRANSFERALPHA) + { + mo->alpha = self->alpha; + } + if (flags & SIXF_TRANSFERRENDERSTYLE) + { + mo->RenderStyle = self->RenderStyle; + } return true; } From 33f83cc7f92b9b814f1bdc8e166da01d9386f453 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Sep 2014 14:17:24 -0500 Subject: [PATCH 065/313] Added Blue Shadow's A_SpawnItemEx changes: (2/2) SXF_TRANSFERALPHA and SXF_TRANSFERRENDERSTYLE --- wadsrc/static/actors/constants.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index d5358e5af..bf998e6b0 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -65,6 +65,8 @@ const int SXF_TRANSFERSCALE = 16384; const int SXF_TRANSFERSPECIAL = 32768; const int SXF_CLEARCALLERSPECIAL = 65536; const int SXF_TRANSFERSTENCILCOL = 131072; +const int SXF_TRANSFERALPHA = 262144; +const int SXF_TRANSFERRENDERSTYLE = 524288; // Flags for A_Chase const int CHF_FASTCHASE = 1; From 16a380f82ad5ded598640f12ac498e3b7cac9d04 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Sep 2014 17:27:18 -0500 Subject: [PATCH 066/313] - Added Blue Shadow's DropInventory ACS function. --- src/p_acs.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 405d60320..c5ea7031d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4367,6 +4367,7 @@ enum EACSFunctions ACSF_ChangeActorAngle, ACSF_ChangeActorPitch, // 80 ACSF_GetArmorInfo, + ACSF_DropInventory, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5491,6 +5492,42 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) break; } + case ACSF_DropInventory: + { + const char *type = FBehavior::StaticLookupString(args[1]); + AInventory *inv; + + if (type != NULL) + { + if (args[0] == 0) + { + if (activator != NULL) + { + inv = activator->FindInventory(type); + if (inv) + { + activator->DropInventory(inv); + } + } + } + else + { + FActorIterator it(args[0]); + AActor *actor; + + while ((actor = it.Next()) != NULL) + { + inv = actor->FindInventory(type); + if (inv) + { + actor->DropInventory(inv); + } + } + } + } + break; + } + case ACSF_CheckFlag: { AActor *actor = SingleActorFromTID(args[0], activator); From e8ca4fa94e9c92306b4da2566ee460d948df3029 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Sep 2014 11:55:49 +0300 Subject: [PATCH 067/313] Applied fixes for timer to its thread-based implementation --- src/cocoa/i_timer.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cocoa/i_timer.cpp b/src/cocoa/i_timer.cpp index 29d3f8865..db6811212 100644 --- a/src/cocoa/i_timer.cpp +++ b/src/cocoa/i_timer.cpp @@ -68,10 +68,7 @@ bool s_timerInitialized; bool s_timerExitRequested; uint32_t s_ticStart; -uint32_t s_ticNext; - uint32_t s_timerStart; -uint32_t s_timerNext; int s_tics; @@ -99,7 +96,6 @@ void* TimerThreadFunc(void*) } s_timerStart = SDL_GetTicks(); - s_timerNext = Scale(Scale(s_timerStart, TICRATE, 1000) + 1, 1000, TICRATE); pthread_cond_broadcast(&s_timerEvent); pthread_mutex_unlock(&s_timerMutex); @@ -113,7 +109,6 @@ int GetTimeThreaded(bool saveMS) if (saveMS) { s_ticStart = s_timerStart; - s_ticNext = s_timerNext; } return s_tics; @@ -147,14 +142,12 @@ fixed_t I_GetTimeFrac(uint32* ms) if (NULL != ms) { - *ms = s_ticNext; + *ms = s_ticStart + 1000 / TICRATE; } - const uint32_t step = s_ticNext - s_ticStart; - - return 0 == step + return 0 == s_ticStart ? FRACUNIT - : clamp( (now - s_ticStart) * FRACUNIT / step, 0, FRACUNIT); + : clamp( (now - s_ticStart) * FRACUNIT * TICRATE / 1000, 0, FRACUNIT); } From 49dca4d9cba994150e49b8b07fdf617a354b1f79 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Sep 2014 11:56:15 +0300 Subject: [PATCH 068/313] Native Cocoa back-end is enabled by default --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ed4d0f00..3cfb73c26 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,7 +26,7 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON ) -option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" OFF ) +option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) From 44683657f22e29330f302cd80f82e71e7012e617 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 20 Sep 2014 17:34:57 -0500 Subject: [PATCH 069/313] - Added jpalomo's A_SetSpeed patch. --- src/thingdef/thingdef_codeptr.cpp | 14 ++++++++++++++ src/thingdef/thingdef_expression.cpp | 1 + wadsrc/static/actors/actor.txt | 2 ++ 3 files changed, 17 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index f25a0818e..f80e89aad 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4980,3 +4980,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) P_DropItem(self, spawntype, amount, chance); } + +//========================================================================== +// +// A_SetSpeed +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(speed, 0); + + self->Speed = speed; +} diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index c578ddb2b..7dbf5ec36 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -88,6 +88,7 @@ DEFINE_MEMBER_VARIABLE(height, AActor) DEFINE_MEMBER_VARIABLE(radius, AActor) DEFINE_MEMBER_VARIABLE(reactiontime, AActor) DEFINE_MEMBER_VARIABLE(meleerange, AActor) +DEFINE_MEMBER_VARIABLE(Speed, AActor) //========================================================================== diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 501933f04..e66226c1e 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -62,6 +62,7 @@ ACTOR Actor native //: Thinker native fixed_t radius; native int reactiontime; native fixed_t meleerange; + native fixed_t speed; // Meh, MBF redundant functions. Only for DeHackEd support. action native A_Turn(float angle = 0); @@ -302,6 +303,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_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From cbf72fe9922e221ab441f7579c229382af72ab27 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 21 Sep 2014 08:32:39 +0200 Subject: [PATCH 070/313] - fixed: Since the serializing code cannot distinguish between user variables of the same name coming from different actors in an inheritance chain, this kind of name duplication must be prohibited. --- src/thingdef/thingdef_parse.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 498003b4e..88e6084b6 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -374,6 +374,8 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) FScriptPosition::ErrorCounter++; } + + FName symname = sc.String; if (sc.CheckToken('[')) { @@ -391,6 +393,15 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) } sc.MustGetToken(';'); + // We must ensure that we do not define duplicates, even when they come from a parent table. + if (symt->FindSymbol(symname, true) != NULL) + { + sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", + symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; + return; + } + PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1)); sym->ValueType = valuetype; From f92cdb0ce799901e54795dbc70fe383f62b2ad27 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 21 Sep 2014 09:11:33 +0200 Subject: [PATCH 071/313] - fixed: When the SectorAction's TriggerAction method was renamed, the inherited one in the MusicChanger was overlooked. (Bit thanks to the C++ specs for allowing to override a non-virtual method with a virtual one without even showing a warning...) --- src/s_advsound.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 5b2e4e7c1..f7a40da9b 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2347,14 +2347,14 @@ class AMusicChanger : public ASectorAction { DECLARE_CLASS (AMusicChanger, ASectorAction) public: - virtual bool TriggerAction (AActor *triggerer, int activationType); + virtual bool DoTriggerAction (AActor *triggerer, int activationType); virtual void Tick(); virtual void PostBeginPlay(); }; IMPLEMENT_CLASS(AMusicChanger) -bool AMusicChanger::TriggerAction (AActor *triggerer, int activationType) +bool AMusicChanger::DoTriggerAction (AActor *triggerer, int activationType) { if (activationType & SECSPAC_Enter) { @@ -2364,7 +2364,7 @@ bool AMusicChanger::TriggerAction (AActor *triggerer, int activationType) reactiontime = 30; } } - return Super::TriggerAction (triggerer, activationType); + return Super::DoTriggerAction (triggerer, activationType); } void AMusicChanger::Tick() From 313b2293193a7ba1d55cd85ffe52797f0e07d093 Mon Sep 17 00:00:00 2001 From: GitExl Date: Sun, 21 Sep 2014 16:43:17 +0200 Subject: [PATCH 072/313] Add PickActor ACS function. --- src/p_acs.cpp | 33 +++++++++++++++++++++++++++++++++ src/p_local.h | 1 + src/p_map.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index c5ea7031d..fd41d6e51 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4368,6 +4368,7 @@ enum EACSFunctions ACSF_ChangeActorPitch, // 80 ACSF_GetArmorInfo, ACSF_DropInventory, + ACSF_PickActor, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5593,6 +5594,38 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_PickActor: + if (argCount >= 5) + { + actor = SingleActorFromTID(args[0], activator); + if (actor == NULL) + { + return 0; + } + + DWORD actorMask = MF_SHOOTABLE; + if (argCount >= 6) { + actorMask = args[5]; + } + + DWORD wallMask = ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN; + if (argCount >= 7) { + wallMask = args[6]; + } + + AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); + if (pickedActor == NULL) { + return 0; + } + + pickedActor->RemoveFromHash(); + pickedActor->tid = args[4]; + pickedActor->AddToHash(); + + return 1; + } + break; + default: break; } diff --git a/src/p_local.h b/src/p_local.h index 58d9d02e5..939018734 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -462,6 +462,7 @@ enum // P_LineAttack flags AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); +AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, DWORD actorMask, DWORD wallMask); void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version diff --git a/src/p_map.cpp b/src/p_map.cpp index 4ea8762ec..2274e7571 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3783,6 +3783,52 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, return NULL; } +//========================================================================== +// +// P_LinePickActor +// +//========================================================================== + +AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, + DWORD actorMask, DWORD wallMask) +{ + fixed_t vx, vy, vz, shootz; + + angle >>= ANGLETOFINESHIFT; + pitch = (angle_t)(pitch) >> ANGLETOFINESHIFT; + + vx = FixedMul(finecosine[pitch], finecosine[angle]); + vy = FixedMul(finecosine[pitch], finesine[angle]); + vz = -finesine[pitch]; + + shootz = t1->z - t1->floorclip + (t1->height >> 1); + if (t1->player != NULL) + { + shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor); + } + else + { + shootz += 8 * FRACUNIT; + } + + FTraceResults trace; + Origin TData; + + TData.Caller = t1; + TData.hitGhosts = true; + + if (Trace(t1->x, t1->y, shootz, t1->Sector, vx, vy, vz, distance, + actorMask, wallMask, t1, trace, TRACE_NoSky, CheckForActor, &TData)) + { + if (trace.HitType == TRACE_HitActor) + { + return trace.Actor; + } + } + + return NULL; +} + //========================================================================== // // From bb3d2fa5350cbb3b37147be73ac45a5d9c6fd3d5 Mon Sep 17 00:00:00 2001 From: GitExl Date: Tue, 23 Sep 2014 12:10:39 +0200 Subject: [PATCH 073/313] Add CanRaiseActor ACS function. --- src/p_acs.cpp | 21 +++++++++++++++++++++ src/p_local.h | 1 + src/p_things.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fd41d6e51..86ab02272 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4369,6 +4369,7 @@ enum EACSFunctions ACSF_GetArmorInfo, ACSF_DropInventory, ACSF_PickActor, + ACSF_CanRaiseActor, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5626,6 +5627,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_CanRaiseActor: + if (argCount >= 1) { + if (args[0] == 0) { + actor = SingleActorFromTID(args[0], activator); + if (actor != NULL) { + return P_Thing_CanRaise(actor); + } + } + + FActorIterator iterator(args[0]); + bool canraiseall = false; + while ((actor = iterator.Next())) + { + canraiseall = !P_Thing_CanRaise(actor) | canraiseall; + } + + return !canraiseall; + } + break; + default: break; } diff --git a/src/p_local.h b/src/p_local.h index 939018734..8ba1dd40b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -171,6 +171,7 @@ int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); void P_RemoveThing(AActor * actor); bool P_Thing_Raise(AActor *thing); +bool P_Thing_CanRaise(AActor *thing); const PClass *P_GetSpawnableType(int spawnnum); // diff --git a/src/p_things.cpp b/src/p_things.cpp index e62c65ae7..2a93ee31e 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -445,6 +445,40 @@ bool P_Thing_Raise(AActor *thing) return true; } +bool P_Thing_CanRaise(AActor *thing) +{ + FState * RaiseState = thing->GetRaiseState(); + if (RaiseState == NULL) + { + return false; + } + + AActor *info = thing->GetDefault(); + + // Check against real height and radius + int oldflags = thing->flags; + fixed_t oldheight = thing->height; + fixed_t oldradius = thing->radius; + + thing->flags |= MF_SOLID; + thing->height = info->height; + thing->radius = info->radius; + + bool check = P_CheckPosition (thing, thing->x, thing->y); + + // Restore checked properties + thing->flags = oldflags; + thing->radius = oldradius; + thing->height = oldheight; + + if (!check) + { + return false; + } + + return true; +} + void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob) { if (actor != NULL) From 79d9a573bb2520355d387f4b02fc6fc455b1c146 Mon Sep 17 00:00:00 2001 From: John Palomo Jr Date: Wed, 24 Sep 2014 03:56:20 -0400 Subject: [PATCH 074/313] Added AProp_MeleeRange to SetActorProperty. --- src/p_acs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fd41d6e51..983728c8d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3843,6 +3843,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->reactiontime = value; break; + case APROP_MeleeRange: + actor->meleerange = value; + break; + case APROP_ViewHeight: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) static_cast(actor)->ViewHeight = value; From b1f87295b86487686b789f9cd730be525bc6fbe6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 16:47:41 -0500 Subject: [PATCH 075/313] - Added A_DamageSelf. - A_DamageSelf(int amount, name damagetype) --- src/thingdef/thingdef_codeptr.cpp | 31 +++++++++++++++++++++++++++---- wadsrc/static/actors/actor.txt | 1 + 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index f80e89aad..aff2629e4 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3532,7 +3532,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) //=========================================================================== // -// A_DamageMaster (int amount) +// A_DamageMaster (int amount, str damagetype) // Damages the master of this child by the specified amount. Negative values heal. // //=========================================================================== @@ -3558,7 +3558,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== // -// A_DamageChildren (amount) +// A_DamageChildren (amount, str damagetype) // Damages the children of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3592,7 +3592,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== // -// A_DamageSiblings (amount) +// A_DamageSiblings (int amount, str damagetype) // Damages the siblings of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -4993,4 +4993,27 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) ACTION_PARAM_FIXED(speed, 0); self->Speed = speed; -} +} + +//=========================================================================== +// +// A_DamageSelf (int amount, str damagetype) +// Damages the calling actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (amount > 0) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } +} \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e66226c1e..95d055afb 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -304,6 +304,7 @@ 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"); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From 542b8a7171a8e5daec4a0ec206c88c9bbde16a9e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sat, 16 Aug 2014 21:15:39 +1200 Subject: [PATCH 076/313] Added network load balancing - Guests can now attempt to match latency with the arbitrator. (net_loadbalance) - Added althud feature to show arbitrator and local latency. (hud_showlag 1 is on for netgames, 2 is always on) --- src/d_net.cpp | 34 +++++++++++++++++++++++---- src/d_net.h | 1 + src/g_shared/shared_hud.cpp | 47 +++++++++++++++++++++++++++++++++++++ wadsrc/static/menudef.txt | 8 +++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 9ba6eefa0..8bbe70e43 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -117,6 +117,7 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; int ticdup; +int arb_maketic; // Arbitrators maketic difference void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -151,6 +152,8 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } +CVAR(Bool, net_loadbalance, true, CVAR_SERVERINFO) + // [RH] Special "ticcmds" get stored in here static struct TicSpecial { @@ -347,6 +350,9 @@ int NetbufferSize () k += netbuffer[k] + 1; } + if (doomcom.remotenode == nodeforplayer[Net_Arbitrator]) + k++; + if (netbuffer[0] & NCMD_MULTI) { count = netbuffer[k]; @@ -570,6 +576,9 @@ bool HGetPacket (void) if (doomcom.datalength != NetbufferSize ()) { + Printf("Bad packet length %i (calculated %i)\n", + doomcom.datalength, NetbufferSize()); + if (debugfile) fprintf (debugfile,"---bad packet length %i (calculated %i)\n", doomcom.datalength, NetbufferSize()); @@ -782,6 +791,11 @@ void GetPackets (void) } } + if (netconsole == Net_Arbitrator) + { + arb_maketic = netbuffer[k++]; + } + playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) { @@ -1190,6 +1204,12 @@ void NetUpdate (void) } } + if (consoleplayer == Net_Arbitrator) + { + // The number of tics we just made should be removed from the count. + netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); + } + if (numtics > 0) { int l; @@ -1298,10 +1318,16 @@ void NetUpdate (void) // very jerky. The way I have it written right now basically means // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. - - if (NetMode != NET_PacketServer) + int average = 0; + if (net_loadbalance) + average = (((maketic - newtics - gametic) / ticdup) + arb_maketic) / 2; + if (NetMode == NET_PeerToPeer) { - mastertics = nettics[nodeforplayer[Net_Arbitrator]]; + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; + } + else if (NetMode == NET_PacketServer) + { + mastertics = mastertics + average; } if (nettics[0] <= mastertics) { @@ -2713,7 +2739,7 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - + Printf("%d (%d ms) arbitrator buffer time\n", arb_maketic * ticdup, (arb_maketic * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 4cd3e66f5..8d352b25d 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -143,6 +143,7 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; +extern int arb_maketic; extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 2daeff7a8..67a6bd82c 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -37,6 +37,7 @@ // copy would be. #include "doomtype.h" +#include "doomdef.h" #include "v_video.h" #include "gi.h" #include "c_cvars.h" @@ -48,6 +49,7 @@ #include "p_local.h" #include "doomstat.h" #include "g_level.h" +#include "d_net.h" #include @@ -73,6 +75,7 @@ CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD +CVAR (Int , hud_showlag, 0, CVAR_ARCHIVE); // Show input latency (maketic - gametic difference) CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green @@ -917,6 +920,49 @@ static void DrawTime() DrawHudText(SmallFont, hud_timecolor, timeString, hudwidth - width, height, FRACUNIT); } +//--------------------------------------------------------------------------- +// +// Draw in-game latency +// +//--------------------------------------------------------------------------- + +static void DrawLatency() +{ + if (hud_showlag <= 0 || + (hud_showlag == 1 && !netgame) || + hud_showlag > 2) + { + return; + } + + int localdelay = (maketic - gametic) * (1000 / TICRATE); + int arbitratordelay = (arb_maketic * ticdup) * (1000 / TICRATE); + int color = CR_GREEN; + if (MAX(localdelay, arbitratordelay) > 200) + { + color = CR_YELLOW; + } + if (MAX(localdelay, arbitratordelay) > 400) + { + color = CR_ORANGE; + } + if (MAX(localdelay, arbitratordelay) >= ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE)) + { + color = CR_RED; + } + + char tempstr[32]; + + const int millis = (level.time % TICRATE) * (1000 / TICRATE); + mysnprintf(tempstr, sizeof(tempstr), "a:%dms - l:%dms", arbitratordelay, localdelay); + + const int characterCount = strlen(tempstr); + const int width = SmallFont->GetCharWidth('0') * characterCount + 2; // small offset from screen's border + const int height = SmallFont->GetHeight() * 2; + + DrawHudText(SmallFont, color, tempstr, hudwidth - width, height, FRACUNIT); +} + //--------------------------------------------------------------------------- // @@ -982,6 +1028,7 @@ void DrawHUD() if (idmypos) DrawCoordinates(CPlayer); DrawTime(); + DrawLatency(); } else { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 324c94e6b..66d7537f6 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -805,6 +805,13 @@ OptionValue "AltHUDTime" 9, "System" } +OptionValue "AltHUDLag" +{ + 0, "Off" + 1, "Netgames only" + 2, "Always" +} + OptionMenu "AltHUDOptions" { Title "Alternative HUD" @@ -819,6 +826,7 @@ OptionMenu "AltHUDOptions" Option "Show weapons", "hud_showweapons", "OnOff" Option "Show time", "hud_showtime", "AltHUDTime" Option "Time color", "hud_timecolor", "TextColors" + Option "Show network latency", "hud_showlag", "AltHUDLag" Slider "Red ammo display below %", "hud_ammo_red", 0, 100, 1, 0 Slider "Yellow ammo display below %", "hud_ammo_yellow", 0, 100, 1, 0 Slider "Red health display below", "hud_health_red", 0, 100, 1, 0 From 97586c317ebaba058d25e981b968b90787516a83 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:08:17 +1200 Subject: [PATCH 077/313] Further refinements to network balancing - Added delay times of all players to the scoreboard - Removed balancing from packet-server (tried it, didn't work) - Calculations remove an extra tic to account for possible bias --- src/d_net.cpp | 36 ++++++++++++++++-------------------- src/d_net.h | 4 +++- src/g_shared/shared_hud.cpp | 4 ++-- src/hu_scores.cpp | 26 +++++++++++++++++++------- wadsrc/static/language.enu | 1 + 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 8bbe70e43..ff2474464 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -110,6 +110,7 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; +int netdelay[MAXNETNODES]; // Used for storing network delay times. int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -117,7 +118,6 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; int ticdup; -int arb_maketic; // Arbitrators maketic difference void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -152,7 +152,7 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Bool, net_loadbalance, true, CVAR_SERVERINFO) +CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) // [RH] Special "ticcmds" get stored in here static struct TicSpecial @@ -350,8 +350,8 @@ int NetbufferSize () k += netbuffer[k] + 1; } - if (doomcom.remotenode == nodeforplayer[Net_Arbitrator]) - k++; + // Network delay byte + k++; if (netbuffer[0] & NCMD_MULTI) { @@ -791,10 +791,8 @@ void GetPackets (void) } } - if (netconsole == Net_Arbitrator) - { - arb_maketic = netbuffer[k++]; - } + // Pull current network delay from node + netdelay[netnode] = netbuffer[k++]; playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) @@ -1204,11 +1202,9 @@ void NetUpdate (void) } } - if (consoleplayer == Net_Arbitrator) - { - // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); - } + // Send current network delay + // The number of tics we just made should be removed from the count. + netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); if (numtics > 0) { @@ -1319,16 +1315,16 @@ void NetUpdate (void) // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. int average = 0; - if (net_loadbalance) - average = (((maketic - newtics - gametic) / ticdup) + arb_maketic) / 2; + if (NetMode == NET_PeerToPeer) { + // Try to guess ahead the time it takes to send responses to the arbitrator + // [ED850] It seems that there is a bias based on network adaption (which the netwrok arbitrator doesn't do), + // so I have set this up to assume one less tic, which appears to balance it out. + if (net_ticbalance) + average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; } - else if (NetMode == NET_PacketServer) - { - mastertics = mastertics + average; - } if (nettics[0] <= mastertics) { gametime--; @@ -2739,7 +2735,7 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - Printf("%d (%d ms) arbitrator buffer time\n", arb_maketic * ticdup, (arb_maketic * ticdup) * (1000 / TICRATE)); + Printf("%d (%d ms) arbitrator buffer time\n", ARBITRATOR_DELAY * ticdup, (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 8d352b25d..1e019031c 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -143,7 +143,9 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; -extern int arb_maketic; +extern int netdelay[MAXNETNODES]; +extern int nodeforplayer[MAXPLAYERS]; +#define ARBITRATOR_DELAY netdelay[nodeforplayer[Net_Arbitrator]] extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 67a6bd82c..e83e2e988 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -935,8 +935,8 @@ static void DrawLatency() return; } - int localdelay = (maketic - gametic) * (1000 / TICRATE); - int arbitratordelay = (arb_maketic * ticdup) * (1000 / TICRATE); + int localdelay = (netdelay[0] * ticdup) * (1000 / TICRATE); + int arbitratordelay = (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE); int color = CR_GREEN; if (MAX(localdelay, arbitratordelay) > 200) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index af5d1bbaa..7d6c5fb1e 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -48,6 +48,7 @@ #include "d_player.h" #include "hu_stuff.h" #include "gstrings.h" +#include "d_net.h" // MACROS ------------------------------------------------------------------ @@ -61,7 +62,7 @@ static void HU_DoDrawScores (player_t *, player_t *[MAXPLAYERS]); static void HU_DrawTimeRemaining (int y); -static void HU_DrawPlayer (player_t *, bool, int, int, int, int, int, int, int, int); +static void HU_DrawPlayer(player_t *, bool, int, int, int, int, int, int, int, int, int); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -228,7 +229,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER int maxnamewidth, maxscorewidth, maxiconheight; int numTeams = 0; int x, y, ypadding, bottom; - int col2, col3, col4; + int col2, col3, col4, col5; if (deathmatch) { @@ -309,12 +310,14 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER const char *text_color = GStrings("SCORE_COLOR"), *text_frags = GStrings(deathmatch ? "SCORE_FRAGS" : "SCORE_KILLS"), - *text_name = GStrings("SCORE_NAME"); + *text_name = GStrings("SCORE_NAME"), + *text_delay = GStrings("SCORE_DELAY"); col2 = (SmallFont->StringWidth(text_color) + 8) * CleanXfac; col3 = col2 + (SmallFont->StringWidth(text_frags) + 8) * CleanXfac; col4 = col3 + maxscorewidth * CleanXfac; - x = (SCREENWIDTH >> 1) - ((maxnamewidth * CleanXfac + col4) >> 1); + col5 = col4 + (maxnamewidth + 8) * CleanXfac; + x = (SCREENWIDTH >> 1) - (((SmallFont->StringWidth(text_delay) * CleanXfac) + col5) >> 1); screen->DrawText (SmallFont, color, x, y, text_color, DTA_CleanNoMove, true, TAG_DONE); @@ -325,6 +328,9 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER screen->DrawText (SmallFont, color, x + col4, y, text_name, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, x + col5, y, text_delay, + DTA_CleanNoMove, true, TAG_DONE); + y += height + 6 * CleanYfac; bottom -= height; @@ -332,7 +338,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER { if (playeringame[sortedplayers[i] - players]) { - HU_DrawPlayer (sortedplayers[i], player==sortedplayers[i], x, col2, col3, col4, maxnamewidth, y, ypadding, lineheight); + HU_DrawPlayer(sortedplayers[i], player == sortedplayers[i], x, col2, col3, col4, col5, maxnamewidth, y, ypadding, lineheight); y += lineheight + CleanYfac; } } @@ -377,7 +383,7 @@ static void HU_DrawTimeRemaining (int y) // //========================================================================== -static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int maxnamewidth, int y, int ypadding, int height) +static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int col5, int maxnamewidth, int y, int ypadding, int height) { int color; char str[80]; @@ -387,12 +393,13 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, // The teamplay mode uses colors to show teams, so we need some // other way to do highlighting. And it may as well be used for // all modes for the sake of consistancy. - screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col4 + (maxnamewidth + 24)*CleanXfac, height + 2); + screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col5 + (maxnamewidth + 24)*CleanXfac, height + 2); } col2 += col1; col3 += col1; col4 += col1; + col5 += col1; color = HU_GetRowColor(player, highlight); HU_DrawColorBar(col1, y, height, (int)(player - players)); @@ -412,6 +419,11 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); + mysnprintf(str, countof(str), "%d", (netdelay[nodeforplayer[(int)(player - players)]] * ticdup) * (1000 / TICRATE)); + + screen->DrawText(SmallFont, color, col5, y + ypadding, str, + DTA_CleanNoMove, true, TAG_DONE); + if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ()) { FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()]; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 4990cb417..a92e22dfe 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -836,6 +836,7 @@ SCORE_BONUS = "BONUS"; SCORE_COLOR = "COLOR"; SCORE_SECRET = "SECRET"; SCORE_NAME = "NAME"; +SCORE_DELAY = "DELAY(ms)"; SCORE_KILLS = "KILLS"; SCORE_FRAGS = "FRAGS"; SCORE_DEATHS = "DEATHS"; From ded3bc73be14c7606c1970a8cffc0c04bee40b5a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:46:48 +1200 Subject: [PATCH 078/313] New extratic method - Extratic changed to server var, that can be changed at any time (net_extratic) - Added net_extratic 2 which resends all unconfirmed tics - Corrected bad variable typo in delay reporting --- src/d_net.cpp | 42 +++++++++++++++++++++++++++++------------- src/d_net.h | 1 - src/i_net.cpp | 5 ----- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index ff2474464..466a56c32 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -117,7 +117,7 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; -int ticdup; +int ticdup; void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -153,6 +153,17 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) +CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) +{ + if (self < 0) + { + self = 0; + } + else if (self > 3) + { + I_SetFPSLimit(-1); + } +} // [RH] Special "ticcmds" get stored in here static struct TicSpecial @@ -1166,7 +1177,15 @@ void NetUpdate (void) if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); - resendto[i] = MAX (0, lowtic - doomcom.extratics); + switch (net_extratic) + { + case 0: + default: + resendto[i] = MAX(0, lowtic); break; + case 1: resendto[i] = MAX(0, lowtic - 1); break; + case 2: resendto[i] = MAX(0, lowtic - (lowtic - nettics[i])); break; + case 3: resendto[i] = MAX(0, lowtic - (BACKUPTICS / 2 - 1)); break; + } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) { @@ -1204,7 +1223,7 @@ void NetUpdate (void) // Send current network delay // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); + netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); if (numtics > 0) { @@ -1319,7 +1338,7 @@ void NetUpdate (void) if (NetMode == NET_PeerToPeer) { // Try to guess ahead the time it takes to send responses to the arbitrator - // [ED850] It seems that there is a bias based on network adaption (which the netwrok arbitrator doesn't do), + // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. if (net_ticbalance) average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; @@ -1368,9 +1387,8 @@ void NetUpdate (void) // // 0 One byte set to NCMD_SETUP+2 // 1 One byte for ticdup setting -// 2 One byte for extratics setting -// 3 One byte for NetMode setting -// 4 String with starting map's name +// 2 One byte for NetMode setting +// 3 String with starting map's name // . Four bytes for the RNG seed // . Stream containing remaining game info // @@ -1451,10 +1469,9 @@ bool DoArbitrate (void *userdata) data->gotsetup[0] = 0x80; ticdup = doomcom.ticdup = netbuffer[1]; - doomcom.extratics = netbuffer[2]; - NetMode = netbuffer[3]; + NetMode = netbuffer[2]; - stream = &netbuffer[4]; + stream = &netbuffer[3]; s = ReadString (&stream); startmap = s; delete[] s; @@ -1519,9 +1536,8 @@ bool DoArbitrate (void *userdata) { netbuffer[0] = NCMD_SETUP+2; netbuffer[1] = (BYTE)doomcom.ticdup; - netbuffer[2] = (BYTE)doomcom.extratics; - netbuffer[3] = NetMode; - stream = &netbuffer[4]; + netbuffer[2] = NetMode; + stream = &netbuffer[3]; WriteString (startmap, &stream); WriteLong (rngseed, &stream); C_WriteCVars (&stream, CVAR_SERVERINFO, true); diff --git a/src/d_net.h b/src/d_net.h index 1e019031c..64c4e0f43 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -70,7 +70,6 @@ struct doomcom_t // info common to all nodes SWORD numnodes; // console is always node 0. SWORD ticdup; // 1 = no duplication, 2-5 = dup for slow nets - SWORD extratics; // 1 = send a backup tic in every packet #ifdef DJGPP SWORD pad[5]; // keep things aligned for DOS drivers #endif diff --git a/src/i_net.cpp b/src/i_net.cpp index 889688b48..e03c2c0e8 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -938,11 +938,6 @@ bool I_InitNetwork (void) doomcom.ticdup = 1; } - if (Args->CheckParm ("-extratic")) - doomcom.extratics = 1; - else - doomcom.extratics = 0; - v = Args->CheckValue ("-port"); if (v) { From 4041f56cc8d9d9105ff86e7d1f7b6d1b0ad00900 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:47:01 +1200 Subject: [PATCH 079/313] Bumped net version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index c8917212d..92b1816a1 100644 --- a/src/version.h +++ b/src/version.h @@ -51,7 +51,7 @@ const char *GetVersionString(); // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 230 +#define NETGAMEVERSION 231 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to From 78f8bf19d2f60ffef414a30ab004c082599d658a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 22:45:51 +1200 Subject: [PATCH 080/313] Extratics mode 3 didn't work well - Removed Extratics mode 3 - Fixed a typo --- src/d_net.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 466a56c32..84bfe5856 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -159,9 +159,9 @@ CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) { self = 0; } - else if (self > 3) + else if (self > 2) { - I_SetFPSLimit(-1); + self = 2; } } @@ -1183,8 +1183,7 @@ void NetUpdate (void) default: resendto[i] = MAX(0, lowtic); break; case 1: resendto[i] = MAX(0, lowtic - 1); break; - case 2: resendto[i] = MAX(0, lowtic - (lowtic - nettics[i])); break; - case 3: resendto[i] = MAX(0, lowtic - (BACKUPTICS / 2 - 1)); break; + case 2: resendto[i] = MAX(0, nettics[i]); break; } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) From c585eee82ba971ca92ffe18d53dd164238f38673 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 22:55:13 +1200 Subject: [PATCH 081/313] Don't need to MAX() mode 2 --- src/d_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 84bfe5856..7de859269 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1183,7 +1183,7 @@ void NetUpdate (void) default: resendto[i] = MAX(0, lowtic); break; case 1: resendto[i] = MAX(0, lowtic - 1); break; - case 2: resendto[i] = MAX(0, nettics[i]); break; + case 2: resendto[i] = nettics[i]; break; } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) From 6cd4fd81518f8e608250cf7c5004208164653392 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 23:13:41 +1200 Subject: [PATCH 082/313] Added menu options for network settings --- wadsrc/static/menudef.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 66d7537f6..496f1e066 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -341,6 +341,7 @@ OptionMenu "OptionsMenu" Submenu "Automap Options", "AutomapOptions" Submenu "HUD Options", "HUDOptions" Submenu "Miscellaneous Options", "MiscOptions" + Submenu "Network Options", "NetworkOptions" Submenu "Sound Options", "SoundOptions" Submenu "Display Options", "VideoOptions" Submenu "Set video mode", "VideoModeMenu" @@ -1599,3 +1600,27 @@ OptionMenu VideoModeMenu class VideoModeMenu } +/*======================================= + * + * Network options menu + * + *=======================================*/ + +OptionMenu NetworkOptions +{ + Title "NETWORK OPTIONS" + StaticText "Local options", 1 + Option "Movement prediction", "cl_noprediction", "OffOn" + StaticText " " + StaticText "Host options", 1 + Option "Extra Tics", "net_extratic", "ExtraTicMode" + Option "Latency balancing", "net_ticbalance", "OnOff" + +} + +OptionValue ExtraTicMode +{ + 0, "None" + 1, "1" + 2, "All unacknowledged" +} From 4db8b3e421d2018ac9d57abf5cbdf249adca4004 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 19 Aug 2014 22:30:33 +1200 Subject: [PATCH 083/313] Made delay updates less erratic --- src/d_net.cpp | 15 +++++++++++---- src/d_net.h | 3 +-- src/g_shared/shared_hud.cpp | 8 +++++--- src/hu_scores.cpp | 9 ++++++++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 7de859269..6e2877495 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -110,7 +110,7 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; -int netdelay[MAXNETNODES]; // Used for storing network delay times. +int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times. int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -803,7 +803,7 @@ void GetPackets (void) } // Pull current network delay from node - netdelay[netnode] = netbuffer[k++]; + netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++]; playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) @@ -1340,7 +1340,15 @@ void NetUpdate (void) // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. if (net_ticbalance) - average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; + { + for (i = 0; i < BACKUPTICS; i++) + { + average += netdelay[nodeforplayer[Net_Arbitrator]][i]; + } + average /= BACKUPTICS; + average = ((netdelay[0][nettics[0] % BACKUPTICS] + average) / 2) - 1; + } + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; } if (nettics[0] <= mastertics) @@ -2750,7 +2758,6 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - Printf("%d (%d ms) arbitrator buffer time\n", ARBITRATOR_DELAY * ticdup, (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 64c4e0f43..07921a301 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -142,9 +142,8 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; -extern int netdelay[MAXNETNODES]; +extern int netdelay[MAXNETNODES][BACKUPTICS]; extern int nodeforplayer[MAXPLAYERS]; -#define ARBITRATOR_DELAY netdelay[nodeforplayer[Net_Arbitrator]] extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index e83e2e988..110675791 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -934,9 +934,11 @@ static void DrawLatency() { return; } - - int localdelay = (netdelay[0] * ticdup) * (1000 / TICRATE); - int arbitratordelay = (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE); + int i, localdelay = 0, arbitratordelay = 0; + for (i = 0; i < BACKUPTICS; i++) localdelay += netdelay[0][i]; + for (i = 0; i < BACKUPTICS; i++) arbitratordelay += netdelay[nodeforplayer[Net_Arbitrator]][i]; + localdelay = ((localdelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); + arbitratordelay = ((arbitratordelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); int color = CR_GREEN; if (MAX(localdelay, arbitratordelay) > 200) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index 7d6c5fb1e..8b9a7d0ad 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -419,7 +419,14 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); - mysnprintf(str, countof(str), "%d", (netdelay[nodeforplayer[(int)(player - players)]] * ticdup) * (1000 / TICRATE)); + int avgdelay = 0; + for (int i = 0; i < BACKUPTICS; i++) + { + avgdelay += netdelay[nodeforplayer[(int)(player - players)]][i]; + } + avgdelay /= BACKUPTICS; + + mysnprintf(str, countof(str), "%d", (avgdelay * ticdup) * (1000 / TICRATE)); screen->DrawText(SmallFont, color, col5, y + ypadding, str, DTA_CleanNoMove, true, TAG_DONE); From 42e6737803adfd3984ea3481667c0d668165f92e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 19 Aug 2014 22:31:45 +1200 Subject: [PATCH 084/313] Player prediction now updates listener --- src/p_user.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_user.cpp b/src/p_user.cpp index fbe24cc83..9c3d7f321 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2728,6 +2728,8 @@ void P_PredictPlayer (player_t *player) P_PlayerThink (player); player->mo->Tick (); } + + S_UpdateSounds(players[consoleplayer].camera); // move positional sounds } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); From c661da299592b16981425edbf06914e5f4dcbc2f Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Wed, 20 Aug 2014 19:41:07 +1200 Subject: [PATCH 085/313] Stop negative tic counts from corrupting messages --- src/d_net.cpp | 2 +- src/i_net.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 6e2877495..dfee7460e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1173,7 +1173,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = lowtic - realstart; + numtics = MAX(0, lowtic - realstart); if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); diff --git a/src/i_net.cpp b/src/i_net.cpp index e03c2c0e8..3ec9d7881 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -208,11 +208,11 @@ void PacketSend (void) { I_FatalError("Netbuffer overflow!"); } + assert(!(doomcom.data[0] & NCMD_COMPRESSED)); uLong size = TRANSMIT_SIZE - 1; if (doomcom.datalength >= 10) { - assert(!(doomcom.data[0] & NCMD_COMPRESSED)); TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED; c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9); size += 1; From 530f4746731ac6d13c7b86646b84f4b996dd7d1a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 21 Aug 2014 16:31:29 +1200 Subject: [PATCH 086/313] Shifted netmode reporting for guests - The netmode is now reported after a guest has received it. - Minor code cleanup --- src/d_net.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index dfee7460e..fb6e9722d 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1173,7 +1173,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = MAX(0, lowtic - realstart); + numtics = lowtic - realstart; if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); @@ -1181,12 +1181,12 @@ void NetUpdate (void) { case 0: default: - resendto[i] = MAX(0, lowtic); break; + resendto[i] = lowtic; break; case 1: resendto[i] = MAX(0, lowtic - 1); break; case 2: resendto[i] = nettics[i]; break; } - if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) + if (numtics <= 0 && resendOnly && !remoteresend[i] && nettics[i]) { continue; } @@ -1692,15 +1692,18 @@ void D_CheckNetGame (void) consoleplayer = doomcom.consoleplayer; - v = Args->CheckValue ("-netmode"); - if (v != NULL) + if (consoleplayer == Net_Arbitrator) { - NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer; - } - if (doomcom.numnodes > 1) - { - Printf ("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", - v != NULL ? "forced" : "auto"); + v = Args->CheckValue("-netmode"); + if (v != NULL) + { + NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer; + } + if (doomcom.numnodes > 1) + { + Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", + v != NULL ? "forced" : "auto"); + } } // [RH] Setup user info @@ -1728,6 +1731,11 @@ void D_CheckNetGame (void) for (i = 0; i < doomcom.numnodes; i++) nodeingame[i] = true; + if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1) + { + Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server"); + } + Printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom.numplayers, doomcom.numnodes); } From ad0a1ad86576a5b5fd0205f9eb6de895dc4feccd Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 21 Aug 2014 16:41:59 +1200 Subject: [PATCH 087/313] Readded -extratic for compatibility with launchers --- src/d_net.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/d_net.cpp b/src/d_net.cpp index fb6e9722d..06886e175 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1704,6 +1704,11 @@ void D_CheckNetGame (void) Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", v != NULL ? "forced" : "auto"); } + + if (Args->CheckParm("-extratic")) + { + net_extratic = 1; + } } // [RH] Setup user info From 53b6e7d4d537c204646012bdb03b8f607402f6ee Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sat, 23 Aug 2014 15:17:11 +1200 Subject: [PATCH 088/313] Added silent line teleport prediction - Allow activation of line teleport specials during prediction - Moved prediction functions to improve uncapped framerates --- src/d_main.cpp | 4 ++-- src/p_map.cpp | 24 +++++++++++++++--------- src/p_spec.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ src/p_spec.h | 1 + src/p_user.cpp | 7 +++++-- src/r_utility.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/r_utility.h | 2 ++ 7 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index ab33bfaec..8a8751041 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -756,9 +756,9 @@ void D_Display () } screen->SetBlendingRect(viewwindowx, viewwindowy, viewwindowx + viewwidth, viewwindowy + viewheight); - P_PredictPlayer(&players[consoleplayer]); + //P_PredictPlayer(&players[consoleplayer]); Renderer->RenderView(&players[consoleplayer]); - P_UnPredictPlayer(); + //P_UnPredictPlayer(); if ((hw2d = screen->Begin2D(viewactive))) { // Redraw everything every frame when using 2D accel diff --git a/src/p_map.cpp b/src/p_map.cpp index 2274e7571..f0087e220 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -380,7 +380,9 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra // ... and some items can never be telefragged while others will be telefragged by everything that teleports upon them. if ((StompAlwaysFrags && !(th->flags6 & MF6_NOTELEFRAG)) || (th->flags7 & MF7_ALWAYSTELEFRAG)) { - P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS); + // Don't actually damage if predicting a teleport + if (thing->player == NULL || !(thing->player->cheats & CF_PREDICTING)) + P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS); continue; } return false; @@ -1981,13 +1983,6 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, thing->AdjustFloorClip(); } - // [RH] Don't activate anything if just predicting - if (thing->player && (thing->player->cheats & CF_PREDICTING)) - { - thing->flags6 &= ~MF6_INTRYMOVE; - return true; - } - // if any special lines were hit, do the effect if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) { @@ -1998,7 +1993,11 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, oldside = P_PointOnLineSide(oldx, oldy, ld); if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER)) { - if (thing->player) + if (thing->player && (thing->player->cheats & CF_PREDICTING)) + { + P_PredictLine(ld, thing, oldside, SPAC_Cross); + } + else if (thing->player) { P_ActivateLine(ld, thing, oldside, SPAC_Cross); } @@ -2024,6 +2023,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, } } + // [RH] Don't activate anything if just predicting + if (thing->player && (thing->player->cheats & CF_PREDICTING)) + { + thing->flags6 &= ~MF6_INTRYMOVE; + return true; + } + // [RH] Check for crossing fake floor/ceiling newsec = thing->Sector; if (newsec->heightsec && oldsec->heightsec && newsec->SecActTarget) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 00ed322c0..4ac1c59d9 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -73,6 +73,7 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); void P_SetupPortals(); +EXTERN_CVAR(Bool, cl_predict_specials) IMPLEMENT_POINTY_CLASS (DScroller) DECLARE_POINTER (m_Interpolations[0]) @@ -408,6 +409,47 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) return true; } +//============================================================================ +// +// P_PredictLine +// +//============================================================================ + +bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType) +{ + int lineActivation; + INTBOOL buttonSuccess; + BYTE special; + + // Only predict a very specifc section of specials + if (line->special != Teleport_Line) + { + return false; + } + + if (!P_TestActivateLine(line, mo, side, activationType) || !cl_predict_specials) + { + return false; + } + + if (line->locknumber > 0) return false; + lineActivation = line->activation; + buttonSuccess = false; + buttonSuccess = P_ExecuteSpecial(line->special, + line, mo, side == 1, line->args[0], + line->args[1], line->args[2], + line->args[3], line->args[4]); + + special = line->special; + + // end of changed code + if (developer && buttonSuccess) + { + Printf("Line special %d predicted on line %i\n", special, int(line - lines)); + } + return true; +} + // // P_PlayerInSpecialSector // Called every tic frame diff --git a/src/p_spec.h b/src/p_spec.h index c9bb6eded..906a38a8c 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -166,6 +166,7 @@ void P_UpdateSpecials (void); // when needed bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType); bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType); +bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType); void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL); void P_PlayerOnSpecialFlat (player_t *player, int floorType); diff --git a/src/p_user.cpp b/src/p_user.cpp index 9c3d7f321..7b049a2df 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -62,6 +62,7 @@ static FRandom pr_skullpop ("SkullPop"); // Variables for prediction CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; static TArray PredictionTouchingSectorsBackup; @@ -2722,14 +2723,16 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; + bool NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { + if (!NoInterpolateOld) + R_RebuildViewInterpolation(player); + player->cmd = localcmds[i % LOCALCMDTICS]; P_PlayerThink (player); player->mo->Tick (); } - - S_UpdateSounds(players[consoleplayer].camera); // move positional sounds } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 5bf38ad26..19d446b5b 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -729,6 +729,46 @@ void R_ClearPastViewer (AActor *actor) } } +//========================================================================== +// +// R_RebuildViewInterpolation +// +//========================================================================== + +void R_RebuildViewInterpolation(player_t *player) +{ + InterpolationViewer *iview; + if (NoInterpolateView) + { + if (player != NULL && player->camera != NULL) + { + iview = FindPastViewer(player->camera); + } + + if (iview == NULL) + return; + + NoInterpolateView = false; + iview->oviewx = iview->nviewx; + iview->oviewy = iview->nviewy; + iview->oviewz = iview->nviewz; + iview->oviewpitch = iview->nviewpitch; + iview->oviewangle = iview->nviewangle; + } +} + +//========================================================================== +// +// R_GetViewInterpolationStatus +// +//========================================================================== + +bool R_GetViewInterpolationStatus() +{ + return NoInterpolateView; +} + + //========================================================================== // // R_SetupFrame diff --git a/src/r_utility.h b/src/r_utility.h index 85ca7c410..2d9aac086 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -61,6 +61,8 @@ inline angle_t R_PointToAngle (fixed_t x, fixed_t y) { return R_PointToAngle2 (v subsector_t *R_PointInSubsector (fixed_t x, fixed_t y); fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy); void R_ResetViewInterpolation (); +void R_RebuildViewInterpolation(player_t *player); +bool R_GetViewInterpolationStatus(); void R_SetViewSize (int blocks); void R_SetFOV (float fov); float R_GetFOV (); From 9e68983b4469906102b7d810fe02e1756d5a3540 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 26 Aug 2014 22:29:39 +1200 Subject: [PATCH 089/313] Added standard teleports to line prediction - Added standard teleports to line prediction - Menudef for line special prediction --- src/p_spec.cpp | 3 ++- src/p_teleport.cpp | 27 +++++++++++++++++---------- wadsrc/static/menudef.txt | 1 + 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 4ac1c59d9..d58062a7e 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -422,7 +422,8 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType) BYTE special; // Only predict a very specifc section of specials - if (line->special != Teleport_Line) + if (line->special != Teleport_Line && + line->special != Teleport) { return false; } diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 50470d9eb..b64c04a5a 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -96,6 +96,8 @@ void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid) bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, bool useFog, bool sourceFog, bool keepOrientation, bool bHaltVelocity, bool keepHeight) { + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); + fixed_t oldx; fixed_t oldy; fixed_t oldz; @@ -181,7 +183,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, angle = thing->angle; } // Spawn teleport fog at source and destination - if (sourceFog) + if (sourceFog && !predicting) { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; AActor *fog = Spawn (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE); @@ -189,11 +191,14 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, } if (useFog) { - fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; - an = angle >> ANGLETOFINESHIFT; - AActor *fog = Spawn (x + 20*finecosine[an], - y + 20*finesine[an], thing->z + fogDelta, ALLOW_REPLACE); - fog->target = thing; + if (!predicting) + { + fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; + an = angle >> ANGLETOFINESHIFT; + AActor *fog = Spawn(x + 20 * finecosine[an], + y + 20 * finesine[an], thing->z + fogDelta, ALLOW_REPLACE); + fog->target = thing; + } if (thing->player) { // [RH] Zoom player's field of vision @@ -226,7 +231,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, return true; } -static AActor *SelectTeleDest (int tid, int tag) +static AActor *SelectTeleDest (int tid, int tag, bool norandom) { AActor *searcher; @@ -276,7 +281,7 @@ static AActor *SelectTeleDest (int tid, int tag) } else { - if (count != 1) + if (count != 1 && !norandom) { count = 1 + (pr_teleport() % count); } @@ -323,6 +328,8 @@ static AActor *SelectTeleDest (int tid, int tag) bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight) { + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); + AActor *searcher; fixed_t z; angle_t angle = 0; @@ -342,7 +349,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool { // Don't teleport if hit back of line, so you can get out of teleporter. return 0; } - searcher = SelectTeleDest (tid, tag); + searcher = SelectTeleDest(tid, tag, predicting); if (searcher == NULL) { return false; @@ -390,7 +397,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool thing->velx = FixedMul(velx, c) - FixedMul(vely, s); thing->vely = FixedMul(vely, c) + FixedMul(velx, s); } - if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing) + if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing && !predicting) { thing->player->mo->PlayIdle (); } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 496f1e066..6d313faf1 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1611,6 +1611,7 @@ OptionMenu NetworkOptions Title "NETWORK OPTIONS" StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" + Option "Predict line actions", "cl_predict_specials", "OnOff" StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 8f82243f4c5a3ca729aa5e400b3447b64d8965e1 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 26 Aug 2014 22:31:42 +1200 Subject: [PATCH 090/313] Don't balance if already the slowest node - Network balancing shouldn't be run if already too far behind - Don't save network settings - Added net_fakelatency for debug builds --- src/d_net.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 13 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 06886e175..eb8cb4eeb 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -152,8 +152,8 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) -CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) +CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE) +CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE) { if (self < 0) { @@ -165,6 +165,19 @@ CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) } } +#ifdef _DEBUG +CVAR(Int, net_fakelatency, 0, 0); + +struct PacketStore +{ + int timer; + doomcom_t message; +}; + +static TArray InBuffer; +static TArray OutBuffer; +#endif + // [RH] Special "ticcmds" get stored in here static struct TicSpecial { @@ -504,7 +517,30 @@ void HSendPacket (int node, int len) doomcom.remotenode = node; doomcom.datalength = len; - I_NetCmd (); +#ifdef _DEBUG + if (net_fakelatency / 2 > 0) + { + PacketStore store; + store.message = doomcom; + store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE)); + OutBuffer.Push(store); + } + else + I_NetCmd(); + + for (unsigned int i = 0; i < OutBuffer.Size(); i++) + { + if (OutBuffer[i].timer <= I_GetTime(false)) + { + doomcom = OutBuffer[i].message; + I_NetCmd(); + OutBuffer.Delete(i); + i = -1; + } + } +#else + I_NetCmd(); +#endif } // @@ -526,12 +562,42 @@ bool HGetPacket (void) if (demoplayback) return false; - + doomcom.command = CMD_GET; I_NetCmd (); + +#ifdef _DEBUG + if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1) + { + PacketStore store; + store.message = doomcom; + store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE)); + InBuffer.Push(store); + doomcom.remotenode = -1; + } if (doomcom.remotenode == -1) + { + bool gotmessage = false; + for (unsigned int i = 0; i < InBuffer.Size(); i++) + { + if (InBuffer[i].timer <= I_GetTime(false)) + { + doomcom = InBuffer[i].message; + InBuffer.Delete(i); + gotmessage = true; + break; + } + } + if (!gotmessage) + return false; + } +#else + if (doomcom.remotenode == -1) + { return false; + } +#endif if (debugfile) { @@ -1173,7 +1239,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = lowtic - realstart; + numtics = MAX(0, lowtic - realstart); if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); @@ -1186,7 +1252,7 @@ void NetUpdate (void) case 2: resendto[i] = nettics[i]; break; } - if (numtics <= 0 && resendOnly && !remoteresend[i] && nettics[i]) + if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) { continue; } @@ -1332,24 +1398,49 @@ void NetUpdate (void) // very jerky. The way I have it written right now basically means // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. - int average = 0; if (NetMode == NET_PeerToPeer) { - // Try to guess ahead the time it takes to send responses to the arbitrator + // Try to guess ahead the time it takes to send responses to the slowest node // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. + int totalavg = 0; if (net_ticbalance) { - for (i = 0; i < BACKUPTICS; i++) + // We shouldn't adapt if we are already the slowest node, otherwise it just adds more latency + bool slow = true; + int nodeavg = 0; + for (i = 1; i < MAXNETNODES; i++) { - average += netdelay[nodeforplayer[Net_Arbitrator]][i]; + if (!nodeingame[i]) + continue; + + if (netdelay[i][0] > netdelay[0][0]) + { + slow = false; + break; + } + } + + if (!slow) + { + int totalnodes = 0; + for (i = 0; i < MAXNETNODES; i++) + { + if (!nodeingame[i]) + continue; + + totalnodes++; + nodeavg = 0; + for (j = 0; j < BACKUPTICS; j++) nodeavg += netdelay[i][j]; + totalavg += (nodeavg / BACKUPTICS); + } + + totalavg = (totalavg / totalnodes) - 1; } - average /= BACKUPTICS; - average = ((netdelay[0][nettics[0] % BACKUPTICS] + average) / 2) - 1; } - mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg; } if (nettics[0] <= mastertics) { @@ -1867,6 +1958,9 @@ void TryRunTics (void) { C_Ticker(); M_Ticker(); + // Repredict the player for new buffered movement + P_UnPredictPlayer(); + P_PredictPlayer(&players[consoleplayer]); } return; } @@ -1902,6 +1996,9 @@ void TryRunTics (void) { C_Ticker (); M_Ticker (); + // Repredict the player for new buffered movement + P_UnPredictPlayer(); + P_PredictPlayer(&players[consoleplayer]); return; } } @@ -1915,6 +2012,7 @@ void TryRunTics (void) // run the count tics if (counts > 0) { + P_UnPredictPlayer(); while (counts--) { if (gametic > lowtic) @@ -1934,6 +2032,7 @@ void TryRunTics (void) NetUpdate (); // check for new console commands } + P_PredictPlayer(&players[consoleplayer]); S_UpdateSounds (players[consoleplayer].camera); // move positional sounds } } From 84cf79980397df87594816452fc917fdd2da6899 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 26 Sep 2014 15:37:14 +1200 Subject: [PATCH 091/313] Final changes to balancing --- src/d_net.cpp | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index eb8cb4eeb..c0585a366 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -111,6 +111,7 @@ unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times. +int lastaverage; int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -1407,36 +1408,26 @@ void NetUpdate (void) int totalavg = 0; if (net_ticbalance) { - // We shouldn't adapt if we are already the slowest node, otherwise it just adds more latency - bool slow = true; - int nodeavg = 0; - for (i = 1; i < MAXNETNODES; i++) - { - if (!nodeingame[i]) - continue; + // We shouldn't adapt if we are already the slower then the arbitrator, otherwise it just adds more latency + int nodeavg = 0, arbavg = 0; - if (netdelay[i][0] > netdelay[0][0]) - { - slow = false; - break; - } + for (j = 0; j < BACKUPTICS; j++) + { + arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j]; + nodeavg += netdelay[0][j]; } + arbavg /= BACKUPTICS; + nodeavg /= BACKUPTICS; - if (!slow) + if (arbavg > nodeavg) { - int totalnodes = 0; - for (i = 0; i < MAXNETNODES; i++) - { - if (!nodeingame[i]) - continue; - - totalnodes++; - nodeavg = 0; - for (j = 0; j < BACKUPTICS; j++) nodeavg += netdelay[i][j]; - totalavg += (nodeavg / BACKUPTICS); - } - - totalavg = (totalavg / totalnodes) - 1; + lastaverage = totalavg = ((arbavg + nodeavg) / 2); + } + else + { + if (nodeavg > (arbavg + 2) && lastaverage > 0) + lastaverage--; + totalavg = lastaverage; } } From e25b91d5a1abf72f172a311fac0ed19882f4a1a8 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 26 Sep 2014 16:11:52 +1200 Subject: [PATCH 092/313] Cleanup --- src/d_main.cpp | 4 ++-- src/d_net.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 8a8751041..a24586bc4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -756,9 +756,9 @@ void D_Display () } screen->SetBlendingRect(viewwindowx, viewwindowy, viewwindowx + viewwidth, viewwindowy + viewheight); - //P_PredictPlayer(&players[consoleplayer]); + Renderer->RenderView(&players[consoleplayer]); - //P_UnPredictPlayer(); + if ((hw2d = screen->Begin2D(viewactive))) { // Redraw everything every frame when using 2D accel diff --git a/src/d_net.cpp b/src/d_net.cpp index c0585a366..d33f47e38 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1402,13 +1402,10 @@ void NetUpdate (void) if (NetMode == NET_PeerToPeer) { - // Try to guess ahead the time it takes to send responses to the slowest node - // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), - // so I have set this up to assume one less tic, which appears to balance it out. int totalavg = 0; if (net_ticbalance) { - // We shouldn't adapt if we are already the slower then the arbitrator, otherwise it just adds more latency + // Try to guess ahead the time it takes to send responses to the slowest node int nodeavg = 0, arbavg = 0; for (j = 0; j < BACKUPTICS; j++) @@ -1419,12 +1416,14 @@ void NetUpdate (void) arbavg /= BACKUPTICS; nodeavg /= BACKUPTICS; + // We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency if (arbavg > nodeavg) { lastaverage = totalavg = ((arbavg + nodeavg) / 2); } else { + // Allow room to guess two tics ahead if (nodeavg > (arbavg + 2) && lastaverage > 0) lastaverage--; totalavg = lastaverage; From 422e83a1b91dd749288f1314689db08f722ce340 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 23:12:25 -0500 Subject: [PATCH 093/313] - Added more A_Damage functions: - A_DamageTarget - A_DamageTracer -Both take a number for how much damage to deal and the damagetype to inflict. Negative numbers means healing. --- src/thingdef/thingdef_codeptr.cpp | 64 ++++++++++++++++++++++++++++--- wadsrc/static/actors/actor.txt | 2 + 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index aff2629e4..6c44fa79e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5007,13 +5007,67 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); - if (amount > 0) + if (amount > 0) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } +} + +//=========================================================================== +// +// A_DamageTarget (int amount, str damagetype) +// Damages the target of the actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (self->target != NULL) { - P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + + if (amount > 0) + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self->target, amount); + } } - else if (amount < 0) +} + +//=========================================================================== +// +// A_DamageTracer (int amount, str damagetype) +// Damages the tracer of the actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (self->target != NULL) { - amount = -amount; - P_GiveBody(self, amount); + + if (amount > 0) + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self->tracer, amount); + } } } \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 95d055afb..448d83f09 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -305,6 +305,8 @@ ACTOR Actor native //: Thinker 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"); + action native A_DamageTarget(int amount, name damagetype = "none"); + action native A_DamageTracer(int amount, name damagetype = "none"); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From 6586fa29fd67323978a15376fd91c5a3945938ea Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 23:56:10 -0500 Subject: [PATCH 094/313] - Added FDARI's A_JumpIf CheckClass submission. - bool CheckClass(string classname, int ptr_select = aaptr_default, bool match_superclass = false) --- src/namedef.h | 1 + src/thingdef/thingdef_function.cpp | 76 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/namedef.h b/src/namedef.h index a4c8da638..1e7fbd0d3 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -298,6 +298,7 @@ xx(Abs) xx(ACS_NamedExecuteWithResult) xx(CallACS) xx(Sqrt) +xx(CheckClass) // Various actor names which are used internally xx(MapSpot) diff --git a/src/thingdef/thingdef_function.cpp b/src/thingdef/thingdef_function.cpp index 2858e9a86..9bf80dcaa 100644 --- a/src/thingdef/thingdef_function.cpp +++ b/src/thingdef/thingdef_function.cpp @@ -43,6 +43,8 @@ #include "tarray.h" #include "thingdef.h" #include "thingdef_exp.h" +#include "actor.h" +#include "actorptrselect.h" static TMap CreatorMap; @@ -185,3 +187,77 @@ public: GLOBALFUNCTION_ADDER(Sqrt); +//========================================================================== +// +// Function: checkclass +// +//========================================================================== + +class FxGlobalFunctionCall_CheckClass : public FxGlobalFunctionCall +{ + public: + GLOBALFUNCTION_DEFINE(CheckClass); + + FxExpression *Resolve(FCompileContext& ctx) + { + CHECKRESOLVED(); + + if (!ResolveArgs(ctx, 1, 3, false)) + return NULL; + + for (int i = ArgList->Size(); i > 1;) + { + if (!(*ArgList)[--i]->ValueType.isNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter"); + delete this; + return NULL; + } + } + + switch ((*ArgList)[0]->ValueType.Type) + { + case VAL_Class: case VAL_Name:break; + default: + ScriptPosition.Message(MSG_ERROR, "actor class expected for parameter"); + delete this; + return NULL; + } + + ValueType = VAL_Float; + return this; + } + + ExpVal EvalExpression(AActor *self) + { + ExpVal ret; + ret.Type = VAL_Int; + + const PClass * checkclass; + { + ExpVal v = (*ArgList)[0]->EvalExpression(self); + checkclass = v.GetClass(); + if (!checkclass) + { + checkclass = PClass::FindClass(v.GetName()); + if (!checkclass) { ret.Int = 0; return ret; } + } + } + + bool match_superclass = false; + int pick_pointer = AAPTR_DEFAULT; + + switch (ArgList->Size()) + { + case 3: match_superclass = (*ArgList)[2]->EvalExpression(self).GetBool(); + case 2: pick_pointer = (*ArgList)[1]->EvalExpression(self).GetInt(); + } + + self = COPY_AAPTR(self, pick_pointer); + if (!self){ ret.Int = 0; return ret; } + ret.Int = match_superclass ? checkclass->IsAncestorOf(self->GetClass()) : checkclass == self->GetClass(); + return ret; + } + }; + +GLOBALFUNCTION_ADDER(CheckClass); \ No newline at end of file From 54ccf5d44d0facf13f6d1af8c5ef9531daafad1c Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Fri, 26 Sep 2014 10:52:11 +0200 Subject: [PATCH 095/313] - Fixed a possible uninitialized condition. In the function R_RebuildViewInterpolation, the pointer 'iview' was not initialized when the player or its camera were NULL, hence 'iview == NULL' was garbage. Also, the function FindPastViewer does not return NULL, hence the mentioned check is not needed at all. Just return early if the player camera does not exist. --- src/r_utility.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 19d446b5b..585e3dcf3 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -737,24 +737,20 @@ void R_ClearPastViewer (AActor *actor) void R_RebuildViewInterpolation(player_t *player) { - InterpolationViewer *iview; - if (NoInterpolateView) - { - if (player != NULL && player->camera != NULL) - { - iview = FindPastViewer(player->camera); - } + if (player == NULL || player->camera == NULL) + return; - if (iview == NULL) - return; + if (!NoInterpolateView) + return; + NoInterpolateView = false; - NoInterpolateView = false; - iview->oviewx = iview->nviewx; - iview->oviewy = iview->nviewy; - iview->oviewz = iview->nviewz; - iview->oviewpitch = iview->nviewpitch; - iview->oviewangle = iview->nviewangle; - } + InterpolationViewer *iview = FindPastViewer(player->camera); + + iview->oviewx = iview->nviewx; + iview->oviewy = iview->nviewy; + iview->oviewz = iview->nviewz; + iview->oviewpitch = iview->nviewpitch; + iview->oviewangle = iview->nviewangle; } //========================================================================== From 5b71ce6dcb631cbfa8b902e3c3eac658e22b1eb2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 26 Sep 2014 11:48:20 -0500 Subject: [PATCH 096/313] - Added FDARI's A_JumpIfHealthLower patch. Now with pointer accessibility. --- src/thingdef/thingdef_codeptr.cpp | 12 ++++++++++-- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6c44fa79e..608e1c605 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -601,11 +601,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) //========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(health, 0); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_INT(ptr_selector, 2); - if (self->health < health) ACTION_JUMP(jump); + 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! } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 448d83f09..d4c79c3de 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -204,7 +204,7 @@ ACTOR Actor native //: Thinker 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_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); + action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); action native A_JumpIfCloser(float distance, state label); action native A_JumpIfTracerCloser(float distance, state label); action native A_JumpIfMasterCloser(float distance, state label); From 0735cb95502ccebc354295e2d38c506ba86afcc0 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 00:10:31 -0500 Subject: [PATCH 097/313] - Updated many functions. - Added A_KillTarget(damagetype, int flags). - Added A_KillTracer(damagetype, int flags). - Added A_RemoveTarget. - Added A_RemoveTracer. A_Kill (Master/Target/Tracer/Children/Siblings): - KILS_FOILINVUL: foils invulnerability. - KILS_KILLMISSILES: destroys projectiles. Does not work on invulnerable projectiles without KILS_FOILINVUL, and doesn't work at all on missiles with NODAMAGE flag. - KILS_NOMONSTERS: actors that are monsters will not be killed. A_Damage (Self/Target/Master/Tracer/Children/Siblings): - DMSS_FOILINVUL: foils invulnerability. - DMSS_AFFECTARMOR: damages the actor's armor instead of bypassing it entirely. - DMSS_KILL: damages the actor by its remaining health (useful for modular DECORATE programming). - Added A_SpawnItemEx flags: - SXF_SETTARGET: sets the calling actor as the target. - SXF_SETTRACER: sets the calling actor as the tracer. Both of these functions take priority similar to SXF_SETMASTER over SXF_TRANSFERPOINTERS. --- src/thingdef/thingdef_codeptr.cpp | 363 +++++++++++++++++++++++++---- wadsrc/static/actors/actor.txt | 22 +- wadsrc/static/actors/constants.txt | 12 + 3 files changed, 342 insertions(+), 55 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 608e1c605..c257a4f70 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1765,6 +1765,8 @@ enum SIX_Flags SIXF_TRANSFERSTENCILCOL = 1 << 17, SIXF_TRANSFERALPHA = 1 << 18, SIXF_TRANSFERRENDERSTYLE = 1 << 19, + SIXF_SETTARGET = 1 << 20, + SIXF_SETTRACER = 1 << 21, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1827,7 +1829,6 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { // If this is a monster transfer all friendliness information mo->CopyFriendliness(originator, true); - if (flags & SIXF_SETMASTER) mo->master = originator; // don't let it attack you (optional)! } else if (originator->player) { @@ -1857,6 +1858,14 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { 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; @@ -2666,31 +2675,60 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) } +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + //=========================================================================== // -// A_KillMaster +// A_KillMaster(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); if (self->master != NULL) { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((self->master->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 ((!(self->master->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->master->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->master, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } //=========================================================================== // -// A_KillChildren +// A_KillChildren(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); TThinkerIterator it; AActor *mo; @@ -2699,20 +2737,38 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { if (mo->master == self) { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(mo, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } } //=========================================================================== // -// A_KillSiblings +// A_KillSiblings(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); TThinkerIterator it; AActor *mo; @@ -2723,7 +2779,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { if (mo->master == self->master && mo != self) { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(mo, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } } @@ -3537,24 +3610,47 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) ACTION_JUMP(jump); } +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; //=========================================================================== // -// A_DamageMaster (int amount, str damagetype) +// A_DamageMaster (int amount, str damagetype, int flags) // Damages the master of this child by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->master->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->master, self, self, self->master->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -3566,7 +3662,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== // -// A_DamageChildren (amount, str damagetype) +// A_DamageChildren (amount, str damagetype, int flags) // Damages the children of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3575,17 +3671,32 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) TThinkerIterator it; AActor * mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); while ( (mo = it.Next()) ) { if (mo->master == self) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) //Bypass if kill flag is present; it won't matter. It intends to kill them. { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -3600,7 +3711,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== // -// A_DamageSiblings (int amount, str damagetype) +// A_DamageSiblings (int amount, str damagetype, int flags) // Damages the siblings of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3609,24 +3720,39 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) TThinkerIterator it; AActor * mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->master != NULL) { - while ( (mo = it.Next()) ) + while ((mo = it.Next())) { if (mo->master == self->master && mo != self) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_NO_ARMOR); - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); + if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(mo, amount); + } } } } @@ -5005,45 +5131,75 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) //=========================================================================== // -// A_DamageSelf (int amount, str damagetype) +// A_DamageSelf (int amount, str damagetype, int flags) // Damages the calling actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(self->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) { - P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self, amount); + if (flags & DMSS_KILL) + { + P_DamageMobj(self, self, self, self->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } } //=========================================================================== // -// A_DamageTarget (int amount, str damagetype) +// A_DamageTarget (int amount, str damagetype, int flags) // Damages the target of the actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->target != NULL) { - - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->target->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->target, self, self, self->target->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -5055,22 +5211,37 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) //=========================================================================== // -// A_DamageTracer (int amount, str damagetype) +// A_DamageTracer (int amount, str damagetype, int flags) // Damages the tracer of the actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->target != NULL) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -5078,4 +5249,104 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) P_GiveBody(self->tracer, amount); } } +} + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->target != NULL) + { + if ((self->target->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 ((!(self->target->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->target->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } + } +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->tracer != NULL) + { + if ((self->tracer->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 ((!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->tracer->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->tracer, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } + } +} + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +{ + if (self->target != NULL) + { + P_RemoveThing(self->target); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +{ + if (self->tracer != NULL) + { + P_RemoveThing(self->tracer); + } } \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index d4c79c3de..52a0338fe 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -237,9 +237,9 @@ ACTOR Actor native //: Thinker action native A_RemoveMaster(); action native A_RemoveChildren(bool removeall = false); action native A_RemoveSiblings(bool removeall = false); - action native A_KillMaster(name damagetype = "none"); - action native A_KillChildren(name damagetype = "none"); - action native A_KillSiblings(name damagetype = "none"); + 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(); action native A_RaiseChildren(); action native A_RaiseSiblings(); @@ -275,9 +275,9 @@ 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"); - action native A_DamageChildren(int amount, name damagetype = "none"); - action native A_DamageSiblings(int amount, name damagetype = "none"); + 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(); @@ -304,9 +304,13 @@ 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"); - action native A_DamageTarget(int amount, name damagetype = "none"); - action native A_DamageTracer(int amount, name damagetype = "none"); + 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(); + action native A_RemoveTracer(); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index bf998e6b0..1f2df20cc 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -67,6 +67,8 @@ const int SXF_CLEARCALLERSPECIAL = 65536; const int SXF_TRANSFERSTENCILCOL = 131072; const int SXF_TRANSFERALPHA = 262144; const int SXF_TRANSFERRENDERSTYLE = 524288; +const int SXF_SETTARGET = 1048576; +const int SXF_SETTRACER = 2097152; // Flags for A_Chase const int CHF_FASTCHASE = 1; @@ -360,6 +362,16 @@ enum CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ }; +// 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; + +// 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; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From afaa88a4604028d968041bcd708e4a42354a7ae7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 08:48:36 +0200 Subject: [PATCH 098/313] - consolidated the common portion of the 6 different A_Damage* functions into a subfunction. --- src/thingdef/thingdef_codeptr.cpp | 335 ++++++++++-------------------- 1 file changed, 106 insertions(+), 229 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c257a4f70..3893b3a1b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3610,156 +3610,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) ACTION_JUMP(jump); } -enum DMSS -{ - DMSS_FOILINVUL = 1, - DMSS_AFFECTARMOR = 2, - DMSS_KILL = 4, -}; - -//=========================================================================== -// -// A_DamageMaster (int amount, str damagetype, int flags) -// Damages the master of this child by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - - - if (self->master != NULL) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->master->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->master, self, self, self->master->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->master, amount); - } - } -} - -//=========================================================================== -// -// A_DamageChildren (amount, str damagetype, int flags) -// Damages the children of this master by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - TThinkerIterator it; - AActor * mo; - - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - if ((amount > 0) || (flags & DMSS_KILL)) //Bypass if kill flag is present; it won't matter. It intends to kill them. - { - if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); - } - } - } -} - -// [KS] *** End of my modifications *** - -//=========================================================================== -// -// A_DamageSiblings (int amount, str damagetype, int flags) -// Damages the siblings of this master by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - TThinkerIterator it; - AActor * mo; - - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); - } - } - } - } - } -} - - //=========================================================================== // // Modified code pointer from Skulltag @@ -5131,8 +4981,51 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) //=========================================================================== // -// A_DamageSelf (int amount, str damagetype, int flags) -// Damages the calling actor by the specified amount. Negative values heal. +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags) +// Damages the specified actor by the specified amount. Negative values heal. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) +{ + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } +} + +//=========================================================================== +// +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) @@ -5142,36 +5035,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self, self, self, self->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self, amount); - } + DoDamage(self, self, amount, DamageType, flags); } //=========================================================================== // -// A_DamageTarget (int amount, str damagetype, int flags) -// Damages the target of the actor by the specified amount. Negative values heal. +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) @@ -5181,38 +5050,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if (self->target != NULL) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->target->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->target, self, self, self->target->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->target, amount); - } - } + if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); } //=========================================================================== // -// A_DamageTracer (int amount, str damagetype, int flags) -// Damages the tracer of the actor by the specified amount. Negative values heal. +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) @@ -5222,31 +5065,65 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if (self->target != NULL) - { + if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); +} - if ((amount > 0) || (flags & DMSS_KILL)) +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) { - if (!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->tracer, amount); + if (mo->master == self->master && mo != self) DoDamage(mo, self, amount, DamageType, flags); } } } From e025f4090221746fc35db4065a2d1455117d737b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 08:54:18 +0200 Subject: [PATCH 099/313] - more redundancy removal: Consolidated the common part of the A_Kill* functions into a subfunction. --- src/thingdef/thingdef_codeptr.cpp | 267 +++++++++++------------------- 1 file changed, 96 insertions(+), 171 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 3893b3a1b..7c2653580 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2675,133 +2675,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) } -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, -}; - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->master != NULL) - { - if ((self->master->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 ((!(self->master->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->master->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->master, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(mo, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) - { - if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(mo, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } - } - } -} - //=========================================================================== // // A_CountdownArg @@ -5128,6 +5001,46 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) } } + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) +{ + 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->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } +} + + + //=========================================================================== // // A_KillTarget(damagetype, int flags) @@ -5139,30 +5052,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); - if (self->target != NULL) - { - if ((self->target->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 ((!(self->target->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->target->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->target, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } + if (self->target != NULL) DoKill(self->target, self, damagetype, flags); } //=========================================================================== @@ -5176,32 +5066,67 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); - if (self->tracer != NULL) + if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) DoKill(self->master, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) { - if ((self->tracer->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + if (mo->master == self) DoKill(mo, self, damagetype, flags); + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) { - //[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 ((!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->tracer->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->tracer, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } + if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); } } } + //=========================================================================== // // A_RemoveTarget From 68c481945ad95098390b75153ea14b83e13c2cb7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 09:36:38 +0200 Subject: [PATCH 100/313] - extended parameter list of A_BFGSpray. --- src/g_doom/a_doomweaps.cpp | 29 ++++++++++++++++++++++------- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 424e691e7..38c823e18 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -546,6 +546,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireBFG) P_SpawnPlayerMissile (self, 0, 0, 0, PClass::FindClass("BFGBall"), self->angle, NULL, NULL, !!(dmflags2 & DF2_NO_FREEAIMBFG)); } + // // A_BFGSpray // Spawn a BFG explosion on every monster in view @@ -559,14 +560,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) AActor *thingToHit; AActor *linetarget; - ACTION_PARAM_START(3); + ACTION_PARAM_START(7); ACTION_PARAM_CLASS(spraytype, 0); ACTION_PARAM_INT(numrays, 1); ACTION_PARAM_INT(damagecnt, 2); + ACTION_PARAM_ANGLE(angle, 3); + ACTION_PARAM_FIXED(distance, 4); + ACTION_PARAM_ANGLE(vrange, 5); + ACTION_PARAM_INT(defdamage, 6); if (spraytype == NULL) spraytype = PClass::FindClass("BFGExtra"); if (numrays <= 0) numrays = 40; if (damagecnt <= 0) damagecnt = 15; + if (angle == 0) angle = ANG90; + if (distance <= 0) distance = 16 * 64 * FRACUNIT; + if (vrange == 0) vrange = ANGLE_1 * 32; // [RH] Don't crash if no target if (!self->target) @@ -575,10 +583,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) // offset angles from its attack angle for (i = 0; i < numrays; i++) { - an = self->angle - ANG90/2 + ANG90/numrays*i; + an = self->angle - angle/2 + angle/numrays*i; // self->target is the originator (player) of the missile - P_AimLineAttack (self->target, an, 16*64*FRACUNIT, &linetarget, ANGLE_1*32); + P_AimLineAttack (self->target, an, distance, &linetarget, vrange); if (!linetarget) continue; @@ -589,10 +597,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) if (spray && (spray->flags5 & MF5_PUFFGETSOWNER)) spray->target = self->target; - - damage = 0; - for (j = 0; j < damagecnt; ++j) - damage += (pr_bfgspray() & 7) + 1; + if (defdamage == 0) + { + damage = 0; + for (j = 0; j < damagecnt; ++j) + damage += (pr_bfgspray() & 7) + 1; + } + else + { + // if this is used, damagecnt will be ignored + damage = defdamage; + } thingToHit = linetarget; int newdam = P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash), diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 52a0338fe..d404f23a2 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -70,7 +70,7 @@ ACTOR Actor native //: Thinker // End of MBF redundant functions. action native A_MonsterRail(); - action native A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecount = 15); + action native A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecount = 15, float angle = 90, float distance = 16*64, float vrange = 32, int damage = 0); action native A_Pain(); action native A_NoBlocking(); action native A_XScream(); From 68a5db3c8c429736c001d412c6a90703a2aa2288 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 13:22:14 -0500 Subject: [PATCH 101/313] - Added SXF_NOPOINTERS for A_SpawnItemEx. - Added WARPF_ABSOLUTEPOSITION for A_Warp. --- src/thingdef/thingdef_codeptr.cpp | 112 +++++++++++++++++------------ wadsrc/static/actors/constants.txt | 48 +++++++------ 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 7c2653580..297cb81ec 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1767,6 +1767,7 @@ enum SIX_Flags SIXF_TRANSFERRENDERSTYLE = 1 << 19, SIXF_SETTARGET = 1 << 20, SIXF_SETTRACER = 1 << 21, + SIXF_NOPOINTERS = 1 << 22, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1854,6 +1855,13 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) // 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->target = NULL; + mo->master = NULL; + mo->tracer = NULL; + } if (flags & SIXF_SETMASTER) { mo->master = originator; @@ -4378,7 +4386,8 @@ enum WARPF WARPF_STOP = 0x80, WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200 + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) @@ -4411,59 +4420,72 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; } - - if (!(flags & WARPF_ABSOLUTEOFFSET)) + if (!(flags & WARPF_ABSOLUTEPOSITION)) { - angle_t fineangle = angle>>ANGLETOFINESHIFT; - oldx = 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(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - oldx = self->x; - oldy = self->y; - oldz = self->z; - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - self->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) + if (!(flags & WARPF_ABSOLUTEOFFSET)) { - // extra unlink, link and environment calculation + angle_t fineangle = angle >> ANGLETOFINESHIFT; + oldx = 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(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + oldx = self->x; + oldy = self->y; + oldz = self->z; + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + self->SetOrigin( - self->x, - self->y, - self->floorz + zofs); + 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 + self->SetOrigin( + self->x, + self->y, + self->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 + self->z = self->floorz; + } } 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 - self->z = self->floorz; + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); } } - else + else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. { - self->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); + if (flags & WARPF_TOFLOOR) + { + self->SetOrigin(xofs, yofs, self->floorz + zofs); + } + else + { + self->SetOrigin(xofs, yofs, zofs); + } } if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1f2df20cc..3419561f8 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -46,29 +46,30 @@ const int FBF_NOFLASH = 16; const int FBF_NORANDOMPUFFZ = 32; // Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1; -const int SXF_ABSOLUTEPOSITION = 2; -const int SXF_ABSOLUTEANGLE = 4; -const int SXF_ABSOLUTEMOMENTUM = 8; -const int SXF_ABSOLUTEVELOCITY = 8; -const int SXF_SETMASTER = 16; -const int SXF_NOCHECKPOSITION = 32; -const int SXF_TELEFRAG = 64; -const int SXF_CLIENTSIDE = 128; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 256; -const int SXF_TRANSFERPITCH = 512; -const int SXF_TRANSFERPOINTERS = 1024; -const int SXF_USEBLOODCOLOR = 2048; -const int SXF_CLEARCALLERTID = 4096; -const int SXF_MULTIPLYSPEED = 8192; -const int SXF_TRANSFERSCALE = 16384; -const int SXF_TRANSFERSPECIAL = 32768; -const int SXF_CLEARCALLERSPECIAL = 65536; -const int SXF_TRANSFERSTENCILCOL = 131072; -const int SXF_TRANSFERALPHA = 262144; -const int SXF_TRANSFERRENDERSTYLE = 524288; -const int SXF_SETTARGET = 1048576; -const int SXF_SETTRACER = 2097152; +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; // Flags for A_Chase const int CHF_FASTCHASE = 1; @@ -316,6 +317,7 @@ Const Int WARPF_COPYINTERPOLATION = 0x40; Const Int WARPF_STOP = 0x80; Const Int WARPF_TOFLOOR = 0x100; Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; // flags for A_SetPitch/SetAngle const int SPF_FORCECLAMP = 1; From 97d5d614c481150b20d3c874b59024f2af20cca7 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 16:50:24 -0500 Subject: [PATCH 102/313] - Fixed: SXF_NOPOINTERS didn't clear LastHeard, causing monsters to spawn with targets. --- .gitattributes | 63 + src/thingdef/thingdef_codeptr.cpp | 10351 ++++++++++++++-------------- 2 files changed, 5239 insertions(+), 5175 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..1ff0c4230 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 297cb81ec..4266c01c7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1,5176 +1,5177 @@ -/* -** 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(6); - 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); - - int aimmode = flags & CMF_AIMMODE; - - AActor * targ; - AActor * missile; - - if (self->target != 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, self->target, 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); - 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(7); - 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); - - if(Range==0) Range=MISSILERANGE; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if (self->target || (Flags & CBAF_AIMFACING)) - { - if (!(Flags & CBAF_AIMFACING)) A_FaceTarget (self); - 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, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) -{ - ACTION_PARAM_START(5); - 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); - - 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); - - // turn to face target - if (linetarget) - { - if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - 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); -} - -//=========================================================================== -// -// 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); -} - -//=========================================================================== -// -// 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, -}; - -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; - } - 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) - { - 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->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 -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(reduce, 0); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha += reduce; - // Should this clamp alpha to 1.0? -} - -//=========================================================================== -// -// 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_BOOL(remove, 1); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha -= reduce; - if (self->alpha <= 0 && 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_BOOL(remove, 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 (self->alpha == target && 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(2); - ACTION_PARAM_FIXED(scalex, 0); - ACTION_PARAM_FIXED(scaley, 1); - - self->scaleX = scalex; - self->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) -{ - 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; - } - if ((dx*dx) + (dy*dy) + (dz*dz) <= 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(2); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - - 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)) - { - 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)) - { - 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) -{ - 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; - } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range){ - // Within range - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) -{ - ACTION_PARAM_START(2); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - - 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)) - { - 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)) - { - 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; - - 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->SetState (self->SpawnState); - self->renderflags &= ~RF_INVISIBLE; - - if (flags & RSF_FOG) - { - Spawn (self->x, self->y, self->z + TELEFOGHEIGHT, ALLOW_REPLACE); - } - 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 = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, -}; - -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; - } - 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=2, - JLOSF_CLOSENOFOV=4, - JLOSF_CLOSENOSIGHT=8, - JLOSF_CLOSENOJUMP=16, - JLOSF_DEADNOJUMP=32, - JLOSF_CHECKMASTER=64, - JLOSF_TARGETLOS=128, - JLOSF_FLIPFOV=256, - JLOSF_ALLYNOJUMP=512, - JLOSF_COMBATANTONLY=1024, - JLOSF_NOAUTOAIM=2048, -}; - -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)) - { - 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; - - 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_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) -{ - if (self->master != NULL) - { - P_RemoveThing(self->master); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } - } -} - -//=========================================================================== -// -// A_RaiseMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) -{ - if (self->master != NULL) - { - P_Thing_Raise(self->master); - } -} - -//=========================================================================== -// -// A_RaiseChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) -{ - TThinkerIterator it; - AActor *mo; - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self) - { - P_Thing_Raise(mo); - } - } -} - -//=========================================================================== -// -// A_RaiseSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) -{ - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self) - { - P_Thing_Raise(mo); - } - } - } -} - -//=========================================================================== -// -// 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(2); - ACTION_PARAM_ANGLE(angle, 0); - ACTION_PARAM_INT(flags, 1) - self->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_SetPitch -// -// Set actor's pitch (in degrees). -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_ANGLE(pitch, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->player != NULL || (flags & SPF_FORCECLAMP)) - { // clamp the pitch we set - int min, max; - - if (self->player != NULL) - { - min = self->player->MinPitch; - max = self->player->MaxPitch; - } - else - { - min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); - max = ANGLE_90 - (1 << ANGLETOFINESHIFT); - } - pitch = clamp(pitch, min, max); - } - self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_ScaleVelocity -// -// Scale actor's velocity. -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(scale, 0); - - 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); - - // 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); - } -} - -//=========================================================================== -// -// 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); - - INTBOOL was_moving = self->velx | self->vely | self->velz; - - fixed_t vx = x, vy = y, vz = z; - fixed_t sina = finesine[self->angle >> ANGLETOFINESHIFT]; - fixed_t cosa = finecosine[self->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 - { - self->velx = vx; - self->vely = vy; - self->velz = vz; - } - else // add new velocity to old velocity - { - self->velx += vx; - self->vely += vy; - self->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 = 1, // Allow telefrag in order to teleport. - TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) -}; - -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; - } - - if (TeleportState == NULL) - { - // Default to Teleport. - TeleportState = self->FindState("Teleport"); - // If still nothing, then return. - if (!TeleportState) 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; - if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (FogType) - { - Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); - } - - ACTION_JUMP(TeleportState); - - self->z = self->floorz; - self->angle = spot->angle; - self->velx = self->vely = self->velz = 0; - } -} - -//=========================================================================== -// -// 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, -}; - -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); - - fixed_t - - oldx, - oldy, - oldz; - - AActor *reference = COPY_AAPTR(self, destination_selector); - - if (!reference) - { - ACTION_SET_RESULT(false); - return; - } - - if (!(flags & WARPF_ABSOLUTEANGLE)) - { - angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; - } - if (!(flags & WARPF_ABSOLUTEPOSITION)) - { - if (!(flags & WARPF_ABSOLUTEOFFSET)) - { - angle_t fineangle = angle >> ANGLETOFINESHIFT; - oldx = 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(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - oldx = self->x; - oldy = self->y; - oldz = self->z; - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - self->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 - self->SetOrigin( - self->x, - self->y, - self->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 - self->z = self->floorz; - } - } - else - { - self->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) - { - self->SetOrigin(xofs, yofs, self->floorz + zofs); - } - else - { - self->SetOrigin(xofs, yofs, zofs); - } - } - - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) - { - if (flags & WARPF_TESTONLY) - { - self->SetOrigin(oldx, oldy, oldz); - } - else - { - self->angle = angle; - - if (flags & WARPF_STOP) - { - self->velx = 0; - self->vely = 0; - self->velz = 0; - } - - if (flags & WARPF_WARPINTERPOLATION) - { - self->PrevX += self->x - oldx; - self->PrevY += self->y - oldy; - self->PrevZ += self->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; - } - else if (! (flags & WARPF_INTERPOLATE)) - { - self->PrevX = self->x; - self->PrevY = self->y; - self->PrevZ = self->z; - } - } - - 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 - { - self->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, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_MASK = 63, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -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 - { - 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 (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path - 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(1); - ACTION_PARAM_FIXED(speed, 0); - - self->Speed = speed; -} - -//=========================================================================== -// -// Common A_Damage handler -// -// A_Damage* (int amount, str damagetype, int flags) -// Damages the specified actor by the specified amount. Negative values heal. -// -//=========================================================================== - -enum DMSS -{ - DMSS_FOILINVUL = 1, - DMSS_AFFECTARMOR = 2, - DMSS_KILL = 4, -}; - -static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) -{ - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(dmgtarget, amount); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - DoDamage(self, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - TThinkerIterator it; - AActor * mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - 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); - } - } -} - - -//=========================================================================== -// -// A_Kill*(damagetype, int flags) -// -//=========================================================================== -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, -}; - -static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) -{ - 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->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->target, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } -} - - - -//=========================================================================== -// -// A_KillTarget(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->target != NULL) DoKill(self->target, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillTracer(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->master != NULL) DoKill(self->master, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) DoKill(mo, self, damagetype, flags); - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); - } - } -} - - -//=========================================================================== -// -// A_RemoveTarget -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) -{ - if (self->target != NULL) - { - P_RemoveThing(self->target); - } -} - -//=========================================================================== -// -// A_RemoveTracer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) -{ - if (self->tracer != NULL) - { - P_RemoveThing(self->tracer); - } +/* +** 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(6); + 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); + + int aimmode = flags & CMF_AIMMODE; + + AActor * targ; + AActor * missile; + + if (self->target != 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, self->target, 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); + 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(7); + 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); + + if(Range==0) Range=MISSILERANGE; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if (self->target || (Flags & CBAF_AIMFACING)) + { + if (!(Flags & CBAF_AIMFACING)) A_FaceTarget (self); + 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, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) +{ + ACTION_PARAM_START(5); + 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); + + 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); + + // turn to face target + if (linetarget) + { + if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) + P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + 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); +} + +//=========================================================================== +// +// 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); +} + +//=========================================================================== +// +// 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, +}; + +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; + } + 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 +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(reduce, 0); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha += reduce; + // Should this clamp alpha to 1.0? +} + +//=========================================================================== +// +// 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_BOOL(remove, 1); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha -= reduce; + if (self->alpha <= 0 && 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_BOOL(remove, 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 (self->alpha == target && 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(2); + ACTION_PARAM_FIXED(scalex, 0); + ACTION_PARAM_FIXED(scaley, 1); + + self->scaleX = scalex; + self->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) +{ + 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; + } + if ((dx*dx) + (dy*dy) + (dz*dz) <= 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(2); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + + 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)) + { + 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)) + { + 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) +{ + 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; + } + if ((dx*dx) + (dy*dy) + (dz*dz) <= range){ + // Within range + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) +{ + ACTION_PARAM_START(2); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + + 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)) + { + 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)) + { + 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; + + 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->SetState (self->SpawnState); + self->renderflags &= ~RF_INVISIBLE; + + if (flags & RSF_FOG) + { + Spawn (self->x, self->y, self->z + TELEFOGHEIGHT, ALLOW_REPLACE); + } + 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 = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, +}; + +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; + } + 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=2, + JLOSF_CLOSENOFOV=4, + JLOSF_CLOSENOSIGHT=8, + JLOSF_CLOSENOJUMP=16, + JLOSF_DEADNOJUMP=32, + JLOSF_CHECKMASTER=64, + JLOSF_TARGETLOS=128, + JLOSF_FLIPFOV=256, + JLOSF_ALLYNOJUMP=512, + JLOSF_COMBATANTONLY=1024, + JLOSF_NOAUTOAIM=2048, +}; + +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)) + { + 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; + + 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_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) +{ + if (self->master != NULL) + { + P_RemoveThing(self->master); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(removeall,0); + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + P_RemoveThing(mo); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(removeall,0); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + P_RemoveThing(mo); + } + } + } +} + +//=========================================================================== +// +// A_RaiseMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) +{ + if (self->master != NULL) + { + P_Thing_Raise(self->master); + } +} + +//=========================================================================== +// +// A_RaiseChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) +{ + TThinkerIterator it; + AActor *mo; + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self) + { + P_Thing_Raise(mo); + } + } +} + +//=========================================================================== +// +// A_RaiseSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) +{ + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self) + { + P_Thing_Raise(mo); + } + } + } +} + +//=========================================================================== +// +// 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(2); + ACTION_PARAM_ANGLE(angle, 0); + ACTION_PARAM_INT(flags, 1) + self->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_SetPitch +// +// Set actor's pitch (in degrees). +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_ANGLE(pitch, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->player != NULL || (flags & SPF_FORCECLAMP)) + { // clamp the pitch we set + int min, max; + + if (self->player != NULL) + { + min = self->player->MinPitch; + max = self->player->MaxPitch; + } + else + { + min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); + max = ANGLE_90 - (1 << ANGLETOFINESHIFT); + } + pitch = clamp(pitch, min, max); + } + self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_ScaleVelocity +// +// Scale actor's velocity. +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(scale, 0); + + 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); + + // 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); + } +} + +//=========================================================================== +// +// 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); + + INTBOOL was_moving = self->velx | self->vely | self->velz; + + fixed_t vx = x, vy = y, vz = z; + fixed_t sina = finesine[self->angle >> ANGLETOFINESHIFT]; + fixed_t cosa = finecosine[self->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 + { + self->velx = vx; + self->vely = vy; + self->velz = vz; + } + else // add new velocity to old velocity + { + self->velx += vx; + self->vely += vy; + self->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 = 1, // Allow telefrag in order to teleport. + TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) +}; + +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; + } + + if (TeleportState == NULL) + { + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) 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; + if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (FogType) + { + Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + } + + ACTION_JUMP(TeleportState); + + self->z = self->floorz; + self->angle = spot->angle; + self->velx = self->vely = self->velz = 0; + } +} + +//=========================================================================== +// +// 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, +}; + +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); + + fixed_t + + oldx, + oldy, + oldz; + + AActor *reference = COPY_AAPTR(self, destination_selector); + + if (!reference) + { + ACTION_SET_RESULT(false); + return; + } + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; + } + if (!(flags & WARPF_ABSOLUTEPOSITION)) + { + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + angle_t fineangle = angle >> ANGLETOFINESHIFT; + oldx = 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(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + oldx = self->x; + oldy = self->y; + oldz = self->z; + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + + self->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 + self->SetOrigin( + self->x, + self->y, + self->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 + self->z = self->floorz; + } + } + else + { + self->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) + { + self->SetOrigin(xofs, yofs, self->floorz + zofs); + } + else + { + self->SetOrigin(xofs, yofs, zofs); + } + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) + { + if (flags & WARPF_TESTONLY) + { + self->SetOrigin(oldx, oldy, oldz); + } + else + { + self->angle = angle; + + if (flags & WARPF_STOP) + { + self->velx = 0; + self->vely = 0; + self->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + self->PrevX += self->x - oldx; + self->PrevY += self->y - oldy; + self->PrevZ += self->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; + } + else if (! (flags & WARPF_INTERPOLATE)) + { + self->PrevX = self->x; + self->PrevY = self->y; + self->PrevZ = self->z; + } + } + + 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 + { + self->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, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_MASK = 63, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +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 + { + 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 (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path + 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(1); + ACTION_PARAM_FIXED(speed, 0); + + self->Speed = speed; +} + +//=========================================================================== +// +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags) +// Damages the specified actor by the specified amount. Negative values heal. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) +{ + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + DoDamage(self, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + 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); + } + } +} + + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) +{ + 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->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } +} + + + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->target != NULL) DoKill(self->target, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) DoKill(self->master, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoKill(mo, self, damagetype, flags); + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) + { + if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); + } + } +} + + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +{ + if (self->target != NULL) + { + P_RemoveThing(self->target); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +{ + if (self->tracer != NULL) + { + P_RemoveThing(self->tracer); + } } \ No newline at end of file From c962c56079480acff87afc078d23a4019b304492 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Sep 2014 01:04:18 +0200 Subject: [PATCH 103/313] - removed accidentally committed .gitattributes file. --- .gitattributes | 63 -------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c4230..000000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain From a2f7b86a0f460c88821ddfb2463d2c9de252cbd2 Mon Sep 17 00:00:00 2001 From: fdari Date: Sun, 28 Sep 2014 11:52:37 +0200 Subject: [PATCH 104/313] IsPointerEqual (ACS and Decorate) Decorate: IsPointerEqual(int aaptr_selector1, int aaptr_selector2) ACS: IsPointerEqual(int aaptr_selector1, int aaptr_selector2, int tid1 = 0, int tid2 = 0) Compare the pointers values returned by two pointer select operations. Returns true if they both resolve to the same value. Null values can be explicitly tested using IsPointerEqual(AAPTR_NULL, ...) ACS: IsPointerEqual(int aaptr1, int aaptr2, int tid1 = 0, int tid2 = 0) This function lets you compare pointers from other actors than the activator, using tids. Tid1 determines the actor used to resolve aaptr1, Tid2 does the same for aaptr2. If tid1 and tid2 are equal, the same actor will be used for resolving both pointers (that could always happen randomly; this way you know it will happen). --- src/namedef.h | 1 + src/p_acs.cpp | 17 +++++++++++++++ src/thingdef/thingdef_function.cpp | 35 +++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/namedef.h b/src/namedef.h index 1e7fbd0d3..5c65ab655 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -299,6 +299,7 @@ xx(ACS_NamedExecuteWithResult) xx(CallACS) xx(Sqrt) xx(CheckClass) +xx(IsPointerEqual) // Various actor names which are used internally xx(MapSpot) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 983728c8d..9fa5079ff 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4373,6 +4373,7 @@ enum EACSFunctions ACSF_GetArmorInfo, ACSF_DropInventory, ACSF_PickActor, + ACSF_IsPointerEqual, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5630,6 +5631,22 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_IsPointerEqual: + { + int tid1 = 0, tid2 = 0; + switch (argCount) + { + case 4: tid2 = args[3]; + case 3: tid1 = args[2]; + } + + actor = SingleActorFromTID(tid1, activator); + AActor * actor2 = tid2 == tid1 ? actor : SingleActorFromTID(tid2, activator); + + return COPY_AAPTR(actor, args[0]) == COPY_AAPTR(actor2, args[1]); + } + break; + default: break; } diff --git a/src/thingdef/thingdef_function.cpp b/src/thingdef/thingdef_function.cpp index 9bf80dcaa..fcc936017 100644 --- a/src/thingdef/thingdef_function.cpp +++ b/src/thingdef/thingdef_function.cpp @@ -260,4 +260,37 @@ class FxGlobalFunctionCall_CheckClass : public FxGlobalFunctionCall } }; -GLOBALFUNCTION_ADDER(CheckClass); \ No newline at end of file +GLOBALFUNCTION_ADDER(CheckClass); + +//========================================================================== +// +// Function: ispointerequal +// +//========================================================================== + +class FxGlobalFunctionCall_IsPointerEqual : public FxGlobalFunctionCall +{ + public: + GLOBALFUNCTION_DEFINE(IsPointerEqual); + + FxExpression *Resolve(FCompileContext& ctx) + { + CHECKRESOLVED(); + + if (!ResolveArgs(ctx, 2, 2, true)) + return NULL; + + ValueType = VAL_Int; + return this; + } + + ExpVal EvalExpression(AActor *self) + { + ExpVal ret; + ret.Type = VAL_Int; + ret.Int = COPY_AAPTR(self, (*ArgList)[0]->EvalExpression(self).GetInt()) == COPY_AAPTR(self, (*ArgList)[1]->EvalExpression(self).GetInt()); + return ret; + } +}; + +GLOBALFUNCTION_ADDER(IsPointerEqual); \ No newline at end of file From c4f0f95ec8bc0320f0d50be2b55bdc55bc016843 Mon Sep 17 00:00:00 2001 From: fdari Date: Sun, 28 Sep 2014 15:06:52 +0200 Subject: [PATCH 105/313] Get linetarget (aim target) from any actor (not just player): AAPTR_LINETARGET --- src/actorptrselect.cpp | 7 +++++++ src/actorptrselect.h | 3 ++- wadsrc/static/actors/constants.txt | 31 +++++++++++++++--------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/actorptrselect.cpp b/src/actorptrselect.cpp index f1f50eeb5..774067550 100644 --- a/src/actorptrselect.cpp +++ b/src/actorptrselect.cpp @@ -56,6 +56,13 @@ AActor *COPY_AAPTR(AActor *origin, int selector) case AAPTR_TRACER: return origin->tracer; case AAPTR_FRIENDPLAYER: return origin->FriendPlayer ? AAPTR_RESOLVE_PLAYERNUM(origin->FriendPlayer - 1) : NULL; + + case AAPTR_GET_LINETARGET: + { + AActor *gettarget = NULL; + P_BulletSlope(origin, &gettarget); + return gettarget; + } } } diff --git a/src/actorptrselect.h b/src/actorptrselect.h index 46e1aa54d..bfc88fb3c 100644 --- a/src/actorptrselect.h +++ b/src/actorptrselect.h @@ -36,12 +36,13 @@ enum AAPTR AAPTR_PLAYER8 = 0x2000, AAPTR_FRIENDPLAYER = 0x4000, + AAPTR_GET_LINETARGET = 0x8000, AAPTR_PLAYER_SELECTORS = AAPTR_PLAYER_GETTARGET|AAPTR_PLAYER_GETCONVERSATION, AAPTR_GENERAL_SELECTORS = - AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER, + AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER|AAPTR_GET_LINETARGET, AAPTR_STATIC_SELECTORS = AAPTR_PLAYER1|AAPTR_PLAYER2|AAPTR_PLAYER3|AAPTR_PLAYER4| diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 3419561f8..428f92d14 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -279,24 +279,25 @@ Const Int BLOCKF_USE = 128; // Pointer constants, bitfield-enabled Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 1; -Const Int AAPTR_TARGET = 2; -Const Int AAPTR_MASTER = 4; -Const Int AAPTR_TRACER = 8; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; -Const Int AAPTR_PLAYER_GETTARGET = 16; -Const Int AAPTR_PLAYER_GETCONVERSATION = 32; +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; -Const Int AAPTR_PLAYER1 = 64; -Const Int AAPTR_PLAYER2 = 128; -Const Int AAPTR_PLAYER3 = 256; -Const Int AAPTR_PLAYER4 = 512; -Const Int AAPTR_PLAYER5 = 1024; -Const Int AAPTR_PLAYER6 = 2048; -Const Int AAPTR_PLAYER7 = 4096; -Const Int AAPTR_PLAYER8 = 8192; +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; -Const Int AAPTR_FRIENDPLAYER = 16384; +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; // Pointer operation flags From 43b86288c76e50c1eac79257714b5e3243b55be1 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 28 Sep 2014 08:15:00 -0500 Subject: [PATCH 106/313] - Added A_Remove(int pointer, int flags). - RMVF_MISSILES removes missiles. - RMVF_NOMONSTERS ignores monsters. - RMVF_MISC includes non-monsters and missiles. - RMVF_EVERYTHING disregards all checks and remove it. These flags now apply to the following in addition to the new function: -A_RemoveTarget -A_RemoveMaster -A_RemoveTracer -A_RemoveChildren -A_RemoveSiblings --- src/thingdef/thingdef_codeptr.cpp | 181 +++++++++++++++++++---------- wadsrc/static/actors/actor.txt | 11 +- wadsrc/static/actors/constants.txt | 9 ++ 3 files changed, 133 insertions(+), 68 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 4266c01c7..13b5a0cb3 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3671,65 +3671,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) } } - -//=========================================================================== -// -// A_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) -{ - if (self->master != NULL) - { - P_RemoveThing(self->master); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } - } -} - //=========================================================================== // // A_RaiseMaster @@ -5063,7 +5004,6 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags } - //=========================================================================== // // A_KillTarget(damagetype, int flags) @@ -5149,6 +5089,39 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) } } +//=========================================================================== +// +// DoRemove +// +//=========================================================================== + +enum RMVF_flags +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, +}; + +static void DoRemove(AActor *removetarget, int flags) +{ + 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); + } +} //=========================================================================== // @@ -5157,7 +5130,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) { - if (self->target != NULL) + if ((self->target != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) { P_RemoveThing(self->target); } @@ -5170,8 +5143,90 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) { - if (self->tracer != NULL) + if ((self->tracer != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) { P_RemoveThing(self->tracer); } -} \ No newline at end of file +} + +//=========================================================================== +// +// A_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) + { + DoRemove(self->master, flags); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags); + } + } + } +} + +//=========================================================================== +// +// A_Remove +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(removee, 0); + ACTION_PARAM_INT(flags, 1); + + AActor *reference = COPY_AAPTR(self, removee); + + if (reference != NULL) + { + DoRemove(reference, flags); + } +} + diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index d404f23a2..280321ad0 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -234,9 +234,9 @@ 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(); - action native A_RemoveChildren(bool removeall = false); - action native A_RemoveSiblings(bool removeall = false); + 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); @@ -309,8 +309,9 @@ ACTOR Actor native //: Thinker 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(); - action native A_RemoveTracer(); + 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_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 3419561f8..41d69a157 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -380,6 +380,15 @@ const int AMF_TARGETEMITTER = 1; const int AMF_TARGETNONPLAYER = 2; 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 +}; + // This is only here to provide one global variable for testing. native int testglobalvar; From 96c6e7d9bfe5b466c85e01e6bee65cf62dad22c2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 28 Sep 2014 08:20:27 -0500 Subject: [PATCH 107/313] Minor fix; didn't mean to include that flag check. --- src/thingdef/thingdef_codeptr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 13b5a0cb3..e724a1ba2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5130,7 +5130,7 @@ static void DoRemove(AActor *removetarget, int flags) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) { - if ((self->target != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) + if ((self->target != NULL)) { P_RemoveThing(self->target); } @@ -5143,7 +5143,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) { - if ((self->tracer != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) + if ((self->tracer != NULL)) { P_RemoveThing(self->tracer); } From 770547e6618588a47c1c5d4b65bd3603cd59873b Mon Sep 17 00:00:00 2001 From: Teemu Piippo Date: Sun, 28 Sep 2014 17:17:19 +0300 Subject: [PATCH 108/313] - added udmf key midtex3dimpassible which causes the midtex to behave like a finite-height impassible line; practically this means the mid texture lets projectiles pass through it. --- specs/udmf_zdoom.txt | 51 +++++++++++++++++++++++--------------------- src/doomdata.h | 1 + src/namedef.h | 3 ++- src/p_3dmidtex.cpp | 7 ++++++ src/p_udmf.cpp | 11 +++++++++- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 1128b2e55..cbb5b902c 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -92,30 +92,33 @@ Note: All fields default to false unless mentioned otherwise. linedef { - alpha = ; // Translucency of this line, default is 1.0 - renderstyle = ; // Render style, can be "translucent" or "add", - // default is "translucent". - playeruseback = ; // New SPAC flag, true = player can use from back side. - anycross = ; // New SPAC flag, true = any non-projectile - // crossing will trigger this line - monsteractivate = ; // Monsters can trigger this line. - // For compatibility only because this flag's - // semantics can not be fully reproduced with - // explicit trigger flags. - blockplayers = ; // Line blocks players' movement. - blockeverything = ; // Line blocks everything. - firstsideonly = ; // Line can only be triggered from the front side. - zoneboundary = ; // Line is a boundary for sound reverb zones. - clipmidtex = ; // Line's mid textures are clipped to floor and ceiling. - wrapmidtex = ; // Line's mid textures are wrapped. - midtex3d = ; // Actors can walk on mid texture. - checkswitchrange = ;// Switches can only be activated when vertically reachable. - blockprojectiles = ;// Line blocks all projectiles - blockuse = ; // Line blocks all use actions - blocksight = ; // Line blocks monster line of sight - blockhitscan = ; // Line blocks hitscan attacks - locknumber = ; // Line special is locked - arg0str = ; // Alternate string-based version of arg0 + alpha = ; // Translucency of this line, default is 1.0 + renderstyle = ; // Render style, can be "translucent" or "add", + // default is "translucent". + playeruseback = ; // New SPAC flag, true = player can use from back side. + anycross = ; // New SPAC flag, true = any non-projectile + // crossing will trigger this line + monsteractivate = ; // Monsters can trigger this line. + // For compatibility only because this flag's + // semantics can not be fully reproduced with + // explicit trigger flags. + blockplayers = ; // Line blocks players' movement. + blockeverything = ; // Line blocks everything. + firstsideonly = ; // Line can only be triggered from the front side. + zoneboundary = ; // Line is a boundary for sound reverb zones. + clipmidtex = ; // Line's mid textures are clipped to floor and ceiling. + wrapmidtex = ; // Line's mid textures are wrapped. + midtex3d = ; // Actors can walk on mid texture. + midtex3dimpassible = ;// Used in conjuction with midtex3d - causes the mid + // texture to behave like an impassible line (projectiles + // pass through it). + checkswitchrange = ; // Switches can only be activated when vertically reachable. + blockprojectiles = ; // Line blocks all projectiles + blockuse = ; // Line blocks all use actions + blocksight = ; // Line blocks monster line of sight + blockhitscan = ; // Line blocks hitscan attacks + locknumber = ; // Line special is locked + arg0str = ; // Alternate string-based version of arg0 transparent = ; // true = line is a Strife transparent line (alpha 0.25) diff --git a/src/doomdata.h b/src/doomdata.h index 215c2a263..f190be37d 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -162,6 +162,7 @@ enum ELineFlags ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks + ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING }; diff --git a/src/namedef.h b/src/namedef.h index 1e7fbd0d3..4706fa500 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -419,6 +419,7 @@ xx(Passuse) xx(Repeatspecial) xx(Conversation) xx(Locknumber) +xx(Midtex3dimpassible) xx(Playercross) xx(Playeruse) @@ -597,4 +598,4 @@ xx(NeverSwitchOnPickup) xx(MoveBob) xx(StillBob) xx(PlayerClass) -xx(Wi_NoAutostartMap) \ No newline at end of file +xx(Wi_NoAutostartMap) diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index ccb6f0359..ac6d7aca7 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -258,6 +258,13 @@ bool P_GetMidTexturePosition(const line_t *line, int sideno, fixed_t *ptextop, f bool P_LineOpening_3dMidtex(AActor *thing, const line_t *linedef, FLineOpening &open, bool restrict) { + // [TP] Impassible-like 3dmidtextures do not block missiles + if ((linedef->flags & ML_3DMIDTEX_IMPASS) + && (thing->flags & MF_MISSILE || thing->BounceFlags & BOUNCE_MBF)) + { + return false; + } + fixed_t tt, tb; open.abovemidtex = false; diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 7588199b4..473cdcd81 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1030,11 +1030,16 @@ public: Flag(ld->flags, ML_BLOCKHITSCAN, key); continue; - // [Dusk] lock number + // [TP] Locks the special with a key case NAME_Locknumber: ld->locknumber = CheckInt(key); continue; + // [TP] Causes a 3d midtex to behave like an impassible line + case NAME_Midtex3dimpassible: + Flag(ld->flags, ML_3DMIDTEX_IMPASS, key); + continue; + default: break; } @@ -1081,6 +1086,10 @@ public: { ld->args[1] = -FName(arg1str); } + if ((ld->flags & ML_3DMIDTEX_IMPASS) && !(ld->flags & ML_3DMIDTEX)) // [TP] + { + Printf ("Line %d has midtex3dimpassible without midtex3d.\n", index); + } } //=========================================================================== From 3050ea9a6d3a85f89bcb27c5254e26cf0304a3b7 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 11:27:22 -0500 Subject: [PATCH 109/313] - Fixed: A_RemoveTracer/Target didn't take flags into account. --- src/thingdef/thingdef_codeptr.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index e724a1ba2..e3dbbfbb7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5128,11 +5128,13 @@ static void DoRemove(AActor *removetarget, int flags) // A_RemoveTarget // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) { - if ((self->target != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->target); + DoRemove(self->target, flags); } } @@ -5141,11 +5143,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) // A_RemoveTracer // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) { - if ((self->tracer != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->tracer); + DoRemove(self->tracer, flags); } } From 5030832df0141a7598f5a6d4363f3febf3711829 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 11:48:07 -0500 Subject: [PATCH 110/313] - Added DMSS_NOFACTOR for all A_Damage functions. --- src/thingdef/thingdef_codeptr.cpp | 35 +++++++++++++++++++++++------- wadsrc/static/actors/constants.txt | 1 + 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index e724a1ba2..65888d0d5 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4830,6 +4830,7 @@ enum DMSS DMSS_FOILINVUL = 1, DMSS_AFFECTARMOR = 2, DMSS_KILL = 4, + DMSS_NOFACTOR = 8, }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) @@ -4844,12 +4845,26 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy } if (flags & DMSS_AFFECTARMOR) { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + if (flags & DMSS_NOFACTOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_FACTOR); + } + else + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } } else { + if (flags & DMSS_NOFACTOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR | DMG_NO_FACTOR); + } //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + else + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } } } } @@ -5128,11 +5143,13 @@ static void DoRemove(AActor *removetarget, int flags) // A_RemoveTarget // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) { - if ((self->target != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->target); + DoRemove(self->target, flags); } } @@ -5141,11 +5158,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) // A_RemoveTracer // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) { - if ((self->tracer != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->tracer); + DoRemove(self->tracer, flags); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1fe8e75cb..57de8e6ad 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -375,6 +375,7 @@ const int KILS_NOMONSTERS = 4; const int DMSS_FOILINVUL = 1; const int DMSS_AFFECTARMOR = 2; const int DMSS_KILL = 4; +const int DMSS_NOFACTOR = 8; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From c6e1ea864f38cb1f267f33fdd5ade5a9903de8f3 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Thu, 2 Oct 2014 21:03:15 +0100 Subject: [PATCH 111/313] - Added scoreboard toggling. --- src/ct_chat.cpp | 3 ++- src/hu_scores.cpp | 8 ++++++++ src/hu_stuff.h | 2 ++ wadsrc/static/menudef.txt | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index c0c1f13f0..697d72c58 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -273,7 +273,8 @@ void CT_Drawer (void) if (players[consoleplayer].camera != NULL && (Button_ShowScores.bDown || - players[consoleplayer].camera->health <= 0) && + players[consoleplayer].camera->health <= 0 || + SB_ForceActive) && // Don't draw during intermission, since it has its own scoreboard in wi_stuff.cpp. gamestate != GS_INTERMISSION) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index 8b9a7d0ad..fe2735ede 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -49,6 +49,7 @@ #include "hu_stuff.h" #include "gstrings.h" #include "d_net.h" +#include "c_dispatch.h" // MACROS ------------------------------------------------------------------ @@ -117,6 +118,8 @@ int STACK_ARGS compareteams (const void *arg1, const void *arg2) return diff; } +bool SB_ForceActive = false; + // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- @@ -492,3 +495,8 @@ int HU_GetRowColor(player_t *player, bool highlight) } } } + +CCMD (togglescoreboard) +{ + SB_ForceActive = !SB_ForceActive; +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h index dc22a2adc..eb2dc573b 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -52,6 +52,8 @@ void HU_GetPlayerWidths(int &maxnamewidth, int &maxscorewidth, int &maxiconheigh void HU_DrawColorBar(int x, int y, int height, int playernum); int HU_GetRowColor(player_t *player, bool hightlight); +extern bool SB_ForceActive; + // Sorting routines int STACK_ARGS comparepoints(const void *arg1, const void *arg2); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 6d313faf1..36ff3d8ab 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -458,6 +458,7 @@ OptionMenu "CustomizeControls" Control "Run", "+speed" Control "Strafe", "+strafe" Control "Show Scoreboard", "+showscores" + Control "Toggle Scoreboard", "togglescoreboard" StaticText "" StaticText "Chat", 1 Control "Say", "messagemode" From f54a59fdf8e6d54f6cae8b9bd92f90b81bbf26c3 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 17:00:17 -0500 Subject: [PATCH 112/313] - Added JLOSF_CHECKTRACER for A_JumpIfTargetInLOS. - CHECKTRACER doesn't need to be a missile or have the SEEKERMISSILE flag. --- src/thingdef/thingdef_codeptr.cpp | 29 +- wadsrc/static/actors/constants.txt | 796 +++++++++++++++-------------- 2 files changed, 415 insertions(+), 410 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 65888d0d5..45dd9ff49 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3278,18 +3278,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) enum JLOS_flags { - JLOSF_PROJECTILE=1, - JLOSF_NOSIGHT=2, - JLOSF_CLOSENOFOV=4, - JLOSF_CLOSENOSIGHT=8, - JLOSF_CLOSENOJUMP=16, - JLOSF_DEADNOJUMP=32, - JLOSF_CHECKMASTER=64, - JLOSF_TARGETLOS=128, - JLOSF_FLIPFOV=256, - JLOSF_ALLYNOJUMP=512, - JLOSF_COMBATANTONLY=1024, - JLOSF_NOAUTOAIM=2048, + 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) @@ -3314,9 +3315,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) { target = self->master; } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) { - if (self->flags2 & MF2_SEEKERMISSILE) + if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) target = self->tracer; else target = NULL; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 57de8e6ad..5e9eb2d6d 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -1,396 +1,400 @@ - -// Flags for A_PainAttack -const int PAF_NOSKULLATTACK = 1; -const int PAF_AIMFACING = 2; -const int PAF_NOTARGET = 4; - -// Flags for A_VileAttack -const int VAF_DMGTYPEAPPLYTODIRECT = 1; - -// Flags for A_Saw -const int SF_NORANDOM = 1; -const int SF_RANDOMLIGHTMISS = 2; -const int SF_RANDOMLIGHTHIT = 4; -const int SF_RANDOMLIGHTBOTH = 6; -const int SF_NOUSEAMMOMISS = 8; -const int SF_NOUSEAMMO = 16; -const int SF_NOPULLIN = 32; -const int SF_NOTURN = 64; - -// Flags for A_CustomMissile -const int CMF_AIMOFFSET = 1; -const int CMF_AIMDIRECTION = 2; -const int CMF_TRACKOWNER = 4; -const int CMF_CHECKTARGETDEAD = 8; -const int CMF_ABSOLUTEPITCH = 16; -const int CMF_OFFSETPITCH = 32; -const int CMF_SAVEPITCH = 64; -const int CMF_ABSOLUTEANGLE = 128; - -// Flags for A_CustomBulletAttack -const int CBAF_AIMFACING = 1; -const int CBAF_NORANDOM = 2; -const int CBAF_EXPLICITANGLE = 4; -const int CBAF_NOPITCH = 8; -const int CBAF_NORANDOMPUFFZ = 16; - -// Flags for A_GunFlash -const int GFF_NOEXTCHANGE = 1; - -// Flags for A_FireBullets -const int FBF_USEAMMO = 1; -const int FBF_NORANDOM = 2; -const int FBF_EXPLICITANGLE = 4; -const int FBF_NOPITCH = 8; -const int FBF_NOFLASH = 16; -const int FBF_NORANDOMPUFFZ = 32; - -// Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1 << 0; -const int SXF_ABSOLUTEPOSITION = 1 << 1; -const int SXF_ABSOLUTEANGLE = 1 << 2; -const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility -const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. -const int SXF_SETMASTER = 1 << 4; -const int SXF_NOCHECKPOSITION = 1 << 5; -const int SXF_TELEFRAG = 1 << 6; -const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; -const int SXF_TRANSFERPITCH = 1 << 9; -const int SXF_TRANSFERPOINTERS = 1 << 10; -const int SXF_USEBLOODCOLOR = 1 << 11; -const int SXF_CLEARCALLERTID = 1 << 12; -const int SXF_MULTIPLYSPEED = 1 << 13; -const int SXF_TRANSFERSCALE = 1 << 14; -const int SXF_TRANSFERSPECIAL = 1 << 15; -const int SXF_CLEARCALLERSPECIAL = 1 << 16; -const int SXF_TRANSFERSTENCILCOL = 1 << 17; -const int SXF_TRANSFERALPHA = 1 << 18; -const int SXF_TRANSFERRENDERSTYLE = 1 << 19; -const int SXF_SETTARGET = 1 << 20; -const int SXF_SETTRACER = 1 << 21; -const int SXF_NOPOINTERS = 1 << 22; - -// Flags for A_Chase -const int CHF_FASTCHASE = 1; -const int CHF_NOPLAYACTIVE = 2; -const int CHF_NIGHTMAREFAST = 4; -const int CHF_RESURRECT = 8; -const int CHF_DONTMOVE = 16; - -// Flags for A_LookEx -const int LOF_NOSIGHTCHECK = 1; -const int LOF_NOSOUNDCHECK = 2; -const int LOF_DONTCHASEGOAL = 4; -const int LOF_NOSEESOUND = 8; -const int LOF_FULLVOLSEESOUND = 16; -const int LOF_NOJUMP = 32; - -// Flags for A_Respawn -const int RSF_FOG = 1; -const int RSF_KEEPTARGET = 2; -const int RSF_TELEFRAG = 4; - -// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS -const int JLOSF_PROJECTILE = 1; -const int JLOSF_NOSIGHT = 2; -const int JLOSF_CLOSENOFOV = 4; -const int JLOSF_CLOSENOSIGHT = 8; -const int JLOSF_CLOSENOJUMP = 16; -const int JLOSF_DEADNOJUMP = 32; -const int JLOSF_CHECKMASTER = 64; -const int JLOSF_TARGETLOS = 128; -const int JLOSF_FLIPFOV = 256; -const int JLOSF_ALLYNOJUMP = 512; -const int JLOSF_COMBATANTONLY = 1024; -const int JLOSF_NOAUTOAIM = 2048; - -// Flags for A_ChangeVelocity -const int CVF_RELATIVE = 1; -const int CVF_REPLACE = 2; - -// Flags for A_WeaponReady -const int WRF_NOBOB = 1; -const int WRF_NOSWITCH = 2; -const int WRF_NOPRIMARY = 4; -const int WRF_NOSECONDARY = 8; -const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; -const int WRF_ALLOWRELOAD = 16; -const int WRF_ALLOWZOOM = 32; -const int WRF_DISABLESWITCH = 64; - -// Morph constants -const int MRF_ADDSTAMINA = 1; -const int MRF_FULLHEALTH = 2; -const int MRF_UNDOBYTOMEOFPOWER = 4; -const int MRF_UNDOBYCHAOSDEVICE = 8; -const int MRF_FAILNOTELEFRAG = 16; -const int MRF_FAILNOLAUGH = 32; -const int MRF_WHENINVULNERABLE = 64; -const int MRF_LOSEACTUALWEAPON = 128; -const int MRF_NEWTIDBEHAVIOUR = 256; -const int MRF_UNDOBYDEATH = 512; -const int MRF_UNDOBYDEATHFORCED = 1024; -const int MRF_UNDOBYDEATHSAVES = 2048; - -// Flags for A_RailAttack and A_CustomRailgun -const int RGF_SILENT = 1; -const int RGF_NOPIERCING = 2; -const int RGF_EXPLICITANGLE = 4; -const int RGF_FULLBRIGHT = 8; -const int RGF_CENTERZ = 16; - -// Flags for A_Mushroom -const int MSF_Standard = 0; -const int MSF_Classic = 1; -const int MSF_DontHurt = 2; - -// Flags for A_Explode -const int XF_HURTSOURCE = 1; -const int XF_NOTMISSILE = 4; - -// Flags for A_RadiusThrust -const int RTF_AFFECTSOURCE = 1; -const int RTF_NOIMPACTDAMAGE = 2; -const int RTF_NOTMISSILE = 4; - -// Flags for A_Blast -const int BF_USEAMMO = 1; -const int BF_DONTWARN = 2; -const int BF_AFFECTBOSSES = 4; -const int BF_NOIMPACTDAMAGE = 8; - -// Flags for A_SeekerMissile -const int SMF_LOOK = 1; -const int SMF_PRECISE = 2; -const int SMF_CURSPEED = 4; - -// Flags for A_CustomPunch -const int CPF_USEAMMO = 1; -const int CPF_DAGGER = 2; -const int CPF_PULLIN = 4; -const int CPF_NORANDOMPUFFZ = 8; - -// Flags for A_CustomMissile -const int FPF_AIMATANGLE = 1; -const int FPF_TRANSFERTRANSLATION = 2; - -// Flags for A_Teleport -const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; - -// Flags for A_WolfAttack -const int WAF_NORANDOM = 1; -const int WAF_USEPUFF = 2; - -// Flags for A_RadiusGive -enum -{ - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -// Activation flags -enum -{ - THINGSPEC_Default = 0, - THINGSPEC_ThingActs = 1, - THINGSPEC_ThingTargets = 2, - THINGSPEC_TriggerTargets = 4, - THINGSPEC_MonsterTrigger = 8, - THINGSPEC_MissileTrigger = 16, - THINGSPEC_ClearSpecial = 32, - THINGSPEC_NoDeathSpecial = 64, - THINGSPEC_TriggerActs = 128, -}; -// Shorter aliases for same -const int AF_Default = 0; -const int AF_ThingActs = 1; -const int AF_ThingTargets = 2; -const int AF_TriggerTargets = 4; -const int AF_MonsterTrigger = 8; -const int AF_MissileTrigger = 16; -const int AF_ClearSpecial = 32; -const int AF_NoDeathSpecial = 64; -const int AF_TriggerActs = 128; - -// Flags for A_TakeInventory and A_TakeFromTarget -const int TIF_NOTAKEINFINITE = 1; - -// constants for A_PlaySound -enum -{ - CHAN_AUTO = 0, - CHAN_WEAPON = 1, - CHAN_VOICE = 2, - CHAN_ITEM = 3, - CHAN_BODY = 4, - CHAN_5 = 5, - CHAN_6 = 6, - CHAN_7 = 7, - - // modifier flags - CHAN_LISTENERZ = 8, - CHAN_MAYBE_LOCAL = 16, - CHAN_UI = 32, - CHAN_NOPAUSE = 64 -}; - -// sound attenuation values -const float ATTN_NONE = 0; -const float ATTN_NORM = 1; -const float ATTN_IDLE = 1.001; -const float ATTN_STATIC = 3; - -// For SetPlayerProprty action special -Const Int PROP_FROZEN = 0; -Const Int PROP_NOTARGET = 1; -Const Int PROP_INSTANTWEAPONSWITCH = 2; -Const Int PROP_FLY = 3; -Const Int PROP_TOTALLYFROZEN = 4; -Const Int PROP_INVULNERABILITY = 5; // (Deprecated) -Const Int PROP_STRENGTH = 6; // (Deprecated) -Const Int PROP_INVISIBILITY = 7; // (Deprecated) -Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) -Const Int PROP_ALLMAP = 9; // (Deprecated) -Const Int PROP_INFRARED = 10; // (Deprecated) -Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) -Const Int PROP_FLIGHT = 12; // (Deprecated) -Const Int PROP_SPEED = 15; // (Deprecated) -Const Int PROP_BUDDHA = 16; - -// Line_SetBlocking -Const Int BLOCKF_CREATURES = 1; -Const Int BLOCKF_MONSTERS = 2; -Const Int BLOCKF_PLAYERS = 4; -Const Int BLOCKF_FLOATERS = 8; -Const Int BLOCKF_PROJECTILES = 16; -Const Int BLOCKF_EVERYTHING = 32; -Const Int BLOCKF_RAILING = 64; -Const Int BLOCKF_USE = 128; - -// Pointer constants, bitfield-enabled - -Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 0x1; -Const Int AAPTR_TARGET = 0x2; -Const Int AAPTR_MASTER = 0x4; -Const Int AAPTR_TRACER = 0x8; - -Const Int AAPTR_PLAYER_GETTARGET = 0x10; -Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; - -Const Int AAPTR_PLAYER1 = 0x40; -Const Int AAPTR_PLAYER2 = 0x80; -Const Int AAPTR_PLAYER3 = 0x100; -Const Int AAPTR_PLAYER4 = 0x200; -Const Int AAPTR_PLAYER5 = 0x400; -Const Int AAPTR_PLAYER6 = 0x800; -Const Int AAPTR_PLAYER7 = 0x1000; -Const Int AAPTR_PLAYER8 = 0x2000; - -Const Int AAPTR_FRIENDPLAYER = 0x4000; -Const Int AAPTR_LINETARGET = 0x8000; - -// Pointer operation flags - -Const Int PTROP_UNSAFETARGET = 1; -Const Int PTROP_UNSAFEMASTER = 2; -Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; - - -// Flags for A_Warp - -Const Int WARPF_ABSOLUTEOFFSET = 0x1; -Const Int WARPF_ABSOLUTEANGLE = 0x2; -Const Int WARPF_USECALLERANGLE = 0x4; -Const Int WARPF_NOCHECKPOSITION = 0x8; -Const Int WARPF_INTERPOLATE = 0x10; -Const Int WARPF_WARPINTERPOLATION = 0x20; -Const Int WARPF_COPYINTERPOLATION = 0x40; -Const Int WARPF_STOP = 0x80; -Const Int WARPF_TOFLOOR = 0x100; -Const Int WARPF_TESTONLY = 0x200; -Const Int WAPRF_ABSOLUTEPOSITION = 0x400; - -// flags for A_SetPitch/SetAngle -const int SPF_FORCECLAMP = 1; -const int SPF_INTERPOLATE = 2; - - -// flags for A_CheckLOF - -enum -{ - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, - - CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, - CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ -}; - -// 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; - -// 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; - -// Flags for A_AlertMonsters -const int AMF_TARGETEMITTER = 1; -const int AMF_TARGETNONPLAYER = 2; -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 -}; - - -// This is only here to provide one global variable for testing. -native int testglobalvar; + +// Flags for A_PainAttack +const int PAF_NOSKULLATTACK = 1; +const int PAF_AIMFACING = 2; +const int PAF_NOTARGET = 4; + +// Flags for A_VileAttack +const int VAF_DMGTYPEAPPLYTODIRECT = 1; + +// Flags for A_Saw +const int SF_NORANDOM = 1; +const int SF_RANDOMLIGHTMISS = 2; +const int SF_RANDOMLIGHTHIT = 4; +const int SF_RANDOMLIGHTBOTH = 6; +const int SF_NOUSEAMMOMISS = 8; +const int SF_NOUSEAMMO = 16; +const int SF_NOPULLIN = 32; +const int SF_NOTURN = 64; + +// Flags for A_CustomMissile +const int CMF_AIMOFFSET = 1; +const int CMF_AIMDIRECTION = 2; +const int CMF_TRACKOWNER = 4; +const int CMF_CHECKTARGETDEAD = 8; +const int CMF_ABSOLUTEPITCH = 16; +const int CMF_OFFSETPITCH = 32; +const int CMF_SAVEPITCH = 64; +const int CMF_ABSOLUTEANGLE = 128; + +// Flags for A_CustomBulletAttack +const int CBAF_AIMFACING = 1; +const int CBAF_NORANDOM = 2; +const int CBAF_EXPLICITANGLE = 4; +const int CBAF_NOPITCH = 8; +const int CBAF_NORANDOMPUFFZ = 16; + +// Flags for A_GunFlash +const int GFF_NOEXTCHANGE = 1; + +// Flags for A_FireBullets +const int FBF_USEAMMO = 1; +const int FBF_NORANDOM = 2; +const int FBF_EXPLICITANGLE = 4; +const int FBF_NOPITCH = 8; +const int FBF_NOFLASH = 16; +const int FBF_NORANDOMPUFFZ = 32; + +// Flags for A_SpawnItemEx +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; + +// Flags for A_Chase +const int CHF_FASTCHASE = 1; +const int CHF_NOPLAYACTIVE = 2; +const int CHF_NIGHTMAREFAST = 4; +const int CHF_RESURRECT = 8; +const int CHF_DONTMOVE = 16; + +// Flags for A_LookEx +const int LOF_NOSIGHTCHECK = 1; +const int LOF_NOSOUNDCHECK = 2; +const int LOF_DONTCHASEGOAL = 4; +const int LOF_NOSEESOUND = 8; +const int LOF_FULLVOLSEESOUND = 16; +const int LOF_NOJUMP = 32; + +// Flags for A_Respawn +const int RSF_FOG = 1; +const int RSF_KEEPTARGET = 2; +const int RSF_TELEFRAG = 4; + +// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS +enum +{ + 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, +}; + +// Flags for A_ChangeVelocity +const int CVF_RELATIVE = 1; +const int CVF_REPLACE = 2; + +// Flags for A_WeaponReady +const int WRF_NOBOB = 1; +const int WRF_NOSWITCH = 2; +const int WRF_NOPRIMARY = 4; +const int WRF_NOSECONDARY = 8; +const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; +const int WRF_ALLOWRELOAD = 16; +const int WRF_ALLOWZOOM = 32; +const int WRF_DISABLESWITCH = 64; + +// Morph constants +const int MRF_ADDSTAMINA = 1; +const int MRF_FULLHEALTH = 2; +const int MRF_UNDOBYTOMEOFPOWER = 4; +const int MRF_UNDOBYCHAOSDEVICE = 8; +const int MRF_FAILNOTELEFRAG = 16; +const int MRF_FAILNOLAUGH = 32; +const int MRF_WHENINVULNERABLE = 64; +const int MRF_LOSEACTUALWEAPON = 128; +const int MRF_NEWTIDBEHAVIOUR = 256; +const int MRF_UNDOBYDEATH = 512; +const int MRF_UNDOBYDEATHFORCED = 1024; +const int MRF_UNDOBYDEATHSAVES = 2048; + +// Flags for A_RailAttack and A_CustomRailgun +const int RGF_SILENT = 1; +const int RGF_NOPIERCING = 2; +const int RGF_EXPLICITANGLE = 4; +const int RGF_FULLBRIGHT = 8; +const int RGF_CENTERZ = 16; + +// Flags for A_Mushroom +const int MSF_Standard = 0; +const int MSF_Classic = 1; +const int MSF_DontHurt = 2; + +// Flags for A_Explode +const int XF_HURTSOURCE = 1; +const int XF_NOTMISSILE = 4; + +// Flags for A_RadiusThrust +const int RTF_AFFECTSOURCE = 1; +const int RTF_NOIMPACTDAMAGE = 2; +const int RTF_NOTMISSILE = 4; + +// Flags for A_Blast +const int BF_USEAMMO = 1; +const int BF_DONTWARN = 2; +const int BF_AFFECTBOSSES = 4; +const int BF_NOIMPACTDAMAGE = 8; + +// Flags for A_SeekerMissile +const int SMF_LOOK = 1; +const int SMF_PRECISE = 2; +const int SMF_CURSPEED = 4; + +// Flags for A_CustomPunch +const int CPF_USEAMMO = 1; +const int CPF_DAGGER = 2; +const int CPF_PULLIN = 4; +const int CPF_NORANDOMPUFFZ = 8; + +// Flags for A_CustomMissile +const int FPF_AIMATANGLE = 1; +const int FPF_TRANSFERTRANSLATION = 2; + +// Flags for A_Teleport +const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; + +// Flags for A_WolfAttack +const int WAF_NORANDOM = 1; +const int WAF_USEPUFF = 2; + +// Flags for A_RadiusGive +enum +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +// Activation flags +enum +{ + THINGSPEC_Default = 0, + THINGSPEC_ThingActs = 1, + THINGSPEC_ThingTargets = 2, + THINGSPEC_TriggerTargets = 4, + THINGSPEC_MonsterTrigger = 8, + THINGSPEC_MissileTrigger = 16, + THINGSPEC_ClearSpecial = 32, + THINGSPEC_NoDeathSpecial = 64, + THINGSPEC_TriggerActs = 128, +}; +// Shorter aliases for same +const int AF_Default = 0; +const int AF_ThingActs = 1; +const int AF_ThingTargets = 2; +const int AF_TriggerTargets = 4; +const int AF_MonsterTrigger = 8; +const int AF_MissileTrigger = 16; +const int AF_ClearSpecial = 32; +const int AF_NoDeathSpecial = 64; +const int AF_TriggerActs = 128; + +// Flags for A_TakeInventory and A_TakeFromTarget +const int TIF_NOTAKEINFINITE = 1; + +// constants for A_PlaySound +enum +{ + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_5 = 5, + CHAN_6 = 6, + CHAN_7 = 7, + + // modifier flags + CHAN_LISTENERZ = 8, + CHAN_MAYBE_LOCAL = 16, + CHAN_UI = 32, + CHAN_NOPAUSE = 64 +}; + +// sound attenuation values +const float ATTN_NONE = 0; +const float ATTN_NORM = 1; +const float ATTN_IDLE = 1.001; +const float ATTN_STATIC = 3; + +// For SetPlayerProprty action special +Const Int PROP_FROZEN = 0; +Const Int PROP_NOTARGET = 1; +Const Int PROP_INSTANTWEAPONSWITCH = 2; +Const Int PROP_FLY = 3; +Const Int PROP_TOTALLYFROZEN = 4; +Const Int PROP_INVULNERABILITY = 5; // (Deprecated) +Const Int PROP_STRENGTH = 6; // (Deprecated) +Const Int PROP_INVISIBILITY = 7; // (Deprecated) +Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) +Const Int PROP_ALLMAP = 9; // (Deprecated) +Const Int PROP_INFRARED = 10; // (Deprecated) +Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) +Const Int PROP_FLIGHT = 12; // (Deprecated) +Const Int PROP_SPEED = 15; // (Deprecated) +Const Int PROP_BUDDHA = 16; + +// Line_SetBlocking +Const Int BLOCKF_CREATURES = 1; +Const Int BLOCKF_MONSTERS = 2; +Const Int BLOCKF_PLAYERS = 4; +Const Int BLOCKF_FLOATERS = 8; +Const Int BLOCKF_PROJECTILES = 16; +Const Int BLOCKF_EVERYTHING = 32; +Const Int BLOCKF_RAILING = 64; +Const Int BLOCKF_USE = 128; + +// Pointer constants, bitfield-enabled + +Const Int AAPTR_DEFAULT = 0; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; + +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; + +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; + +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; + +// Pointer operation flags + +Const Int PTROP_UNSAFETARGET = 1; +Const Int PTROP_UNSAFEMASTER = 2; +Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; + + +// Flags for A_Warp + +Const Int WARPF_ABSOLUTEOFFSET = 0x1; +Const Int WARPF_ABSOLUTEANGLE = 0x2; +Const Int WARPF_USECALLERANGLE = 0x4; +Const Int WARPF_NOCHECKPOSITION = 0x8; +Const Int WARPF_INTERPOLATE = 0x10; +Const Int WARPF_WARPINTERPOLATION = 0x20; +Const Int WARPF_COPYINTERPOLATION = 0x40; +Const Int WARPF_STOP = 0x80; +Const Int WARPF_TOFLOOR = 0x100; +Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; + +// flags for A_SetPitch/SetAngle +const int SPF_FORCECLAMP = 1; +const int SPF_INTERPOLATE = 2; + + +// flags for A_CheckLOF + +enum +{ + CLOFF_NOAIM_VERT = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, + + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, + CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ +}; + +// 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; + +// 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; + +// Flags for A_AlertMonsters +const int AMF_TARGETEMITTER = 1; +const int AMF_TARGETNONPLAYER = 2; +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, +}; + + +// This is only here to provide one global variable for testing. +native int testglobalvar; From 0f9a8176f54185d6ee1b73f1819788f2c92491ee Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 16:57:31 +1300 Subject: [PATCH 113/313] Unprediction needs to restore selected inventory --- src/p_user.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 7b049a2df..3bbebaffb 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2744,9 +2744,12 @@ void P_UnPredictPlayer () if (player->cheats & CF_PREDICTING) { unsigned int i; - AActor *act = player->mo; + APlayerPawn *act = player->mo; AActor *savedcamera = player->camera; + TObjPtr InvSel = act->InvSel; + int inventorytics = player->inventorytics; + *player = PredictionPlayerBackup; // Restore the camera instead of using the backup's copy, because spynext/prev @@ -2859,6 +2862,9 @@ void P_UnPredictPlayer () } block = block->NextBlock; } + + act->InvSel = InvSel; + player->inventorytics = inventorytics; } } From ae71f94ed2679ac7c8ba45ea83ace50896aa222c Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:04:11 +1300 Subject: [PATCH 114/313] Remove disconnecting players safely Players who were disconnecting would be removed outside of the playsims control, causing problems with sector lists. --- src/d_net.cpp | 75 ++++++++++++++------------------------------------ src/d_player.h | 3 +- src/g_game.cpp | 63 ++++++++++++++++++++++++++++++++++++++++-- src/g_game.h | 1 + 4 files changed, 83 insertions(+), 59 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index d33f47e38..0e057369c 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -39,7 +39,6 @@ #include "cmdlib.h" #include "s_sound.h" #include "m_cheat.h" -#include "p_effect.h" #include "p_local.h" #include "c_dispatch.h" #include "sbar.h" @@ -670,6 +669,9 @@ void PlayerIsGone (int netnode, int netconsole) { int i; + if (!nodeingame[netnode]) + return; + for (i = netnode + 1; i < doomcom.numnodes; ++i) { if (nodeingame[i]) @@ -680,55 +682,17 @@ void PlayerIsGone (int netnode, int netconsole) doomcom.numnodes = netnode; } + if (playeringame[netconsole]) + { + players[netconsole].playerstate = PST_GONE; + } nodeingame[netnode] = false; - playeringame[netconsole] = false; nodejustleft[netnode] = false; - if (deathmatch) - { - Printf ("%s left the game with %d frags\n", - players[netconsole].userinfo.GetName(), - players[netconsole].fragcount); - } - else - { - Printf ("%s left the game\n", players[netconsole].userinfo.GetName()); - } - - // [RH] Revert each player to their own view if spying through the player who left - for (int ii = 0; ii < MAXPLAYERS; ++ii) - { - if (playeringame[ii] && players[ii].camera == players[netconsole].mo) - { - players[ii].camera = players[ii].mo; - if (ii == consoleplayer && StatusBar != NULL) - { - StatusBar->AttachToPlayer (&players[ii]); - } - } - } - - // [RH] Make the player disappear - FBehavior::StaticStopMyScripts (players[netconsole].mo); - if (players[netconsole].mo != NULL) - { - P_DisconnectEffect (players[netconsole].mo); - players[netconsole].mo->player = NULL; - players[netconsole].mo->Destroy (); - if (!(players[netconsole].mo->ObjectFlags & OF_EuthanizeMe)) - { // We just destroyed a morphed player, so now the original player - // has taken their place. Destroy that one too. - players[netconsole].mo->Destroy(); - } - players[netconsole].mo = NULL; - players[netconsole].camera = NULL; - } - // [RH] Let the scripts know the player left - FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, netconsole); if (netconsole == Net_Arbitrator) { - bglobal.RemoveAllBots (true); - Printf ("Removed all bots\n"); + bglobal.RemoveAllBots(true); + Printf("Removed all bots\n"); // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) @@ -737,20 +701,21 @@ void PlayerIsGone (int netnode, int netconsole) { Net_Arbitrator = i; players[i].settings_controller = true; - Printf ("%s is the new arbitrator\n", players[i].userinfo.GetName()); + Printf("%s is the new arbitrator\n", players[i].userinfo.GetName()); break; } } - if (debugfile && NetMode == NET_PacketServer) + } + + if (debugfile && NetMode == NET_PacketServer) + { + if (Net_Arbitrator == consoleplayer) { - if (Net_Arbitrator == consoleplayer) - { - fprintf (debugfile, "I am the new master!\n"); - } - else - { - fprintf (debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]); - } + fprintf(debugfile, "I am the new master!\n"); + } + else + { + fprintf(debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]); } } diff --git a/src/d_player.h b/src/d_player.h index f50db97b1..4ee147dfe 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -176,7 +176,8 @@ typedef enum PST_LIVE, // Playing or camping. PST_DEAD, // Dead on the ground, view follows killer. PST_REBORN, // Ready to restart/respawn??? - PST_ENTER // [BC] Entered the game + PST_ENTER, // [BC] Entered the game + PST_GONE // Player has left the game } playerstate_t; diff --git a/src/g_game.cpp b/src/g_game.cpp index 2058c2df0..95b0f587d 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -76,6 +76,7 @@ #include "d_net.h" #include "d_event.h" #include "p_acs.h" +#include "p_effect.h" #include "m_joy.h" #include "farchive.h" #include "r_renderer.h" @@ -1013,10 +1014,16 @@ void G_Ticker () // do player reborns if needed for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && - (players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER)) + if (playeringame[i]) { - G_DoReborn (i, false); + if ((players[i].playerstate == PST_GONE)) + { + G_DoPlayerPop(i); + } + if ((players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER)) + { + G_DoReborn(i, false); + } } } @@ -1658,6 +1665,56 @@ void G_DoReborn (int playernum, bool freshbot) } } +// +// G_DoReborn +// +void G_DoPlayerPop(int playernum) +{ + playeringame[playernum] = false; + + if (deathmatch) + { + Printf("%s left the game with %d frags\n", + players[playernum].userinfo.GetName(), + players[playernum].fragcount); + } + else + { + Printf("%s left the game\n", players[playernum].userinfo.GetName()); + } + + // [RH] Revert each player to their own view if spying through the player who left + for (int ii = 0; ii < MAXPLAYERS; ++ii) + { + if (playeringame[ii] && players[ii].camera == players[playernum].mo) + { + players[ii].camera = players[ii].mo; + if (ii == consoleplayer && StatusBar != NULL) + { + StatusBar->AttachToPlayer(&players[ii]); + } + } + } + + // [RH] Make the player disappear + FBehavior::StaticStopMyScripts(players[playernum].mo); + if (players[playernum].mo != NULL) + { + P_DisconnectEffect(players[playernum].mo); + players[playernum].mo->player = NULL; + players[playernum].mo->Destroy(); + if (!(players[playernum].mo->ObjectFlags & OF_EuthanizeMe)) + { // We just destroyed a morphed player, so now the original player + // has taken their place. Destroy that one too. + players[playernum].mo->Destroy(); + } + players[playernum].mo = NULL; + players[playernum].camera = NULL; + } + // [RH] Let the scripts know the player left + FBehavior::StaticStartTypedScripts(SCRIPT_Disconnect, NULL, true, playernum); +} + void G_ScreenShot (char *filename) { shotfile = filename; diff --git a/src/g_game.h b/src/g_game.h index 051be86b1..4714d8b55 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -81,6 +81,7 @@ enum EFinishLevelType void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags); void G_DoReborn (int playernum, bool freshbot); +void G_DoPlayerPop(int playernum); // Adds pitch to consoleplayer's viewpitch and clamps it void G_AddViewPitch (int look); From 0c1fde81ad9097421040cfa5445ed987c5395e7a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:32:32 +1300 Subject: [PATCH 115/313] Remove PacketServer auto select PacketServer has the tendency to amplify netgame latency times, so it really shouldn't be auto selected. Added a notice in case it's used. --- src/d_net.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 0e057369c..f15bdf45d 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole) // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].isbot) + if (i != netconsole && playeringame[i] && !players[i].isbot) { Net_Arbitrator = i; players[i].settings_controller = true; @@ -1725,10 +1725,19 @@ void D_CheckNetGame (void) resendto[i] = 0; // which tic to start sending } + // Packet server has proven to be rather slow over the internet. Print a warning about it. + v = Args->CheckValue("-netmode"); + if (v != NULL && (atoi(v) != 0)) + { + Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is unreliable and is prone to running too slow on some internet configurations." + "\nIf the game is running well below excpected speeds, use netmode 0 (P2P) instead.\n"); + } + // I_InitNetwork sets doomcom and netgame if (I_InitNetwork ()) { - NetMode = NET_PacketServer; + // For now, stop auto selecting PacketServer, as it's more likely to cause confusion. + //NetMode = NET_PacketServer; } if (doomcom.id != DOOMCOM_ID) { From c9d603d1c3313b208d83fc9a3c5a3de54f1c33a3 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:52:56 +1300 Subject: [PATCH 116/313] Unreliable isn't exactly the problem --- src/d_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index f15bdf45d..bc4bc7352 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1729,7 +1729,7 @@ void D_CheckNetGame (void) v = Args->CheckValue("-netmode"); if (v != NULL && (atoi(v) != 0)) { - Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is unreliable and is prone to running too slow on some internet configurations." + Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is prone to running too slow on some internet configurations." "\nIf the game is running well below excpected speeds, use netmode 0 (P2P) instead.\n"); } From 71753743504660e73a25ec29c6328a0818f8fd42 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 19:46:34 +1300 Subject: [PATCH 117/313] Keep gateway sockets open during handshake --- src/i_net.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/i_net.cpp b/src/i_net.cpp index 3ec9d7881..6fdcb52ba 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -110,6 +110,7 @@ const char *neterror (void); enum { PRE_CONNECT, // Sent from guest to host for initial connection + PRE_KEEPALIVE, PRE_DISCONNECT, // Sent from guest that aborts the game PRE_ALLHERE, // Sent from host to guest when everybody has connected PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt @@ -548,10 +549,15 @@ bool Host_CheckForConnects (void *userdata) SendConAck (doomcom.numnodes, numplayers); } break; + + case PRE_KEEPALIVE: + break; } } if (doomcom.numnodes < numplayers) { + // Send message to everyone as a keepalive + SendConAck(doomcom.numnodes, numplayers); return false; } @@ -822,6 +828,10 @@ bool Guest_WaitForOthers (void *userdata) } } + packet.Fake = PRE_FAKE; + packet.Message = PRE_KEEPALIVE; + PreSend(&packet, 2, &sendaddress[1]); + return false; } From e1b8b4a8716e92316d4228469913ab2deb6dcf2b Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 6 Oct 2014 15:27:13 +1300 Subject: [PATCH 118/313] Started work on prediction lerping --- src/p_user.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 3bbebaffb..e7e130e56 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -63,6 +63,22 @@ static FRandom pr_skullpop ("SkullPop"); // Variables for prediction CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +#define LERPSCALE 0.05 + +struct PredictCheck +{ + int gametic; + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t pitch; + fixed_t yaw; + bool onground; +} static PredictionResults[BACKUPTICS], PredictionResult_Last; +static int PredictionLerptics; +static int PredictionMaxLerptics; + static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; static TArray PredictionTouchingSectorsBackup; @@ -1882,6 +1898,7 @@ void P_MovePlayer (player_t *player) else { mo->angle += cmd->ucmd.yaw << 16; + Printf("%d\n", cmd->ucmd.yaw); } player->onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2); @@ -2648,7 +2665,7 @@ void P_PredictPlayer (player_t *player) player->mo == NULL || player != &players[consoleplayer] || player->playerstate != PST_LIVE || - !netgame || + //!netgame || /*player->morphTics ||*/ (player->cheats & CF_PREDICTING)) { @@ -2665,8 +2682,8 @@ void P_PredictPlayer (player_t *player) // Save original values for restoration later PredictionPlayerBackup = *player; - AActor *act = player->mo; - memcpy (PredictionActorBackup, &act->x, sizeof(AActor)-((BYTE *)&act->x-(BYTE *)act)); + APlayerPawn *act = player->mo; + memcpy(PredictionActorBackup, &act->x, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act)); act->flags &= ~MF_PICKUP; act->flags2 &= ~MF2_PUSHWALL; @@ -2732,7 +2749,49 @@ void P_PredictPlayer (player_t *player) player->cmd = localcmds[i % LOCALCMDTICS]; P_PlayerThink (player); player->mo->Tick (); + + if (PredictionResults[i % BACKUPTICS].gametic && i == PredictionResults[i % BACKUPTICS].gametic && !NoInterpolateOld && PredictionLerptics >= PredictionMaxLerptics) + { + if (PredictionResults[i % BACKUPTICS].x != player->mo->x || + PredictionResults[i % BACKUPTICS].y != player->mo->y || + (PredictionResults[i % BACKUPTICS].z != player->mo->z && PredictionResults[i % BACKUPTICS].onground && player->onground)) + // If the player was always on the ground, they might be on a lift, and lerping would be disruptive on z height changes alone + { + PredictionLerptics = 0; + PredictionMaxLerptics = (maxtic - gametic); + } + } + + PredictionResults[i % BACKUPTICS].gametic = i; + PredictionResults[i % BACKUPTICS].x = player->mo->x; + PredictionResults[i % BACKUPTICS].y = player->mo->y; + PredictionResults[i % BACKUPTICS].z = player->mo->z; + PredictionResults[i % BACKUPTICS].onground = player->onground; } + + if (PredictionLerptics < PredictionMaxLerptics) + { + PredictionLerptics++; + FVector3 pointold, pointnew, step, difference, result; + pointold.X = FIXED2FLOAT(PredictionResult_Last.x); // Old player pos + pointold.Y = FIXED2FLOAT(PredictionResult_Last.y); + pointold.Z = FIXED2FLOAT(PredictionResult_Last.z); + pointnew.X = FIXED2FLOAT(player->mo->x); // New player pos + pointnew.Y = FIXED2FLOAT(player->mo->y); + pointnew.Z = FIXED2FLOAT(player->mo->z); + + difference = pointnew - pointold; + step = difference / PredictionMaxLerptics; + result = step * PredictionLerptics; + result += pointold; + + player->mo->x = FLOAT2FIXED(result.X); + player->mo->y = FLOAT2FIXED(result.Y); + player->mo->z = FLOAT2FIXED(result.Z); + Printf("Lerped! x%f y%f z%f\n", result.X, result.Y, result.Z); + } + if (PredictionLerptics >= PredictionMaxLerptics) + PredictionResult_Last = PredictionResults[(maxtic - 1) % BACKUPTICS]; } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); @@ -2757,7 +2816,7 @@ void P_UnPredictPlayer () player->camera = savedcamera; act->UnlinkFromWorld(); - memcpy(&act->x, PredictionActorBackup, sizeof(AActor)-((BYTE *)&act->x - (BYTE *)act)); + memcpy(&act->x, PredictionActorBackup, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act)); // The blockmap ordering needs to remain unchanged, too. // Restore sector links and refrences. From 3eaebabdf114c4ae9d6d8590e4e8681692af704d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 6 Oct 2014 17:40:02 +0200 Subject: [PATCH 119/313] - fixed: EV_Teleport must not access the thing's player pointer before ensuring that 'thing' is a valid pointer. --- src/p_teleport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index b64c04a5a..60308d8ed 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -328,7 +328,6 @@ static AActor *SelectTeleDest (int tid, int tag, bool norandom) bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight) { - bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); AActor *searcher; fixed_t z; @@ -341,6 +340,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool { // Teleport function called with an invalid actor return false; } + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); if (thing->flags2 & MF2_NOTELEPORT) { return false; From 47029a3efca0b61f05477a05ab28f518a61507c6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 7 Oct 2014 11:33:08 -0500 Subject: [PATCH 120/313] DoKill bug -Fixed: DoKill did not target 'killtarget' for missiles, causing crashes and making the function not work properly. --- 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 65888d0d5..c513878fc 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5002,7 +5002,7 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags //since that's the whole point of it. if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) { - P_ExplodeMissile(self->target, NULL, NULL); + P_ExplodeMissile(killtarget, NULL, NULL); } } if (!(flags & KILS_NOMONSTERS)) From f766a1ab388d40769e299b733d0b72e8d6484b09 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 11 Oct 2014 16:15:42 -0500 Subject: [PATCH 121/313] - Added SXF_ORIGINATOR. - Only useful for missiles. By default, missiles cannot set themselves as the master when spawning actors. --- src/thingdef/thingdef_codeptr.cpp | 15 +- wadsrc/static/actors/constants.txt | 793 +++++++++++++++-------------- 2 files changed, 406 insertions(+), 402 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c513878fc..2d53b4052 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1768,6 +1768,7 @@ enum SIX_Flags 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) @@ -1803,11 +1804,13 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { mo->pitch = self->pitch; } - while (originator && originator->isMissile()) + if (!(flags & SIXF_ORIGINATOR)) { - originator = originator->target; - } - + while (originator && originator->isMissile()) + { + originator = originator->target; + } + } if (flags & SIXF_TELEFRAG) { P_TeleportMove(mo, mo->x, mo->y, mo->z, true); @@ -5112,9 +5115,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) enum RMVF_flags { - RMVF_MISSILES = 1 << 0, + RMVF_MISSILES = 1 << 0, RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, + RMVF_MISC = 1 << 2, RMVF_EVERYTHING = 1 << 3, }; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 57de8e6ad..553b23112 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -1,396 +1,397 @@ - -// Flags for A_PainAttack -const int PAF_NOSKULLATTACK = 1; -const int PAF_AIMFACING = 2; -const int PAF_NOTARGET = 4; - -// Flags for A_VileAttack -const int VAF_DMGTYPEAPPLYTODIRECT = 1; - -// Flags for A_Saw -const int SF_NORANDOM = 1; -const int SF_RANDOMLIGHTMISS = 2; -const int SF_RANDOMLIGHTHIT = 4; -const int SF_RANDOMLIGHTBOTH = 6; -const int SF_NOUSEAMMOMISS = 8; -const int SF_NOUSEAMMO = 16; -const int SF_NOPULLIN = 32; -const int SF_NOTURN = 64; - -// Flags for A_CustomMissile -const int CMF_AIMOFFSET = 1; -const int CMF_AIMDIRECTION = 2; -const int CMF_TRACKOWNER = 4; -const int CMF_CHECKTARGETDEAD = 8; -const int CMF_ABSOLUTEPITCH = 16; -const int CMF_OFFSETPITCH = 32; -const int CMF_SAVEPITCH = 64; -const int CMF_ABSOLUTEANGLE = 128; - -// Flags for A_CustomBulletAttack -const int CBAF_AIMFACING = 1; -const int CBAF_NORANDOM = 2; -const int CBAF_EXPLICITANGLE = 4; -const int CBAF_NOPITCH = 8; -const int CBAF_NORANDOMPUFFZ = 16; - -// Flags for A_GunFlash -const int GFF_NOEXTCHANGE = 1; - -// Flags for A_FireBullets -const int FBF_USEAMMO = 1; -const int FBF_NORANDOM = 2; -const int FBF_EXPLICITANGLE = 4; -const int FBF_NOPITCH = 8; -const int FBF_NOFLASH = 16; -const int FBF_NORANDOMPUFFZ = 32; - -// Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1 << 0; -const int SXF_ABSOLUTEPOSITION = 1 << 1; -const int SXF_ABSOLUTEANGLE = 1 << 2; -const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility -const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. -const int SXF_SETMASTER = 1 << 4; -const int SXF_NOCHECKPOSITION = 1 << 5; -const int SXF_TELEFRAG = 1 << 6; -const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; -const int SXF_TRANSFERPITCH = 1 << 9; -const int SXF_TRANSFERPOINTERS = 1 << 10; -const int SXF_USEBLOODCOLOR = 1 << 11; -const int SXF_CLEARCALLERTID = 1 << 12; -const int SXF_MULTIPLYSPEED = 1 << 13; -const int SXF_TRANSFERSCALE = 1 << 14; -const int SXF_TRANSFERSPECIAL = 1 << 15; -const int SXF_CLEARCALLERSPECIAL = 1 << 16; -const int SXF_TRANSFERSTENCILCOL = 1 << 17; -const int SXF_TRANSFERALPHA = 1 << 18; -const int SXF_TRANSFERRENDERSTYLE = 1 << 19; -const int SXF_SETTARGET = 1 << 20; -const int SXF_SETTRACER = 1 << 21; -const int SXF_NOPOINTERS = 1 << 22; - -// Flags for A_Chase -const int CHF_FASTCHASE = 1; -const int CHF_NOPLAYACTIVE = 2; -const int CHF_NIGHTMAREFAST = 4; -const int CHF_RESURRECT = 8; -const int CHF_DONTMOVE = 16; - -// Flags for A_LookEx -const int LOF_NOSIGHTCHECK = 1; -const int LOF_NOSOUNDCHECK = 2; -const int LOF_DONTCHASEGOAL = 4; -const int LOF_NOSEESOUND = 8; -const int LOF_FULLVOLSEESOUND = 16; -const int LOF_NOJUMP = 32; - -// Flags for A_Respawn -const int RSF_FOG = 1; -const int RSF_KEEPTARGET = 2; -const int RSF_TELEFRAG = 4; - -// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS -const int JLOSF_PROJECTILE = 1; -const int JLOSF_NOSIGHT = 2; -const int JLOSF_CLOSENOFOV = 4; -const int JLOSF_CLOSENOSIGHT = 8; -const int JLOSF_CLOSENOJUMP = 16; -const int JLOSF_DEADNOJUMP = 32; -const int JLOSF_CHECKMASTER = 64; -const int JLOSF_TARGETLOS = 128; -const int JLOSF_FLIPFOV = 256; -const int JLOSF_ALLYNOJUMP = 512; -const int JLOSF_COMBATANTONLY = 1024; -const int JLOSF_NOAUTOAIM = 2048; - -// Flags for A_ChangeVelocity -const int CVF_RELATIVE = 1; -const int CVF_REPLACE = 2; - -// Flags for A_WeaponReady -const int WRF_NOBOB = 1; -const int WRF_NOSWITCH = 2; -const int WRF_NOPRIMARY = 4; -const int WRF_NOSECONDARY = 8; -const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; -const int WRF_ALLOWRELOAD = 16; -const int WRF_ALLOWZOOM = 32; -const int WRF_DISABLESWITCH = 64; - -// Morph constants -const int MRF_ADDSTAMINA = 1; -const int MRF_FULLHEALTH = 2; -const int MRF_UNDOBYTOMEOFPOWER = 4; -const int MRF_UNDOBYCHAOSDEVICE = 8; -const int MRF_FAILNOTELEFRAG = 16; -const int MRF_FAILNOLAUGH = 32; -const int MRF_WHENINVULNERABLE = 64; -const int MRF_LOSEACTUALWEAPON = 128; -const int MRF_NEWTIDBEHAVIOUR = 256; -const int MRF_UNDOBYDEATH = 512; -const int MRF_UNDOBYDEATHFORCED = 1024; -const int MRF_UNDOBYDEATHSAVES = 2048; - -// Flags for A_RailAttack and A_CustomRailgun -const int RGF_SILENT = 1; -const int RGF_NOPIERCING = 2; -const int RGF_EXPLICITANGLE = 4; -const int RGF_FULLBRIGHT = 8; -const int RGF_CENTERZ = 16; - -// Flags for A_Mushroom -const int MSF_Standard = 0; -const int MSF_Classic = 1; -const int MSF_DontHurt = 2; - -// Flags for A_Explode -const int XF_HURTSOURCE = 1; -const int XF_NOTMISSILE = 4; - -// Flags for A_RadiusThrust -const int RTF_AFFECTSOURCE = 1; -const int RTF_NOIMPACTDAMAGE = 2; -const int RTF_NOTMISSILE = 4; - -// Flags for A_Blast -const int BF_USEAMMO = 1; -const int BF_DONTWARN = 2; -const int BF_AFFECTBOSSES = 4; -const int BF_NOIMPACTDAMAGE = 8; - -// Flags for A_SeekerMissile -const int SMF_LOOK = 1; -const int SMF_PRECISE = 2; -const int SMF_CURSPEED = 4; - -// Flags for A_CustomPunch -const int CPF_USEAMMO = 1; -const int CPF_DAGGER = 2; -const int CPF_PULLIN = 4; -const int CPF_NORANDOMPUFFZ = 8; - -// Flags for A_CustomMissile -const int FPF_AIMATANGLE = 1; -const int FPF_TRANSFERTRANSLATION = 2; - -// Flags for A_Teleport -const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; - -// Flags for A_WolfAttack -const int WAF_NORANDOM = 1; -const int WAF_USEPUFF = 2; - -// Flags for A_RadiusGive -enum -{ - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -// Activation flags -enum -{ - THINGSPEC_Default = 0, - THINGSPEC_ThingActs = 1, - THINGSPEC_ThingTargets = 2, - THINGSPEC_TriggerTargets = 4, - THINGSPEC_MonsterTrigger = 8, - THINGSPEC_MissileTrigger = 16, - THINGSPEC_ClearSpecial = 32, - THINGSPEC_NoDeathSpecial = 64, - THINGSPEC_TriggerActs = 128, -}; -// Shorter aliases for same -const int AF_Default = 0; -const int AF_ThingActs = 1; -const int AF_ThingTargets = 2; -const int AF_TriggerTargets = 4; -const int AF_MonsterTrigger = 8; -const int AF_MissileTrigger = 16; -const int AF_ClearSpecial = 32; -const int AF_NoDeathSpecial = 64; -const int AF_TriggerActs = 128; - -// Flags for A_TakeInventory and A_TakeFromTarget -const int TIF_NOTAKEINFINITE = 1; - -// constants for A_PlaySound -enum -{ - CHAN_AUTO = 0, - CHAN_WEAPON = 1, - CHAN_VOICE = 2, - CHAN_ITEM = 3, - CHAN_BODY = 4, - CHAN_5 = 5, - CHAN_6 = 6, - CHAN_7 = 7, - - // modifier flags - CHAN_LISTENERZ = 8, - CHAN_MAYBE_LOCAL = 16, - CHAN_UI = 32, - CHAN_NOPAUSE = 64 -}; - -// sound attenuation values -const float ATTN_NONE = 0; -const float ATTN_NORM = 1; -const float ATTN_IDLE = 1.001; -const float ATTN_STATIC = 3; - -// For SetPlayerProprty action special -Const Int PROP_FROZEN = 0; -Const Int PROP_NOTARGET = 1; -Const Int PROP_INSTANTWEAPONSWITCH = 2; -Const Int PROP_FLY = 3; -Const Int PROP_TOTALLYFROZEN = 4; -Const Int PROP_INVULNERABILITY = 5; // (Deprecated) -Const Int PROP_STRENGTH = 6; // (Deprecated) -Const Int PROP_INVISIBILITY = 7; // (Deprecated) -Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) -Const Int PROP_ALLMAP = 9; // (Deprecated) -Const Int PROP_INFRARED = 10; // (Deprecated) -Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) -Const Int PROP_FLIGHT = 12; // (Deprecated) -Const Int PROP_SPEED = 15; // (Deprecated) -Const Int PROP_BUDDHA = 16; - -// Line_SetBlocking -Const Int BLOCKF_CREATURES = 1; -Const Int BLOCKF_MONSTERS = 2; -Const Int BLOCKF_PLAYERS = 4; -Const Int BLOCKF_FLOATERS = 8; -Const Int BLOCKF_PROJECTILES = 16; -Const Int BLOCKF_EVERYTHING = 32; -Const Int BLOCKF_RAILING = 64; -Const Int BLOCKF_USE = 128; - -// Pointer constants, bitfield-enabled - -Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 0x1; -Const Int AAPTR_TARGET = 0x2; -Const Int AAPTR_MASTER = 0x4; -Const Int AAPTR_TRACER = 0x8; - -Const Int AAPTR_PLAYER_GETTARGET = 0x10; -Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; - -Const Int AAPTR_PLAYER1 = 0x40; -Const Int AAPTR_PLAYER2 = 0x80; -Const Int AAPTR_PLAYER3 = 0x100; -Const Int AAPTR_PLAYER4 = 0x200; -Const Int AAPTR_PLAYER5 = 0x400; -Const Int AAPTR_PLAYER6 = 0x800; -Const Int AAPTR_PLAYER7 = 0x1000; -Const Int AAPTR_PLAYER8 = 0x2000; - -Const Int AAPTR_FRIENDPLAYER = 0x4000; -Const Int AAPTR_LINETARGET = 0x8000; - -// Pointer operation flags - -Const Int PTROP_UNSAFETARGET = 1; -Const Int PTROP_UNSAFEMASTER = 2; -Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; - - -// Flags for A_Warp - -Const Int WARPF_ABSOLUTEOFFSET = 0x1; -Const Int WARPF_ABSOLUTEANGLE = 0x2; -Const Int WARPF_USECALLERANGLE = 0x4; -Const Int WARPF_NOCHECKPOSITION = 0x8; -Const Int WARPF_INTERPOLATE = 0x10; -Const Int WARPF_WARPINTERPOLATION = 0x20; -Const Int WARPF_COPYINTERPOLATION = 0x40; -Const Int WARPF_STOP = 0x80; -Const Int WARPF_TOFLOOR = 0x100; -Const Int WARPF_TESTONLY = 0x200; -Const Int WAPRF_ABSOLUTEPOSITION = 0x400; - -// flags for A_SetPitch/SetAngle -const int SPF_FORCECLAMP = 1; -const int SPF_INTERPOLATE = 2; - - -// flags for A_CheckLOF - -enum -{ - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, - - CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, - CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ -}; - -// 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; - -// 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; - -// Flags for A_AlertMonsters -const int AMF_TARGETEMITTER = 1; -const int AMF_TARGETNONPLAYER = 2; -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 -}; - - -// This is only here to provide one global variable for testing. -native int testglobalvar; + +// Flags for A_PainAttack +const int PAF_NOSKULLATTACK = 1; +const int PAF_AIMFACING = 2; +const int PAF_NOTARGET = 4; + +// Flags for A_VileAttack +const int VAF_DMGTYPEAPPLYTODIRECT = 1; + +// Flags for A_Saw +const int SF_NORANDOM = 1; +const int SF_RANDOMLIGHTMISS = 2; +const int SF_RANDOMLIGHTHIT = 4; +const int SF_RANDOMLIGHTBOTH = 6; +const int SF_NOUSEAMMOMISS = 8; +const int SF_NOUSEAMMO = 16; +const int SF_NOPULLIN = 32; +const int SF_NOTURN = 64; + +// Flags for A_CustomMissile +const int CMF_AIMOFFSET = 1; +const int CMF_AIMDIRECTION = 2; +const int CMF_TRACKOWNER = 4; +const int CMF_CHECKTARGETDEAD = 8; +const int CMF_ABSOLUTEPITCH = 16; +const int CMF_OFFSETPITCH = 32; +const int CMF_SAVEPITCH = 64; +const int CMF_ABSOLUTEANGLE = 128; + +// Flags for A_CustomBulletAttack +const int CBAF_AIMFACING = 1; +const int CBAF_NORANDOM = 2; +const int CBAF_EXPLICITANGLE = 4; +const int CBAF_NOPITCH = 8; +const int CBAF_NORANDOMPUFFZ = 16; + +// Flags for A_GunFlash +const int GFF_NOEXTCHANGE = 1; + +// Flags for A_FireBullets +const int FBF_USEAMMO = 1; +const int FBF_NORANDOM = 2; +const int FBF_EXPLICITANGLE = 4; +const int FBF_NOPITCH = 8; +const int FBF_NOFLASH = 16; +const int FBF_NORANDOMPUFFZ = 32; + +// Flags for A_SpawnItemEx +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; +const int SXF_ORIGINATOR = 1 << 23; + +// Flags for A_Chase +const int CHF_FASTCHASE = 1; +const int CHF_NOPLAYACTIVE = 2; +const int CHF_NIGHTMAREFAST = 4; +const int CHF_RESURRECT = 8; +const int CHF_DONTMOVE = 16; + +// Flags for A_LookEx +const int LOF_NOSIGHTCHECK = 1; +const int LOF_NOSOUNDCHECK = 2; +const int LOF_DONTCHASEGOAL = 4; +const int LOF_NOSEESOUND = 8; +const int LOF_FULLVOLSEESOUND = 16; +const int LOF_NOJUMP = 32; + +// Flags for A_Respawn +const int RSF_FOG = 1; +const int RSF_KEEPTARGET = 2; +const int RSF_TELEFRAG = 4; + +// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS +const int JLOSF_PROJECTILE = 1; +const int JLOSF_NOSIGHT = 2; +const int JLOSF_CLOSENOFOV = 4; +const int JLOSF_CLOSENOSIGHT = 8; +const int JLOSF_CLOSENOJUMP = 16; +const int JLOSF_DEADNOJUMP = 32; +const int JLOSF_CHECKMASTER = 64; +const int JLOSF_TARGETLOS = 128; +const int JLOSF_FLIPFOV = 256; +const int JLOSF_ALLYNOJUMP = 512; +const int JLOSF_COMBATANTONLY = 1024; +const int JLOSF_NOAUTOAIM = 2048; + +// Flags for A_ChangeVelocity +const int CVF_RELATIVE = 1; +const int CVF_REPLACE = 2; + +// Flags for A_WeaponReady +const int WRF_NOBOB = 1; +const int WRF_NOSWITCH = 2; +const int WRF_NOPRIMARY = 4; +const int WRF_NOSECONDARY = 8; +const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; +const int WRF_ALLOWRELOAD = 16; +const int WRF_ALLOWZOOM = 32; +const int WRF_DISABLESWITCH = 64; + +// Morph constants +const int MRF_ADDSTAMINA = 1; +const int MRF_FULLHEALTH = 2; +const int MRF_UNDOBYTOMEOFPOWER = 4; +const int MRF_UNDOBYCHAOSDEVICE = 8; +const int MRF_FAILNOTELEFRAG = 16; +const int MRF_FAILNOLAUGH = 32; +const int MRF_WHENINVULNERABLE = 64; +const int MRF_LOSEACTUALWEAPON = 128; +const int MRF_NEWTIDBEHAVIOUR = 256; +const int MRF_UNDOBYDEATH = 512; +const int MRF_UNDOBYDEATHFORCED = 1024; +const int MRF_UNDOBYDEATHSAVES = 2048; + +// Flags for A_RailAttack and A_CustomRailgun +const int RGF_SILENT = 1; +const int RGF_NOPIERCING = 2; +const int RGF_EXPLICITANGLE = 4; +const int RGF_FULLBRIGHT = 8; +const int RGF_CENTERZ = 16; + +// Flags for A_Mushroom +const int MSF_Standard = 0; +const int MSF_Classic = 1; +const int MSF_DontHurt = 2; + +// Flags for A_Explode +const int XF_HURTSOURCE = 1; +const int XF_NOTMISSILE = 4; + +// Flags for A_RadiusThrust +const int RTF_AFFECTSOURCE = 1; +const int RTF_NOIMPACTDAMAGE = 2; +const int RTF_NOTMISSILE = 4; + +// Flags for A_Blast +const int BF_USEAMMO = 1; +const int BF_DONTWARN = 2; +const int BF_AFFECTBOSSES = 4; +const int BF_NOIMPACTDAMAGE = 8; + +// Flags for A_SeekerMissile +const int SMF_LOOK = 1; +const int SMF_PRECISE = 2; +const int SMF_CURSPEED = 4; + +// Flags for A_CustomPunch +const int CPF_USEAMMO = 1; +const int CPF_DAGGER = 2; +const int CPF_PULLIN = 4; +const int CPF_NORANDOMPUFFZ = 8; + +// Flags for A_CustomMissile +const int FPF_AIMATANGLE = 1; +const int FPF_TRANSFERTRANSLATION = 2; + +// Flags for A_Teleport +const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; + +// Flags for A_WolfAttack +const int WAF_NORANDOM = 1; +const int WAF_USEPUFF = 2; + +// Flags for A_RadiusGive +enum +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +// Activation flags +enum +{ + THINGSPEC_Default = 0, + THINGSPEC_ThingActs = 1, + THINGSPEC_ThingTargets = 2, + THINGSPEC_TriggerTargets = 4, + THINGSPEC_MonsterTrigger = 8, + THINGSPEC_MissileTrigger = 16, + THINGSPEC_ClearSpecial = 32, + THINGSPEC_NoDeathSpecial = 64, + THINGSPEC_TriggerActs = 128, +}; +// Shorter aliases for same +const int AF_Default = 0; +const int AF_ThingActs = 1; +const int AF_ThingTargets = 2; +const int AF_TriggerTargets = 4; +const int AF_MonsterTrigger = 8; +const int AF_MissileTrigger = 16; +const int AF_ClearSpecial = 32; +const int AF_NoDeathSpecial = 64; +const int AF_TriggerActs = 128; + +// Flags for A_TakeInventory and A_TakeFromTarget +const int TIF_NOTAKEINFINITE = 1; + +// constants for A_PlaySound +enum +{ + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_5 = 5, + CHAN_6 = 6, + CHAN_7 = 7, + + // modifier flags + CHAN_LISTENERZ = 8, + CHAN_MAYBE_LOCAL = 16, + CHAN_UI = 32, + CHAN_NOPAUSE = 64 +}; + +// sound attenuation values +const float ATTN_NONE = 0; +const float ATTN_NORM = 1; +const float ATTN_IDLE = 1.001; +const float ATTN_STATIC = 3; + +// For SetPlayerProprty action special +Const Int PROP_FROZEN = 0; +Const Int PROP_NOTARGET = 1; +Const Int PROP_INSTANTWEAPONSWITCH = 2; +Const Int PROP_FLY = 3; +Const Int PROP_TOTALLYFROZEN = 4; +Const Int PROP_INVULNERABILITY = 5; // (Deprecated) +Const Int PROP_STRENGTH = 6; // (Deprecated) +Const Int PROP_INVISIBILITY = 7; // (Deprecated) +Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) +Const Int PROP_ALLMAP = 9; // (Deprecated) +Const Int PROP_INFRARED = 10; // (Deprecated) +Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) +Const Int PROP_FLIGHT = 12; // (Deprecated) +Const Int PROP_SPEED = 15; // (Deprecated) +Const Int PROP_BUDDHA = 16; + +// Line_SetBlocking +Const Int BLOCKF_CREATURES = 1; +Const Int BLOCKF_MONSTERS = 2; +Const Int BLOCKF_PLAYERS = 4; +Const Int BLOCKF_FLOATERS = 8; +Const Int BLOCKF_PROJECTILES = 16; +Const Int BLOCKF_EVERYTHING = 32; +Const Int BLOCKF_RAILING = 64; +Const Int BLOCKF_USE = 128; + +// Pointer constants, bitfield-enabled + +Const Int AAPTR_DEFAULT = 0; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; + +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; + +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; + +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; + +// Pointer operation flags + +Const Int PTROP_UNSAFETARGET = 1; +Const Int PTROP_UNSAFEMASTER = 2; +Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; + + +// Flags for A_Warp + +Const Int WARPF_ABSOLUTEOFFSET = 0x1; +Const Int WARPF_ABSOLUTEANGLE = 0x2; +Const Int WARPF_USECALLERANGLE = 0x4; +Const Int WARPF_NOCHECKPOSITION = 0x8; +Const Int WARPF_INTERPOLATE = 0x10; +Const Int WARPF_WARPINTERPOLATION = 0x20; +Const Int WARPF_COPYINTERPOLATION = 0x40; +Const Int WARPF_STOP = 0x80; +Const Int WARPF_TOFLOOR = 0x100; +Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; + +// flags for A_SetPitch/SetAngle +const int SPF_FORCECLAMP = 1; +const int SPF_INTERPOLATE = 2; + + +// flags for A_CheckLOF + +enum +{ + CLOFF_NOAIM_VERT = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, + + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, + CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ +}; + +// 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; + +// 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; + +// Flags for A_AlertMonsters +const int AMF_TARGETEMITTER = 1; +const int AMF_TARGETNONPLAYER = 2; +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, +}; + + +// This is only here to provide one global variable for testing. +native int testglobalvar; From 3e6ad8c1a8944800ad928b6f9b534150eadefbd0 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 00:29:15 +1300 Subject: [PATCH 122/313] Further work on prediction lerping --- src/g_game.cpp | 6 +++ src/p_local.h | 1 + src/p_user.cpp | 111 +++++++++++++++++++++----------------- wadsrc/static/menudef.txt | 2 + 4 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 95b0f587d..9020f2da1 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1635,6 +1635,12 @@ void G_DoReborn (int playernum, bool freshbot) } else { + // Old lerp data needs to go + if (playernum == consoleplayer) + { + P_PredictionLerpReset(); + } + // respawn at the start // first disassociate the corpse if (players[playernum].mo) diff --git a/src/p_local.h b/src/p_local.h index 8ba1dd40b..2dc8c773e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -105,6 +105,7 @@ void P_FallingDamage (AActor *ent); void P_PlayerThink (player_t *player); void P_PredictPlayer (player_t *player); void P_UnPredictPlayer (); +void P_PredictionLerpReset(); // // P_MOBJ diff --git a/src/p_user.cpp b/src/p_user.cpp index e7e130e56..7d93f6f24 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -64,20 +64,25 @@ static FRandom pr_skullpop ("SkullPop"); CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -#define LERPSCALE 0.05 +CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + P_PredictionLerpReset(); +} +CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0.1) + self = 0.1; + P_PredictionLerpReset(); +} -struct PredictCheck +struct PredictPos { int gametic; - fixed_t x; - fixed_t y; - fixed_t z; + FVector3 point; fixed_t pitch; fixed_t yaw; - bool onground; -} static PredictionResults[BACKUPTICS], PredictionResult_Last; +} static PredictionLerpFrom, PredictionLerpResult, PredictionLast; static int PredictionLerptics; -static int PredictionMaxLerptics; static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; @@ -1898,7 +1903,6 @@ void P_MovePlayer (player_t *player) else { mo->angle += cmd->ucmd.yaw << 16; - Printf("%d\n", cmd->ucmd.yaw); } player->onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2); @@ -2655,6 +2659,21 @@ void P_PlayerThink (player_t *player) } } +void P_PredictionLerpReset() +{ + PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0; +} + +bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale) +{ + result = to - from; + result *= scale; + result = result + from; + FVector3 delta = result - to; + + return (delta.LengthSquared() > cl_predict_lerpthreshold); +} + void P_PredictPlayer (player_t *player) { int maxtic; @@ -2740,7 +2759,7 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; - bool NoInterpolateOld = R_GetViewInterpolationStatus(); + bool CanLerp = (cl_predict_lerpscale > 0), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) @@ -2750,48 +2769,44 @@ void P_PredictPlayer (player_t *player) P_PlayerThink (player); player->mo->Tick (); - if (PredictionResults[i % BACKUPTICS].gametic && i == PredictionResults[i % BACKUPTICS].gametic && !NoInterpolateOld && PredictionLerptics >= PredictionMaxLerptics) + if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld) { - if (PredictionResults[i % BACKUPTICS].x != player->mo->x || - PredictionResults[i % BACKUPTICS].y != player->mo->y || - (PredictionResults[i % BACKUPTICS].z != player->mo->z && PredictionResults[i % BACKUPTICS].onground && player->onground)) - // If the player was always on the ground, they might be on a lift, and lerping would be disruptive on z height changes alone - { - PredictionLerptics = 0; - PredictionMaxLerptics = (maxtic - gametic); - } + // Z is not compared as lifts will alter this with no apparent change + DoLerp = (PredictionLast.point.X != FIXED2FLOAT(player->mo->x) || + PredictionLast.point.Y != FIXED2FLOAT(player->mo->y)); + } + } + + if (CanLerp) + { + if (DoLerp) + { + // If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap + PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult; + PredictionLerptics = 1; } - PredictionResults[i % BACKUPTICS].gametic = i; - PredictionResults[i % BACKUPTICS].x = player->mo->x; - PredictionResults[i % BACKUPTICS].y = player->mo->y; - PredictionResults[i % BACKUPTICS].z = player->mo->z; - PredictionResults[i % BACKUPTICS].onground = player->onground; + PredictionLast.gametic = maxtic - 1; + PredictionLast.point.X = FIXED2FLOAT(player->mo->x); + PredictionLast.point.Y = FIXED2FLOAT(player->mo->y); + PredictionLast.point.Z = FIXED2FLOAT(player->mo->z); + + if (PredictionLerptics > 0) + { + if (PredictionLerpFrom.gametic > 0 && + P_LerpCalculate(PredictionLerpFrom.point, PredictionLast.point, PredictionLerpResult.point, (float)PredictionLerptics * cl_predict_lerpscale)) + { + PredictionLerptics++; + player->mo->x = FLOAT2FIXED(PredictionLerpResult.point.X); + player->mo->y = FLOAT2FIXED(PredictionLerpResult.point.Y); + player->mo->z = FLOAT2FIXED(PredictionLerpResult.point.Z); + } + else + { + PredictionLerptics = 0; + } + } } - - if (PredictionLerptics < PredictionMaxLerptics) - { - PredictionLerptics++; - FVector3 pointold, pointnew, step, difference, result; - pointold.X = FIXED2FLOAT(PredictionResult_Last.x); // Old player pos - pointold.Y = FIXED2FLOAT(PredictionResult_Last.y); - pointold.Z = FIXED2FLOAT(PredictionResult_Last.z); - pointnew.X = FIXED2FLOAT(player->mo->x); // New player pos - pointnew.Y = FIXED2FLOAT(player->mo->y); - pointnew.Z = FIXED2FLOAT(player->mo->z); - - difference = pointnew - pointold; - step = difference / PredictionMaxLerptics; - result = step * PredictionLerptics; - result += pointold; - - player->mo->x = FLOAT2FIXED(result.X); - player->mo->y = FLOAT2FIXED(result.Y); - player->mo->z = FLOAT2FIXED(result.Z); - Printf("Lerped! x%f y%f z%f\n", result.X, result.Y, result.Z); - } - if (PredictionLerptics >= PredictionMaxLerptics) - PredictionResult_Last = PredictionResults[(maxtic - 1) % BACKUPTICS]; } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 6d313faf1..627c11385 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1612,6 +1612,8 @@ OptionMenu NetworkOptions StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" Option "Predict line actions", "cl_predict_specials", "OnOff" + Option "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.01 + Option "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 98904039b77a1c8afa0eb2d2477ea908b72f766e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 16:32:49 +1300 Subject: [PATCH 123/313] Final work on prediction lerping Added cl_predict_lerpscale and cl_predict_lerpthreshold Added options in menudef Made sure that lerping cannot extrapolate or run on small scales Lerping gets reset when rendering interpolation does or respawn --- src/p_user.cpp | 19 ++++++++++++------- wadsrc/static/menudef.txt | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 7d93f6f24..abf9256f7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -64,14 +64,14 @@ static FRandom pr_skullpop ("SkullPop"); CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { P_PredictionLerpReset(); } -CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (self < 0.1) - self = 0.1; + if (self < 0.1f) + self = 0.1f; P_PredictionLerpReset(); } @@ -2671,7 +2671,8 @@ bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale) result = result + from; FVector3 delta = result - to; - return (delta.LengthSquared() > cl_predict_lerpthreshold); + // As a fail safe, assume extrapolation is the threshold. + return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f); } void P_PredictPlayer (player_t *player) @@ -2759,7 +2760,8 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; - bool CanLerp = (cl_predict_lerpscale > 0), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + // Values too small to be usable for lerping can be considered "off". + bool CanLerp = !(cl_predict_lerpscale < 0.01f), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) @@ -2779,7 +2781,10 @@ void P_PredictPlayer (player_t *player) if (CanLerp) { - if (DoLerp) + if (NoInterpolateOld) + P_PredictionLerpReset(); + + else if (DoLerp) { // If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 627c11385..1fd8dfe2b 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1612,8 +1612,8 @@ OptionMenu NetworkOptions StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" Option "Predict line actions", "cl_predict_specials", "OnOff" - Option "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.01 - Option "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 + Slider "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.05 + Slider "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 3e4446e46b56eb3688491b7cd601b88fe5038237 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 16:49:53 +1300 Subject: [PATCH 124/313] Moved prediction lerp reset to cover all cases. --- src/g_game.cpp | 6 ------ src/p_mobj.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 9020f2da1..95b0f587d 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1635,12 +1635,6 @@ void G_DoReborn (int playernum, bool freshbot) } else { - // Old lerp data needs to go - if (playernum == consoleplayer) - { - P_PredictionLerpReset(); - } - // respawn at the start // first disassociate the corpse if (players[playernum].mo) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d035e0117..1d97311eb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4246,6 +4246,12 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) if ((unsigned)playernum >= (unsigned)MAXPLAYERS || !playeringame[playernum]) return NULL; + // Old lerp data needs to go + if (playernum == consoleplayer) + { + P_PredictionLerpReset(); + } + p = &players[playernum]; if (p->cls == NULL) From 0cd736b48736d6bf8ded7e18acb8eacdc40baf29 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 17:09:29 +1300 Subject: [PATCH 125/313] Prevent lerping when tic duplication not 1 Network dup makes player prediction inaccurate --- src/p_user.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index abf9256f7..b40e44424 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2685,7 +2685,7 @@ void P_PredictPlayer (player_t *player) player->mo == NULL || player != &players[consoleplayer] || player->playerstate != PST_LIVE || - //!netgame || + !netgame || /*player->morphTics ||*/ (player->cheats & CF_PREDICTING)) { @@ -2761,7 +2761,7 @@ void P_PredictPlayer (player_t *player) act->BlockNode = NULL; // Values too small to be usable for lerping can be considered "off". - bool CanLerp = !(cl_predict_lerpscale < 0.01f), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) From 5de6b535aee1bc152ed2aa7461eaacd0b1d3bbfb Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Mon, 13 Oct 2014 08:45:36 +0100 Subject: [PATCH 126/313] - Removed unnecessary 'id' variable from AActor. - Removed unused 'backpack' variable from player_t. - Removed duplicate code from b_move.cpp. - General cleanup. --- src/actor.h | 3 --- src/b_func.cpp | 4 ++-- src/b_move.cpp | 19 +------------------ src/d_netinfo.cpp | 3 --- src/d_player.h | 5 +---- src/g_shared/a_pickups.cpp | 2 +- src/p_mobj.cpp | 13 +++++++------ src/p_user.cpp | 12 +++++++----- src/version.h | 2 +- 9 files changed, 20 insertions(+), 43 deletions(-) diff --git a/src/actor.h b/src/actor.h index bf4b92457..9a3c6844a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -937,9 +937,6 @@ public: TObjPtr Inventory; // [RH] This actor's inventory DWORD InventoryID; // A unique ID to keep track of inventory items - //Added by MC: - SDWORD id; // Player ID (for items, # in list.) - BYTE smokecounter; BYTE FloatBobPhase; BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc) diff --git a/src/b_func.cpp b/src/b_func.cpp index d79e5df11..2703a1933 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -249,7 +249,7 @@ shootmissile: if (m) { - if (inc[actor->id]) + if (inc[actor->player - players]) actor->player->angle += m; else actor->player->angle -= m; @@ -257,7 +257,7 @@ shootmissile: if (abs (actor->player->angle - actor->angle) < 4*ANGLE_1) { - inc[actor->id] = !inc[actor->id]; + inc[actor->player - players] = !inc[actor->player - players]; } if (Check_LOS (actor, enemy, (SHOOTFOV/2))) diff --git a/src/b_move.cpp b/src/b_move.cpp index b456c0c84..0345bccfb 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -17,20 +17,7 @@ #include "gi.h" #include "a_keys.h" #include "d_event.h" - -enum dirtype_t -{ - DI_EAST, - DI_NORTHEAST, - DI_NORTH, - DI_NORTHWEST, - DI_WEST, - DI_SOUTHWEST, - DI_SOUTH, - DI_SOUTHEAST, - DI_NODIR, - NUMDIRS -}; +#include "p_enemy.h" static FRandom pr_botopendoor ("BotOpenDoor"); static FRandom pr_bottrywalk ("BotTryWalk"); @@ -39,10 +26,6 @@ static FRandom pr_botnewchasedir ("BotNewChaseDir"); // borrow some tables from p_enemy.cpp extern dirtype_t opposite[9]; extern dirtype_t diags[4]; -extern fixed_t xspeed[8]; -extern fixed_t yspeed[8]; - -extern TArray spechit; //Called while the bot moves after its player->dest mobj //which can be a weapon/enemy/item whatever. diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index f3f83e25e..6c5fbb6c5 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -60,9 +60,6 @@ static FRandom pr_pickteam ("PickRandomTeam"); -extern bool st_firsttime; -EXTERN_CVAR (Bool, teamplay) - CVAR (Float, autoaim, 5000.f, CVAR_USERINFO | CVAR_ARCHIVE); CVAR (String, name, "Player", CVAR_USERINFO | CVAR_ARCHIVE); CVAR (Color, color, 0x40cf00, CVAR_USERINFO | CVAR_ARCHIVE); diff --git a/src/d_player.h b/src/d_player.h index f50db97b1..070e4dba4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -76,8 +76,6 @@ FPlayerColorSet *P_GetPlayerColorSet(FName classname, int setnum); void P_EnumPlayerColorSets(FName classname, TArray *out); const char *GetPrintableDisplayName(const PClass *cls); -class player_t; - class APlayerPawn : public AActor { DECLARE_CLASS (APlayerPawn, AActor) @@ -398,8 +396,7 @@ public: int inventorytics; BYTE CurrentPlayerClass; // class # for this player instance - bool backpack; - + int frags[MAXPLAYERS]; // kills of other players int fragcount; // [RH] Cumulative frags for this player int lastkilltime; // [RH] For multikills diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index acb6224a3..429cb649b 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1025,7 +1025,7 @@ void AInventory::Touch (AActor *toucher) for (int i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && this == players[i].dest) - players[i].dest = NULL; + players[i].dest = NULL; } } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d035e0117..0cdefcf96 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -252,9 +252,13 @@ void AActor::Serialize (FArchive &arc) << MinMissileChance << SpawnFlags << Inventory - << InventoryID - << id - << FloatBobPhase + << InventoryID; + if (SaveVersion < 4513) + { + SDWORD id; + arc << id; + } + arc << FloatBobPhase << Translation << SeeSound << AttackSound @@ -4358,9 +4362,6 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) mobj->pitch = mobj->roll = 0; mobj->health = p->health; - //Added by MC: Identification (number in the players[MAXPLAYERS] array) - mobj->id = playernum; - // [RH] Set player sprite based on skin if (!(mobj->flags4 & MF4_NOSKIN)) { diff --git a/src/p_user.cpp b/src/p_user.cpp index 7b049a2df..ab9579dcd 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -240,7 +240,6 @@ player_t::player_t() health(0), inventorytics(0), CurrentPlayerClass(0), - backpack(0), fragcount(0), lastkilltime(0), multicount(0), @@ -341,7 +340,6 @@ player_t &player_t::operator=(const player_t &p) health = p.health; inventorytics = p.inventorytics; CurrentPlayerClass = p.CurrentPlayerClass; - backpack = p.backpack; memcpy(frags, &p.frags, sizeof(frags)); fragcount = p.fragcount; lastkilltime = p.lastkilltime; @@ -2889,9 +2887,13 @@ void player_t::Serialize (FArchive &arc) << vely << centering << health - << inventorytics - << backpack - << fragcount + << inventorytics; + if (SaveVersion < 4513) + { + bool backpack; + arc << backpack; + } + arc << fragcount << spreecount << multicount << lastkilltime diff --git a/src/version.h b/src/version.h index 92b1816a1..a7226d8bb 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 4512 +#define SAVEVER 4513 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 83d84eaae9e4e13e92c1ec585855fddd276eb2dc Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Mon, 13 Oct 2014 18:40:25 +0100 Subject: [PATCH 127/313] - Moved bot related variables from player_t into a new FBot class. --- src/b_bot.h | 43 ++++++++++ src/b_func.cpp | 62 ++++++------- src/b_game.cpp | 40 ++++----- src/b_move.cpp | 30 +++---- src/b_think.cpp | 172 ++++++++++++++++++------------------- src/d_main.cpp | 12 +-- src/d_net.cpp | 27 +++--- src/d_player.h | 39 +-------- src/g_game.cpp | 18 ++-- src/g_shared/a_pickups.cpp | 4 +- src/p_acs.cpp | 4 +- src/p_doors.cpp | 2 +- src/p_interaction.cpp | 28 +++--- src/p_map.cpp | 6 +- src/p_mobj.cpp | 12 +-- src/p_pspr.cpp | 8 +- src/p_saveg.cpp | 2 +- src/p_tick.cpp | 2 +- src/p_user.cpp | 146 +++++++++++++------------------ src/wi_stuff.cpp | 10 +-- 20 files changed, 324 insertions(+), 343 deletions(-) diff --git a/src/b_bot.h b/src/b_bot.h index d178a85fa..c82ed03d1 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -145,6 +145,49 @@ protected: bool observer; //Consoleplayer is observer. }; +class FBot +{ +public: + angle_t savedyaw; + int savedpitch; + + angle_t angle; // The wanted angle that the bot try to get every tic. + // (used to get a smoth view movement) + TObjPtr dest; // Move Destination. + TObjPtr prev; // Previous move destination. + + + TObjPtr enemy; // The dead meat. + TObjPtr missile; // A threatening missile that needs to be avoided. + TObjPtr mate; // Friend (used for grouping in teamplay or coop). + TObjPtr last_mate; // If bots mate disappeared (not if died) that mate is + // pointed to by this. Allows bot to roam to it if + // necessary. + + //Skills + struct botskill_t skill; + + //Tickers + int t_active; // Open door, lower lift stuff, door must open and + // lift must go down before bot does anything + // radical like try a stuckmove + int t_respawn; + int t_strafe; + int t_react; + int t_fight; + int t_roam; + int t_rocket; + + //Misc booleans + bool isbot; + bool first_shot; // Used for reaction skill. + bool sleft; // If false, strafe is right. + bool allround; + + fixed_t oldx; + fixed_t oldy; +}; + //Externs extern FCajunMaster bglobal; diff --git a/src/b_func.cpp b/src/b_func.cpp index 2703a1933..8d2df6949 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -141,7 +141,7 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) angle_t an; int m; static bool inc[MAXPLAYERS]; - AActor *enemy = actor->player->enemy; + AActor *enemy = actor->player->Bot.enemy; if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) return; @@ -149,20 +149,20 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) if (actor->player->ReadyWeapon == NULL) return; - if (actor->player->damagecount > actor->player->skill.isp) + if (actor->player->damagecount > actor->player->Bot.skill.isp) { - actor->player->first_shot = true; + actor->player->Bot.first_shot = true; return; } //Reaction skill thing. - if (actor->player->first_shot && + if (actor->player->Bot.first_shot && !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) { - actor->player->t_react = (100-actor->player->skill.reaction+1)/((pr_botdofire()%3)+3); + actor->player->Bot.t_react = (100-actor->player->Bot.skill.reaction+1)/((pr_botdofire()%3)+3); } - actor->player->first_shot = false; - if (actor->player->t_react) + actor->player->Bot.first_shot = false; + if (actor->player->Bot.t_react) return; //MAKEME: Decrease the rocket suicides even more. @@ -199,8 +199,8 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) { //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=actor->player->skill.reaction) - if(Check_LOS(actor, actor->player->enemy, SHOOTFOV)) + if ((pr_botdofire()%200)<=actor->player->Bot.skill.reaction) + if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV)) no_fire = false; } else if (actor->player->ReadyWeapon->ProjectileType != NULL) @@ -211,11 +211,11 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) an = FireRox (actor, enemy, cmd); if(an) { - actor->player->angle = an; + actor->player->Bot.angle = an; //have to be somewhat precise. to avoid suicide. - if (abs (actor->player->angle - actor->angle) < 12*ANGLE_1) + if (abs (actor->player->Bot.angle - actor->angle) < 12*ANGLE_1) { - actor->player->t_rocket = 9; + actor->player->Bot.t_rocket = 9; no_fire = false; } } @@ -225,14 +225,14 @@ shootmissile: dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - actor->player->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); + actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); if (Check_LOS (actor, enemy, SHOOTFOV)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - actor->player->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); + actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); aiming_penalty = 0; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; @@ -240,7 +240,7 @@ shootmissile: aiming_penalty += pr_botdofire()%40;//Dark if (actor->player->damagecount) aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim - aiming_value = actor->player->skill.aiming - aiming_penalty; + aiming_value = actor->player->Bot.skill.aiming - aiming_penalty; if (aiming_value <= 0) aiming_value = 1; m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate @@ -250,12 +250,12 @@ shootmissile: if (m) { if (inc[actor->player - players]) - actor->player->angle += m; + actor->player->Bot.angle += m; else - actor->player->angle -= m; + actor->player->Bot.angle -= m; } - if (abs (actor->player->angle - actor->angle) < 4*ANGLE_1) + if (abs (actor->player->Bot.angle - actor->angle) < 4*ANGLE_1) { inc[actor->player - players] = !inc[actor->player - players]; } @@ -285,20 +285,20 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) bool p_leader[MAXPLAYERS]; //is mate alive? - if (bot->player->mate) + if (bot->player->Bot.mate) { - if (bot->player->mate->health <= 0) - bot->player->mate = NULL; + if (bot->player->Bot.mate->health <= 0) + bot->player->Bot.mate = NULL; else - bot->player->last_mate = bot->player->mate; + bot->player->Bot.last_mate = bot->player->Bot.mate; } - if (bot->player->mate) //Still is.. - return bot->player->mate; + if (bot->player->Bot.mate) //Still is.. + return bot->player->Bot.mate; //Check old_mates status. - if (bot->player->last_mate) - if (bot->player->last_mate->health <= 0) - bot->player->last_mate = NULL; + if (bot->player->Bot.last_mate) + if (bot->player->Bot.last_mate->health <= 0) + bot->player->Bot.last_mate = NULL; for (count = 0; count < MAXPLAYERS; count++) { @@ -307,8 +307,8 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) p_leader[count] = false; for (count2 = 0; count2 < MAXPLAYERS; count2++) { - if (players[count].isbot - && players[count2].mate == players[count].mo) + if (players[count].Bot.isbot + && players[count2].Bot.mate == players[count].mo) { p_leader[count] = true; break; @@ -386,11 +386,11 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) } //Note: It's hard to ambush a bot who is not alone - if (bot->player->allround || bot->player->mate) + if (bot->player->Bot.allround || bot->player->Bot.mate) vangle = ANGLE_MAX; else vangle = ENEMY_SCAN_FOV; - bot->player->allround = false; + bot->player->Bot.allround = false; target = NULL; closest_dist = FIXED_MAX; diff --git a/src/b_game.cpp b/src/b_game.cpp index f3878b8bb..e3f3c97d6 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -118,7 +118,7 @@ void FCajunMaster::Main (int buf) BotThinkCycles.Clock(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].mo && !freeze && players[i].isbot) + if (playeringame[i] && players[i].mo && !freeze && players[i].Bot.isbot) Think (players[i].mo, &netcmds[i][buf]); } BotThinkCycles.Unclock(); @@ -176,10 +176,10 @@ void FCajunMaster::Init () for (i = 0; i < MAXPLAYERS; i++) { waitingforspawn[i] = false; - if (playeringame[i] && players[i].isbot) + if (playeringame[i] && players[i].Bot.isbot) { CleanBotstuff (&players[i]); - players[i].isbot = false; + players[i].Bot.isbot = false; botingame[i] = false; } } @@ -214,7 +214,7 @@ void FCajunMaster::End () getspawned.Clear(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot) + if (playeringame[i] && players[i].Bot.isbot) { if (deathmatch) { @@ -336,7 +336,7 @@ bool FCajunMaster::SpawnBot (const char *name, int color) Net_WriteString (concat); } - players[playernumber].skill = thebot->skill; + players[playernumber].Bot.skill = thebot->skill; thebot->inuse = true; @@ -363,7 +363,7 @@ void FCajunMaster::DoAddBot (int bnum, char *info) else { multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). - players[bnum].isbot = true; + players[bnum].Bot.isbot = true; playeringame[bnum] = true; players[bnum].mo = NULL; players[bnum].playerstate = PST_ENTER; @@ -425,22 +425,22 @@ void FCajunMaster::RemoveAllBots (bool fromlist) //Used when bots are respawned or at level starts. void FCajunMaster::CleanBotstuff (player_t *p) { - p->angle = ANG45; - p->dest = NULL; - p->enemy = NULL; //The dead meat. - p->missile = NULL; //A threatening missile that needs to be avoided. - p->mate = NULL; //Friend (used for grouping in templay or coop. - p->last_mate = NULL; //If bot's mate dissapeared (not if died) that mate is pointed to by this. Allows bot to roam to it if necessary. + p->Bot.angle = ANG45; + p->Bot.dest = NULL; + p->Bot.enemy = NULL; //The dead meat. + p->Bot.missile = NULL; //A threatening missile that needs to be avoided. + p->Bot.mate = NULL; //Friend (used for grouping in templay or coop. + p->Bot.last_mate = NULL; //If bot's mate dissapeared (not if died) that mate is pointed to by this. Allows bot to roam to it if necessary. //Tickers - p->t_active = 0; //Open door, lower lift stuff, door must open and lift must go down before bot does anything radical like try a stuckmove - p->t_respawn = 0; - p->t_strafe = 0; - p->t_react = 0; + p->Bot.t_active = 0; //Open door, lower lift stuff, door must open and lift must go down before bot does anything radical like try a stuckmove + p->Bot.t_respawn = 0; + p->Bot.t_strafe = 0; + p->Bot.t_react = 0; //Misc bools - p->isbot = true; //Important. - p->first_shot = true; //Used for reaction skill. - p->sleft = false; //If false, strafe is right. - p->allround = false; + p->Bot.isbot = true; //Important. + p->Bot.first_shot = true; //Used for reaction skill. + p->Bot.sleft = false; //If false, strafe is right. + p->Bot.allround = false; } diff --git a/src/b_move.cpp b/src/b_move.cpp index 0345bccfb..1f2e4c332 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -33,19 +33,19 @@ void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd) { int delta; - if (Reachable(actor, actor->player->dest)) + if (Reachable(actor, actor->player->Bot.dest)) { // Straight towards it. - actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->dest->x, actor->player->dest->y); + actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot.dest->x, actor->player->Bot.dest->y); } else if (actor->movedir < 8) // turn towards movement direction if not there yet { - actor->player->angle &= (angle_t)(7<<29); - delta = actor->player->angle - (actor->movedir << 29); + actor->player->Bot.angle &= (angle_t)(7<<29); + delta = actor->player->Bot.angle - (actor->movedir << 29); if (delta > 0) - actor->player->angle -= ANG45; + actor->player->Bot.angle -= ANG45; else if (delta < 0) - actor->player->angle += ANG45; + actor->player->Bot.angle += ANG45; } // chase towards destination. @@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) dirtype_t turnaround; - if (!actor->player->dest) + if (!actor->player->Bot.dest) { #ifndef BOT_RELEASE_COMPILE Printf ("Bot tried move without destination\n"); @@ -145,8 +145,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) olddir = (dirtype_t)actor->movedir; turnaround = opposite[olddir]; - deltax = actor->player->dest->x - actor->x; - deltay = actor->player->dest->y - actor->y; + deltax = actor->player->Bot.dest->x - actor->x; + deltay = actor->player->Bot.dest->y - actor->y; if (deltax > 10*FRACUNIT) d[1] = DI_EAST; @@ -315,23 +315,23 @@ void FCajunMaster::TurnToAng (AActor *actor) { if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { - if (actor->player->t_roam && !actor->player->missile) + if (actor->player->Bot.t_roam && !actor->player->Bot.missile) { //Keep angle that where when shot where decided. return; } } - if(actor->player->enemy) - if(!actor->player->dest) //happens when running after item in combat situations, or normal, prevents weak turns + if(actor->player->Bot.enemy) + if(!actor->player->Bot.dest) //happens when running after item in combat situations, or normal, prevents weak turns if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) - if(Check_LOS(actor, actor->player->enemy, SHOOTFOV+5*ANGLE_1)) + if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV+5*ANGLE_1)) maxturn = 3; } - int distance = actor->player->angle - actor->angle; + int distance = actor->player->Bot.angle - actor->angle; - if (abs (distance) < OKAYRANGE && !actor->player->enemy) + if (abs (distance) < OKAYRANGE && !actor->player->Bot.enemy) return; distance /= TURNSENS; diff --git a/src/b_think.cpp b/src/b_think.cpp index cc7f087e6..a5deec372 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -28,13 +28,13 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) { memset (cmd, 0, sizeof(*cmd)); - if (actor->player->enemy && actor->player->enemy->health <= 0) - actor->player->enemy = NULL; + if (actor->player->Bot.enemy && actor->player->Bot.enemy->health <= 0) + actor->player->Bot.enemy = NULL; if (actor->health > 0) //Still alive { if (teamplay || !deathmatch) - actor->player->mate = Choose_Mate (actor); + actor->player->Bot.mate = Choose_Mate (actor); angle_t oldyaw = actor->angle; int oldpitch = actor->pitch; @@ -52,17 +52,17 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; } - if (actor->player->t_active) actor->player->t_active--; - if (actor->player->t_strafe) actor->player->t_strafe--; - if (actor->player->t_react) actor->player->t_react--; - if (actor->player->t_fight) actor->player->t_fight--; - if (actor->player->t_rocket) actor->player->t_rocket--; - if (actor->player->t_roam) actor->player->t_roam--; + if (actor->player->Bot.t_active) actor->player->Bot.t_active--; + if (actor->player->Bot.t_strafe) actor->player->Bot.t_strafe--; + if (actor->player->Bot.t_react) actor->player->Bot.t_react--; + if (actor->player->Bot.t_fight) actor->player->Bot.t_fight--; + if (actor->player->Bot.t_rocket) actor->player->Bot.t_rocket--; + if (actor->player->Bot.t_roam) actor->player->Bot.t_roam--; //Respawn ticker - if (actor->player->t_respawn) + if (actor->player->Bot.t_respawn) { - actor->player->t_respawn--; + actor->player->Bot.t_respawn--; } else if (actor->health <= 0) { // Time to respawn @@ -80,17 +80,17 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) int r; b = actor->player; - if (!b->isbot) + if (!b->Bot.isbot) return; stuck = false; - dist = b->dest ? P_AproxDistance(actor->x-b->dest->x, actor->y-b->dest->y) : 0; + dist = b->Bot.dest ? P_AproxDistance(actor->x-b->Bot.dest->x, actor->y-b->Bot.dest->y) : 0; - if (b->missile && - ((!b->missile->velx || !b->missile->vely) || !Check_LOS(actor, b->missile, SHOOTFOV*3/2))) + if (b->Bot.missile && + ((!b->Bot.missile->velx || !b->Bot.missile->vely) || !Check_LOS(actor, b->Bot.missile, SHOOTFOV*3/2))) { - b->sleft = !b->sleft; - b->missile = NULL; //Probably ended its travel. + b->Bot.sleft = !b->Bot.sleft; + b->Bot.missile = NULL; //Probably ended its travel. } if (actor->pitch > 0) @@ -99,35 +99,35 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) actor->pitch += 80; //HOW TO MOVE: - if (b->missile && (P_AproxDistance(actor->x-b->missile->x, actor->y-b->missile->y)Bot.missile && (P_AproxDistance(actor->x-b->Bot.missile->x, actor->y-b->Bot.missile->y)missile); - actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->missile->x, b->missile->y); - cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN; + Pitch (actor, b->Bot.missile); + actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.missile->x, b->Bot.missile->y); + cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000) - && b->t_strafe<=0) + if ((P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000) + && b->Bot.t_strafe<=0) { - b->t_strafe = 5; - b->sleft = !b->sleft; + b->Bot.t_strafe = 5; + b->Bot.sleft = !b->Bot.sleft; } //If able to see enemy while avoiding missile, still fire at enemy. - if (b->enemy && Check_LOS (actor, b->enemy, SHOOTFOV)) + if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV)) Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->enemy && P_CheckSight (actor, b->enemy, 0)) //Fight! + else if (b->Bot.enemy && P_CheckSight (actor, b->Bot.enemy, 0)) //Fight! { - Pitch (actor, b->enemy); + Pitch (actor, b->Bot.enemy); //Check if it's more important to get an item than fight. - if (b->dest && (b->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. + if (b->Bot.dest && (b->Bot.dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. { -#define is(x) b->dest->IsKindOf (PClass::FindClass (#x)) +#define is(x) b->Bot.dest->IsKindOf (PClass::FindClass (#x)) if ( ( - (actor->health < b->skill.isp && + (actor->health < b->Bot.skill.isp && (is (Medikit) || is (Stimpack) || is (Soulsphere) || @@ -143,75 +143,75 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) ) && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - && Reachable (actor, b->dest)) + && Reachable (actor, b->Bot.dest)) #undef is { goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. } } - b->dest = NULL; //To let bot turn right + b->Bot.dest = NULL; //To let bot turn right if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - if (!(b->enemy->flags3 & MF3_ISMONSTER)) - b->t_fight = AFTERTICS; + if (!(b->Bot.enemy->flags3 & MF3_ISMONSTER)) + b->Bot.t_fight = AFTERTICS; - if (b->t_strafe <= 0 && - (P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000 + if (b->Bot.t_strafe <= 0 && + (P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000 || ((pr_botmove()%30)==10)) ) { stuck = true; - b->t_strafe = 5; - b->sleft = !b->sleft; + b->Bot.t_strafe = 5; + b->Bot.sleft = !b->Bot.sleft; } - b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y); + b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); if (b->ReadyWeapon == NULL || - P_AproxDistance(actor->x-b->enemy->x, actor->y-b->enemy->y) > + P_AproxDistance(actor->x-b->Bot.enemy->x, actor->y-b->Bot.enemy->y) > b->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; + cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; } else if (!stuck) //Too close, so move away. { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; + cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; } //Strafing. - if (b->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. + if (b->Bot.enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. { - cmd->ucmd.sidemove = b->sleft ? -SIDEWALK : SIDEWALK; + cmd->ucmd.sidemove = b->Bot.sleft ? -SIDEWALK : SIDEWALK; } else { - cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN; + cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; } Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->mate && !b->enemy && (!b->dest || b->dest==b->mate)) //Follow mate move. + else if (b->Bot.mate && !b->Bot.enemy && (!b->Bot.dest || b->Bot.dest==b->Bot.mate)) //Follow mate move. { fixed_t matedist; - Pitch (actor, b->mate); + Pitch (actor, b->Bot.mate); - if (!Reachable (actor, b->mate)) + if (!Reachable (actor, b->Bot.mate)) { - if (b->mate == b->dest && pr_botmove.Random() < 32) + if (b->Bot.mate == b->Bot.dest && pr_botmove.Random() < 32) { // [RH] If the mate is the dest, pick a new dest sometimes - b->dest = NULL; + b->Bot.dest = NULL; } goto roam; } - actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->mate->x, b->mate->y); + actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.mate->x, b->Bot.mate->y); - matedist = P_AproxDistance(actor->x - b->mate->x, actor->y - b->mate->y); + matedist = P_AproxDistance(actor->x - b->Bot.mate->x, actor->y - b->Bot.mate->y); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -221,33 +221,33 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) } else //Roam after something. { - b->first_shot = true; + b->Bot.first_shot = true; ///// roam: ///// - if (b->enemy && Check_LOS (actor, b->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. + if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. Dofire (actor, cmd); //Order bot to fire current weapon - if (b->dest && !(b->dest->flags&MF_SPECIAL) && b->dest->health < 0) + if (b->Bot.dest && !(b->Bot.dest->flags&MF_SPECIAL) && b->Bot.dest->health < 0) { //Roaming after something dead. - b->dest = NULL; + b->Bot.dest = NULL; } - if (b->dest == NULL) + if (b->Bot.dest == NULL) { - if (b->t_fight && b->enemy) //Enemy/bot has jumped around corner. So what to do? + if (b->Bot.t_fight && b->Bot.enemy) //Enemy/bot has jumped around corner. So what to do? { - if (b->enemy->player) + if (b->Bot.enemy->player) { - if (((b->enemy->player->ReadyWeapon != NULL && b->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>b->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - b->dest = b->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. + if (((b->Bot.enemy->player->ReadyWeapon != NULL && b->Bot.enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || + (pr_botmove()%100)>b->Bot.skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + b->Bot.dest = b->Bot.enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. else //hide while b->t_fight, but keep view at enemy. - b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y); + b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); } //Just a monster, so kill it. else - b->dest = b->enemy; + b->Bot.dest = b->Bot.enemy; //VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. } @@ -272,42 +272,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) item = it.Next(); } firstthing = item; - b->dest = item; + b->Bot.dest = item; } } - else if (b->mate && (r < 179 || P_CheckSight(actor, b->mate))) + else if (b->Bot.mate && (r < 179 || P_CheckSight(actor, b->Bot.mate))) { - b->dest = b->mate; + b->Bot.dest = b->Bot.mate; } else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) { - b->dest = players[(r&(MAXPLAYERS-1))].mo; + b->Bot.dest = players[(r&(MAXPLAYERS-1))].mo; } } - if (b->dest) + if (b->Bot.dest) { - b->t_roam = MAXROAM; + b->Bot.t_roam = MAXROAM; } } - if (b->dest) + if (b->Bot.dest) { //Bot has a target so roam after it. Roam (actor, cmd); } } //End of movement main part. - if (!b->t_roam && b->dest) + if (!b->Bot.t_roam && b->Bot.dest) { - b->prev = b->dest; - b->dest = NULL; + b->Bot.prev = b->Bot.dest; + b->Bot.dest = NULL; } - if (b->t_fight<(AFTERTICS/2)) + if (b->Bot.t_fight<(AFTERTICS/2)) actor->flags |= MF_DROPOFF; - b->oldx = actor->x; - b->oldy = actor->y; + b->Bot.oldx = actor->x; + b->Bot.oldy = actor->y; } //BOT_WhatToGet @@ -324,7 +324,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) #define typeis(x) item->IsKindOf (PClass::FindClass (#x)) if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == b->prev) + || item == b->Bot.prev) { return; } @@ -366,21 +366,21 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) return; - if ((b->dest == NULL || - !(b->dest->flags & MF_SPECIAL)/* || + if ((b->Bot.dest == NULL || + !(b->Bot.dest->flags & MF_SPECIAL)/* || !Reachable (actor, b->dest)*/)/* && Reachable (actor, item)*/) // Calling Reachable slows this down tremendously { - b->prev = b->dest; - b->dest = item; - b->t_roam = MAXROAM; + b->Bot.prev = b->Bot.dest; + b->Bot.dest = item; + b->Bot.t_roam = MAXROAM; } } void FCajunMaster::Set_enemy (AActor *actor) { AActor *oldenemy; - AActor **enemy = &actor->player->enemy; + AActor **enemy = &actor->player->Bot.enemy; if (*enemy && (*enemy)->health > 0 @@ -397,7 +397,7 @@ void FCajunMaster::Set_enemy (AActor *actor) // and we already have an existing enemy. if (deathmatch || !*enemy) { - actor->player->allround = !!*enemy; + actor->player->Bot.allround = !!*enemy; *enemy = NULL; *enemy = Find_enemy(actor); if (!*enemy) diff --git a/src/d_main.cpp b/src/d_main.cpp index a24586bc4..9ce300e01 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -978,19 +978,19 @@ void D_DoomLoop () int i; for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot.isbot && players[i].mo) { - players[i].savedyaw = players[i].mo->angle; - players[i].savedpitch = players[i].mo->pitch; + players[i].Bot.savedyaw = players[i].mo->angle; + players[i].Bot.savedpitch = players[i].mo->pitch; } } bglobal.Main (maketic%BACKUPTICS); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot.isbot && players[i].mo) { - players[i].mo->angle = players[i].savedyaw; - players[i].mo->pitch = players[i].savedpitch; + players[i].mo->angle = players[i].Bot.savedyaw; + players[i].mo->pitch = players[i].Bot.savedpitch; } } if (advancedemo) diff --git a/src/d_net.cpp b/src/d_net.cpp index bc4bc7352..dfc76f785 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole) // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { - if (i != netconsole && playeringame[i] && !players[i].isbot) + if (i != netconsole && playeringame[i] && !players[i].Bot.isbot) { Net_Arbitrator = i; players[i].settings_controller = true; @@ -902,7 +902,7 @@ void GetPackets (void) for (i = 0; i < numplayers; ++i) { - int node = !players[playerbytes[i]].isbot ? + int node = !players[playerbytes[i]].Bot.isbot ? nodeforplayer[playerbytes[i]] : netnode; SkipTicCmd (&start, nettics[node] - realstart); @@ -918,7 +918,7 @@ void GetPackets (void) // packet. for (i = 0; i < numplayers; ++i) { - if (!players[playerbytes[i]].isbot) + if (!players[playerbytes[i]].Bot.isbot) { nettics[nodeforplayer[playerbytes[i]]] = realend; } @@ -935,10 +935,10 @@ void AdjustBots (int gameticdiv) // be in even when gametic lags behind maketic. for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot.isbot && players[i].mo) { - players[i].savedyaw = players[i].mo->angle; - players[i].savedpitch = players[i].mo->pitch; + players[i].Bot.savedyaw = players[i].mo->angle; + players[i].Bot.savedpitch = players[i].mo->pitch; for (int j = gameticdiv; j < maketic/ticdup; j++) { players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup; @@ -952,10 +952,10 @@ void UnadjustBots () { for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot.isbot && players[i].mo) { - players[i].mo->angle = players[i].savedyaw; - players[i].mo->pitch = players[i].savedpitch; + players[i].mo->angle = players[i].Bot.savedyaw; + players[i].mo->pitch = players[i].Bot.savedpitch; } } } @@ -1127,7 +1127,7 @@ void NetUpdate (void) { if (playeringame[j]) { - if (players[j].isbot || NetMode == NET_PacketServer) + if (players[j].Bot.isbot || NetMode == NET_PacketServer) { count++; } @@ -1269,7 +1269,7 @@ void NetUpdate (void) { if (playeringame[j] && j != playerfornode[i] && j != consoleplayer) { - if (players[j].isbot || NetMode == NET_PacketServer) + if (players[j].Bot.isbot || NetMode == NET_PacketServer) { playerbytes[l++] = j; netbuffer[k++] = j; @@ -1308,9 +1308,8 @@ void NetUpdate (void) } else if (i != 0) { - if (players[playerbytes[l]].isbot) + if (players[playerbytes[l]].Bot.isbot) { - WriteWord (0, &cmddata); // fake consistancy word } else @@ -2875,7 +2874,7 @@ static void Network_Controller (int playernum, bool add) return; } - if (players[playernum].isbot) + if (players[playernum].Bot.isbot) { Printf ("Bots cannot be added to the controller list.\n"); return; diff --git a/src/d_player.h b/src/d_player.h index 5affd6913..055cb75b4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -442,47 +442,10 @@ public: FName LastDamageType; // [RH] For damage-specific pain and death sounds //Added by MC: - angle_t savedyaw; - int savedpitch; - - angle_t angle; // The wanted angle that the bot try to get every tic. - // (used to get a smoth view movement) - TObjPtr dest; // Move Destination. - TObjPtr prev; // Previous move destination. - - - TObjPtr enemy; // The dead meat. - TObjPtr missile; // A threatening missile that needs to be avoided. - TObjPtr mate; // Friend (used for grouping in teamplay or coop). - TObjPtr last_mate; // If bots mate disappeared (not if died) that mate is - // pointed to by this. Allows bot to roam to it if - // necessary. + FBot Bot; bool settings_controller; // Player can control game settings. - //Skills - struct botskill_t skill; - - //Tickers - int t_active; // Open door, lower lift stuff, door must open and - // lift must go down before bot does anything - // radical like try a stuckmove - int t_respawn; - int t_strafe; - int t_react; - int t_fight; - int t_roam; - int t_rocket; - - //Misc booleans - bool isbot; - bool first_shot; // Used for reaction skill. - bool sleft; // If false, strafe is right. - bool allround; - - fixed_t oldx; - fixed_t oldy; - float BlendR; // [RH] Final blending values float BlendG; float BlendB; diff --git a/src/g_game.cpp b/src/g_game.cpp index 95b0f587d..3d166492e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -875,7 +875,7 @@ static void ChangeSpy (int changespy) pnum &= MAXPLAYERS-1; if (playeringame[pnum] && (!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) || - (bot_allowspy && players[pnum].isbot))) + (bot_allowspy && players[pnum].Bot.isbot))) { break; } @@ -1156,7 +1156,7 @@ void G_Ticker () Printf ("%s is turbo!\n", players[i].userinfo.GetName()); } - if (netgame && !players[i].isbot && !demoplayback && (gametic%ticdup) == 0) + if (netgame && !players[i].Bot.isbot && !demoplayback && (gametic%ticdup) == 0) { //players[i].inconsistant = 0; if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy) @@ -1351,7 +1351,7 @@ void G_PlayerReborn (int player) itemcount = p->itemcount; secretcount = p->secretcount; currclass = p->CurrentPlayerClass; - b_skill = p->skill; //Added by MC: + b_skill = p->Bot.skill; //Added by MC: userinfo.TransferFrom(p->userinfo); actor = p->mo; cls = p->cls; @@ -1374,7 +1374,7 @@ void G_PlayerReborn (int player) p->LogText = log; p->cheats |= chasecam; - p->skill = b_skill; //Added by MC: + p->Bot.skill = b_skill; //Added by MC: p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; @@ -1386,11 +1386,11 @@ void G_PlayerReborn (int player) p->ReadyWeapon = p->PendingWeapon; } - //Added by MC: Init bot structure. - if (bglobal.botingame[player]) - bglobal.CleanBotstuff (p); - else - p->isbot = false; + //Added by MC: Init bot structure. + if (bglobal.botingame[player]) + bglobal.CleanBotstuff (p); + else + p->Bot.isbot = false; } // diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 429cb649b..4836ae033 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1024,8 +1024,8 @@ void AInventory::Touch (AActor *toucher) //Added by MC: Check if item taken was the roam destination of any bot for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && this == players[i].dest) - players[i].dest = NULL; + if (playeringame[i] && this == players[i].Bot.dest) + players[i].Bot.dest = NULL; } } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index bc4cbdb75..d2780b4e4 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4228,7 +4228,7 @@ int DLevelScript::DoClassifyActor(int tid) { classify |= ACTOR_VOODOODOLL; } - if (actor->player->isbot) + if (actor->player->Bot.isbot) { classify |= ACTOR_BOT; } @@ -8632,7 +8632,7 @@ scriptwait: } else { - STACK(1) = players[STACK(1)].isbot; + STACK(1) = players[STACK(1)].Bot.isbot; } break; diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 7c6952d7f..de4519505 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -460,7 +460,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, // run into them (otherwise opening them would be // a real pain). { - if (!thing->player || thing->player->isbot) + if (!thing->player || thing->player->Bot.isbot) return false; // JDC: bad guys never close doors //Added by MC: Neither do bots. diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index c3be908ea..1dcf0ebad 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -100,10 +100,10 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher) return; //Added by MC: Finished with this destination. - if (toucher->player != NULL && toucher->player->isbot && special == toucher->player->dest) + if (toucher->player != NULL && toucher->player->Bot.isbot && special == toucher->player->Bot.dest) { - toucher->player->prev = toucher->player->dest; - toucher->player->dest = NULL; + toucher->player->Bot.prev = toucher->player->Bot.dest; + toucher->player->Bot.dest = NULL; } special->Touch (toucher); @@ -593,7 +593,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) // even those caused by other monsters players[0].killcount++; } - + if (player) { // [RH] Death messages @@ -608,17 +608,17 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) //Added by MC: Respawn bots if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) { - if (player->isbot) - player->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; + if (player->Bot.isbot) + player->Bot.t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; //Added by MC: Discard enemies. for (int i = 0; i < MAXPLAYERS; i++) { - if (players[i].isbot && this == players[i].enemy) + if (players[i].Bot.isbot && this == players[i].Bot.enemy) { - if (players[i].dest == players[i].enemy) - players[i].dest = NULL; - players[i].enemy = NULL; + if (players[i].Bot.dest == players[i].Bot.enemy) + players[i].Bot.dest = NULL; + players[i].Bot.enemy = NULL; } } @@ -1193,11 +1193,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // if (player) { - - //Added by MC: Lets bots look allround for enemies if they survive an ambush. - if (player->isbot) + //Added by MC: Lets bots look allround for enemies if they survive an ambush. + if (player->Bot.isbot) { - player->allround = true; + player->Bot.allround = true; } // end of game hell hack @@ -1711,7 +1710,6 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, P_SetMobjState(target, target->info->painstate); } */ - return; } diff --git a/src/p_map.cpp b/src/p_map.cpp index f0087e220..987d83293 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1919,13 +1919,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, } //Added by MC: To prevent bot from getting into dangerous sectors. - if (thing->player && thing->player->isbot && thing->flags & MF_SHOOTABLE) + if (thing->player && thing->player->Bot.isbot && thing->flags & MF_SHOOTABLE) { if (tm.sector != thing->Sector && bglobal.IsDangerous(tm.sector)) { - thing->player->prev = thing->player->dest; - thing->player->dest = NULL; + thing->player->Bot.prev = thing->player->Bot.dest; + thing->player->Bot.dest = NULL; thing->velx = 0; thing->vely = 0; thing->z = oldz; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3e06561a9..e023a7181 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3116,7 +3116,7 @@ void AActor::Tick () special2++; } //Added by MC: Freeze mode. - if (bglobal.freeze && !(player && !player->isbot)) + if (bglobal.freeze && !(player && !player->Bot.isbot)) { return; } @@ -3237,18 +3237,18 @@ void AActor::Tick () bglobal.m_Thinking = true; for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || !players[i].isbot) + if (!playeringame[i] || !players[i].Bot.isbot) continue; if (flags3 & MF3_ISMONSTER) { if (health > 0 - && !players[i].enemy + && !players[i].Bot.enemy && player ? !IsTeammate (players[i].mo) : true && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. - players[i].enemy = this; + players[i].Bot.enemy = this; } } else if (flags & MF_SPECIAL) @@ -3260,10 +3260,10 @@ void AActor::Tick () } else if (flags & MF_MISSILE) { - if (!players[i].missile && (flags3 & MF3_WARNBOT)) + if (!players[i].Bot.missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) - players[i].missile = this; + players[i].Bot.missile = this; } } } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index e477d9751..e050bff16 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -227,7 +227,7 @@ void P_FireWeapon (player_t *player, FState *state) // [SO] 9/2/02: People were able to do an awful lot of damage // when they were observers... - if (!player->isbot && bot_observer) + if (!player->Bot.isbot && bot_observer) { return; } @@ -263,7 +263,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) // [SO] 9/2/02: People were able to do an awful lot of damage // when they were observers... - if (!player->isbot && bot_observer) + if (!player->Bot.isbot && bot_observer) { return; } @@ -298,7 +298,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) void P_ReloadWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->isbot && bot_observer) + if (!player->Bot.isbot && bot_observer) { return; } @@ -329,7 +329,7 @@ void P_ReloadWeapon (player_t *player, FState *state) void P_ZoomWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->isbot && bot_observer) + if (!player->Bot.isbot && bot_observer) { return; } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index bf0da0cbe..dd72a9cc8 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -271,7 +271,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) dst->cheats |= chasecam; - if (dst->isbot) + if (dst->Bot.isbot) { botinfo_t *thebot = bglobal.botinfo; while (thebot && stricmp (name, thebot->name)) diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 07e153ffa..1a6e1e498 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -120,7 +120,7 @@ void P_Ticker (void) for (i = 0; iTick (); // [RH] moved this here diff --git a/src/p_user.cpp b/src/p_user.cpp index 6595f7309..ee6957002 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -293,28 +293,6 @@ player_t::player_t() respawn_time(0), camera(0), air_finished(0), - savedyaw(0), - savedpitch(0), - angle(0), - dest(0), - prev(0), - enemy(0), - missile(0), - mate(0), - last_mate(0), - t_active(0), - t_respawn(0), - t_strafe(0), - t_react(0), - t_fight(0), - t_roam(0), - t_rocket(0), - isbot(0), - first_shot(0), - sleft(0), - allround(0), - oldx(0), - oldy(0), BlendR(0), BlendG(0), BlendB(0), @@ -333,7 +311,7 @@ player_t::player_t() memset (&cmd, 0, sizeof(cmd)); memset (frags, 0, sizeof(frags)); memset (psprites, 0, sizeof(psprites)); - memset (&skill, 0, sizeof(skill)); + memset (&Bot, 0, sizeof(Bot)); } player_t &player_t::operator=(const player_t &p) @@ -401,30 +379,30 @@ player_t &player_t::operator=(const player_t &p) camera = p.camera; air_finished = p.air_finished; LastDamageType = p.LastDamageType; - savedyaw = p.savedyaw; - savedpitch = p.savedpitch; - angle = p.angle; - dest = p.dest; - prev = p.prev; - enemy = p.enemy; - missile = p.missile; - mate = p.mate; - last_mate = p.last_mate; + Bot.savedyaw = p.Bot.savedyaw; + Bot.savedpitch = p.Bot.savedpitch; + Bot.angle = p.Bot.angle; + Bot.dest = p.Bot.dest; + Bot.prev = p.Bot.prev; + Bot.enemy = p.Bot.enemy; + Bot.missile = p.Bot.missile; + Bot.mate = p.Bot.mate; + Bot.last_mate = p.Bot.last_mate; + Bot.skill = p.Bot.skill; + Bot.t_active = p.Bot.t_active; + Bot.t_respawn = p.Bot.t_respawn; + Bot.t_strafe = p.Bot.t_strafe; + Bot.t_react = p.Bot.t_react; + Bot.t_fight = p.Bot.t_fight; + Bot.t_roam = p.Bot.t_roam; + Bot.t_rocket = p.Bot.t_rocket; + Bot.isbot = p.Bot.isbot; + Bot.first_shot = p.Bot.first_shot; + Bot.sleft = p.Bot.sleft; + Bot.allround = p.Bot.allround; + Bot.oldx = p.Bot.oldx; + Bot.oldy = p.Bot.oldy; settings_controller = p.settings_controller; - skill = p.skill; - t_active = p.t_active; - t_respawn = p.t_respawn; - t_strafe = p.t_strafe; - t_react = p.t_react; - t_fight = p.t_fight; - t_roam = p.t_roam; - t_rocket = p.t_rocket; - isbot = p.isbot; - first_shot = p.first_shot; - sleft = p.sleft; - allround = p.allround; - oldx = p.oldx; - oldy = p.oldy; BlendR = p.BlendR; BlendG = p.BlendG; BlendB = p.BlendB; @@ -466,12 +444,12 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep) if (*&poisoner == old) poisoner = replacement, changed++; if (*&attacker == old) attacker = replacement, changed++; if (*&camera == old) camera = replacement, changed++; - if (*&dest == old) dest = replacement, changed++; - if (*&prev == old) prev = replacement, changed++; - if (*&enemy == old) enemy = replacement, changed++; - if (*&missile == old) missile = replacement, changed++; - if (*&mate == old) mate = replacement, changed++; - if (*&last_mate == old) last_mate = replacement, changed++; + if (*&Bot.dest == old) Bot.dest = replacement, changed++; + if (*&Bot.prev == old) Bot.prev = replacement, changed++; + if (*&Bot.enemy == old) Bot.enemy = replacement, changed++; + if (*&Bot.missile == old) Bot.missile = replacement, changed++; + if (*&Bot.mate == old) Bot.mate = replacement, changed++; + if (*&Bot.last_mate == old) Bot.last_mate = replacement, changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; if (*&PremorphWeapon == old) PremorphWeapon = static_cast(rep), changed++; @@ -486,12 +464,12 @@ size_t player_t::PropagateMark() GC::Mark(poisoner); GC::Mark(attacker); GC::Mark(camera); - GC::Mark(dest); - GC::Mark(prev); - GC::Mark(enemy); - GC::Mark(missile); - GC::Mark(mate); - GC::Mark(last_mate); + GC::Mark(Bot.dest); + GC::Mark(Bot.prev); + GC::Mark(Bot.enemy); + GC::Mark(Bot.missile); + GC::Mark(Bot.mate); + GC::Mark(Bot.last_mate); GC::Mark(ReadyWeapon); GC::Mark(ConversationNPC); GC::Mark(ConversationPC); @@ -740,10 +718,10 @@ void APlayerPawn::SetupWeaponSlots() // If we're the local player, then there's a bit more work to do. // This also applies if we're a bot and this is the net arbitrator. if (player - players == consoleplayer || - (player->isbot && consoleplayer == Net_Arbitrator)) + (player->Bot.isbot && consoleplayer == Net_Arbitrator)) { FWeaponSlots local_slots(player->weapons); - if (player->isbot) + if (player->Bot.isbot) { // Bots only need weapons from KEYCONF, not INI modifications. P_PlaybackKeyConfWeapons(&local_slots); } @@ -2175,7 +2153,7 @@ void P_DeathThink (player_t *player) if ((player->cmd.ucmd.buttons & BT_USE || ((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN)) { - if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->isbot)) + if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->Bot.isbot)) { player->cls = NULL; // Force a new class if the player is using a random class player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; @@ -3008,7 +2986,7 @@ void player_t::Serialize (FArchive &arc) << air_finished << turnticks << oldbuttons - << isbot + << Bot.isbot << BlendR << BlendG << BlendB @@ -3092,32 +3070,32 @@ void player_t::Serialize (FArchive &arc) onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2); } - if (isbot) + if (Bot.isbot) { - arc << angle - << dest - << prev - << enemy - << missile - << mate - << last_mate - << skill - << t_active - << t_respawn - << t_strafe - << t_react - << t_fight - << t_roam - << t_rocket - << first_shot - << sleft - << allround - << oldx - << oldy; + arc << Bot.angle + << Bot.dest + << Bot.prev + << Bot.enemy + << Bot.missile + << Bot.mate + << Bot.last_mate + << Bot.skill + << Bot.t_active + << Bot.t_respawn + << Bot.t_strafe + << Bot.t_react + << Bot.t_fight + << Bot.t_roam + << Bot.t_rocket + << Bot.first_shot + << Bot.sleft + << Bot.allround + << Bot.oldx + << Bot.oldy; } else { - dest = prev = enemy = missile = mate = last_mate = NULL; + Bot.dest = Bot.prev = Bot.enemy = Bot.missile = Bot.mate = Bot.last_mate = NULL; } if (arc.IsLoading ()) { diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 2ab93f090..9e773993b 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1330,7 +1330,7 @@ void WI_updateDeathmatchStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].isbot && !playerready[i]) + if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) break; } @@ -1429,7 +1429,7 @@ void WI_drawDeathmatchStats () clamp(int(g*255.f), 0, 255), clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); - if (playerready[pnum] || player->isbot) // Bots are automatically assumed ready, to prevent confusion + if (playerready[pnum] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); @@ -1638,7 +1638,7 @@ void WI_updateNetgameStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].isbot && !playerready[i]) + if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) break; } @@ -1735,7 +1735,7 @@ void WI_drawNetgameStats () clamp(int(g*255.f), 0, 255), clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); - if (playerready[i] || player->isbot) // Bots are automatically assumed ready, to prevent confusion + if (playerready[i] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); @@ -2010,7 +2010,7 @@ void WI_checkForAccelerate(void) { if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && ((players[i].cmd.ucmd.buttons & players[i].oldbuttons) - == players[i].oldbuttons) && !player->isbot) + == players[i].oldbuttons) && !player->Bot.isbot) { acceleratestage = 1; playerready[i] = true; From 29cf6b0daa63012611cbfed7cee1999cbb1506f0 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 14 Oct 2014 12:54:03 +1300 Subject: [PATCH 128/313] Fixed PredictionActor size and typo --- src/d_net.cpp | 2 +- src/p_user.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index bc4bc7352..c06d628f9 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1730,7 +1730,7 @@ void D_CheckNetGame (void) if (v != NULL && (atoi(v) != 0)) { Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is prone to running too slow on some internet configurations." - "\nIf the game is running well below excpected speeds, use netmode 0 (P2P) instead.\n"); + "\nIf the game is running well below expected speeds, use netmode 0 (P2P) instead.\n"); } // I_InitNetwork sets doomcom and netgame diff --git a/src/p_user.cpp b/src/p_user.cpp index b40e44424..fccdbae8e 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -85,7 +85,7 @@ struct PredictPos static int PredictionLerptics; static player_t PredictionPlayerBackup; -static BYTE PredictionActorBackup[sizeof(AActor)]; +static BYTE PredictionActorBackup[sizeof(APlayerPawn)]; static TArray PredictionTouchingSectorsBackup; static TArray PredictionSectorListBackup; static TArray PredictionSector_sprev_Backup; From db323643f81cc3649782b5f5995187d2522a69bf Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Tue, 14 Oct 2014 19:57:11 +0100 Subject: [PATCH 129/313] - Renamed FBot to DBot and made it inherit from DThinker. - Fixed: Bots added by players other than the net arbitrator did not have their skill set. --- src/b_bot.cpp | 71 ++++++++++++++- src/b_bot.h | 16 ++-- src/b_func.cpp | 68 +++++++-------- src/b_game.cpp | 58 ++++--------- src/b_move.cpp | 30 +++---- src/b_think.cpp | 172 ++++++++++++++++++------------------- src/d_main.cpp | 12 +-- src/d_net.cpp | 36 ++++---- src/d_player.h | 2 +- src/d_protocol.h | 2 +- src/g_game.cpp | 21 +++-- src/g_level.cpp | 10 +++ src/g_shared/a_pickups.cpp | 4 +- src/p_acs.cpp | 4 +- src/p_doors.cpp | 2 +- src/p_interaction.cpp | 22 ++--- src/p_map.cpp | 6 +- src/p_mobj.cpp | 12 +-- src/p_pspr.cpp | 8 +- src/p_saveg.cpp | 3 +- src/p_tick.cpp | 2 +- src/p_user.cpp | 108 +++++++++-------------- src/version.h | 2 +- src/wi_stuff.cpp | 10 +-- 24 files changed, 355 insertions(+), 326 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 26c8f99bd..30a8e4877 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -1,7 +1,7 @@ -// Cajun bot console commands. +// Cajun bot // -// [RH] Moved out of d_netcmd.c (in Cajun source), because they don't really -// belong there. +// [RH] Moved console commands out of d_netcmd.c (in Cajun source), because +// they don't really belong there. #include "c_cvars.h" #include "c_dispatch.h" @@ -14,6 +14,71 @@ #include "d_net.h" #include "farchive.h" +IMPLEMENT_POINTY_CLASS(DBot) + DECLARE_POINTER(dest) + DECLARE_POINTER(prev) + DECLARE_POINTER(enemy) + DECLARE_POINTER(missile) + DECLARE_POINTER(mate) + DECLARE_POINTER(last_mate) +END_POINTERS + +DBot::DBot () +{ + savedyaw = 0; + savedpitch = 0; + angle = 0; + dest = NULL; + prev = NULL; + enemy = NULL; + missile = NULL; + mate = NULL; + last_mate = NULL; + memset(&skill, 0, sizeof(skill)); + t_active = 0; + t_respawn = 0; + t_strafe = 0; + t_react = 0; + t_fight = 0; + t_roam = 0; + t_rocket = 0; + first_shot = true; + sleft = false; + allround = false; + increase = false; + oldx = 0; + oldy = 0; +} + +void DBot::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + + arc << savedyaw + << savedpitch + << angle + << dest + << prev + << enemy + << missile + << mate + << last_mate + << skill + << t_active + << t_respawn + << t_strafe + << t_react + << t_fight + << t_roam + << t_rocket + << first_shot + << sleft + << allround + << increase + << oldx + << oldy; +} + CVAR (Int, bot_next_color, 11, 0) CVAR (Bool, bot_observer, false, 0) diff --git a/src/b_bot.h b/src/b_bot.h index c82ed03d1..f414a9687 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -85,11 +85,10 @@ public: void Main (int buf); void Init (); void End(); - void CleanBotstuff (player_t *p); bool SpawnBot (const char *name, int color = NOCOLOR); bool LoadBots (); void ForgetBots (); - void DoAddBot (int bnum, char *info); + void DoAddBot (BYTE **stream); void RemoveAllBots (bool fromlist); //(B_Func.c) @@ -109,7 +108,6 @@ public: bool IsDangerous (sector_t *sec); TArray getspawned; //Array of bots (their names) which should be spawned when starting a game. - bool botingame[MAXPLAYERS]; BYTE freeze:1; //Game in freeze mode. BYTE changefreeze:1; //Game wants to change freeze mode. int botnum; @@ -145,14 +143,20 @@ protected: bool observer; //Consoleplayer is observer. }; -class FBot +class DBot : public DThinker { + DECLARE_CLASS(DBot,DThinker) + HAS_OBJECT_POINTERS public: + DBot (); + + void Serialize (FArchive &arc); + angle_t savedyaw; int savedpitch; angle_t angle; // The wanted angle that the bot try to get every tic. - // (used to get a smoth view movement) + // (used to get a smooth view movement) TObjPtr dest; // Move Destination. TObjPtr prev; // Previous move destination. @@ -179,10 +183,10 @@ public: int t_rocket; //Misc booleans - bool isbot; bool first_shot; // Used for reaction skill. bool sleft; // If false, strafe is right. bool allround; + bool increase; fixed_t oldx; fixed_t oldy; diff --git a/src/b_func.cpp b/src/b_func.cpp index 8d2df6949..0c0ab1ebd 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -140,8 +140,7 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) fixed_t dist; angle_t an; int m; - static bool inc[MAXPLAYERS]; - AActor *enemy = actor->player->Bot.enemy; + AActor *enemy = actor->player->Bot->enemy; if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) return; @@ -149,20 +148,20 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) if (actor->player->ReadyWeapon == NULL) return; - if (actor->player->damagecount > actor->player->Bot.skill.isp) + if (actor->player->damagecount > actor->player->Bot->skill.isp) { - actor->player->Bot.first_shot = true; + actor->player->Bot->first_shot = true; return; } //Reaction skill thing. - if (actor->player->Bot.first_shot && + if (actor->player->Bot->first_shot && !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) { - actor->player->Bot.t_react = (100-actor->player->Bot.skill.reaction+1)/((pr_botdofire()%3)+3); + actor->player->Bot->t_react = (100-actor->player->Bot->skill.reaction+1)/((pr_botdofire()%3)+3); } - actor->player->Bot.first_shot = false; - if (actor->player->Bot.t_react) + actor->player->Bot->first_shot = false; + if (actor->player->Bot->t_react) return; //MAKEME: Decrease the rocket suicides even more. @@ -199,8 +198,8 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) { //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=actor->player->Bot.skill.reaction) - if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV)) + if ((pr_botdofire()%200)<=actor->player->Bot->skill.reaction) + if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV)) no_fire = false; } else if (actor->player->ReadyWeapon->ProjectileType != NULL) @@ -211,11 +210,11 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) an = FireRox (actor, enemy, cmd); if(an) { - actor->player->Bot.angle = an; + actor->player->Bot->angle = an; //have to be somewhat precise. to avoid suicide. - if (abs (actor->player->Bot.angle - actor->angle) < 12*ANGLE_1) + if (abs (actor->player->Bot->angle - actor->angle) < 12*ANGLE_1) { - actor->player->Bot.t_rocket = 9; + actor->player->Bot->t_rocket = 9; no_fire = false; } } @@ -225,14 +224,14 @@ shootmissile: dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); + actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); if (Check_LOS (actor, enemy, SHOOTFOV)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); + actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); aiming_penalty = 0; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; @@ -240,7 +239,7 @@ shootmissile: aiming_penalty += pr_botdofire()%40;//Dark if (actor->player->damagecount) aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim - aiming_value = actor->player->Bot.skill.aiming - aiming_penalty; + aiming_value = actor->player->Bot->skill.aiming - aiming_penalty; if (aiming_value <= 0) aiming_value = 1; m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate @@ -249,15 +248,15 @@ shootmissile: if (m) { - if (inc[actor->player - players]) - actor->player->Bot.angle += m; + if (actor->player->Bot->increase) + actor->player->Bot->angle += m; else - actor->player->Bot.angle -= m; + actor->player->Bot->angle -= m; } - if (abs (actor->player->Bot.angle - actor->angle) < 4*ANGLE_1) + if (abs (actor->player->Bot->angle - actor->angle) < 4*ANGLE_1) { - inc[actor->player - players] = !inc[actor->player - players]; + actor->player->Bot->increase = !actor->player->Bot->increase; } if (Check_LOS (actor, enemy, (SHOOTFOV/2))) @@ -285,20 +284,20 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) bool p_leader[MAXPLAYERS]; //is mate alive? - if (bot->player->Bot.mate) + if (bot->player->Bot->mate) { - if (bot->player->Bot.mate->health <= 0) - bot->player->Bot.mate = NULL; + if (bot->player->Bot->mate->health <= 0) + bot->player->Bot->mate = NULL; else - bot->player->Bot.last_mate = bot->player->Bot.mate; + bot->player->Bot->last_mate = bot->player->Bot->mate; } - if (bot->player->Bot.mate) //Still is.. - return bot->player->Bot.mate; + if (bot->player->Bot->mate) //Still is.. + return bot->player->Bot->mate; //Check old_mates status. - if (bot->player->Bot.last_mate) - if (bot->player->Bot.last_mate->health <= 0) - bot->player->Bot.last_mate = NULL; + if (bot->player->Bot->last_mate) + if (bot->player->Bot->last_mate->health <= 0) + bot->player->Bot->last_mate = NULL; for (count = 0; count < MAXPLAYERS; count++) { @@ -307,8 +306,8 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) p_leader[count] = false; for (count2 = 0; count2 < MAXPLAYERS; count2++) { - if (players[count].Bot.isbot - && players[count2].Bot.mate == players[count].mo) + if (players[count2].Bot != NULL + && players[count2].Bot->mate == players[count].mo) { p_leader[count] = true; break; @@ -337,7 +336,6 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) && ((bot->health/2) <= client->mo->health || !deathmatch) && !p_leader[count]) //taken? { - if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) { test = P_AproxDistance (client->mo->x - bot->x, @@ -386,11 +384,11 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) } //Note: It's hard to ambush a bot who is not alone - if (bot->player->Bot.allround || bot->player->Bot.mate) + if (bot->player->Bot->allround || bot->player->Bot->mate) vangle = ANGLE_MAX; else vangle = ENEMY_SCAN_FOV; - bot->player->Bot.allround = false; + bot->player->Bot->allround = false; target = NULL; closest_dist = FIXED_MAX; diff --git a/src/b_game.cpp b/src/b_game.cpp index e3f3c97d6..35de6ec86 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -118,7 +118,7 @@ void FCajunMaster::Main (int buf) BotThinkCycles.Clock(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].mo && !freeze && players[i].Bot.isbot) + if (playeringame[i] && players[i].mo && !freeze && players[i].Bot != NULL) Think (players[i].mo, &netcmds[i][buf]); } BotThinkCycles.Unclock(); @@ -172,16 +172,9 @@ void FCajunMaster::Init () body1 = NULL; body2 = NULL; - //Remove all bots upon each level start, they'll get spawned instead. for (i = 0; i < MAXPLAYERS; i++) { waitingforspawn[i] = false; - if (playeringame[i] && players[i].Bot.isbot) - { - CleanBotstuff (&players[i]); - players[i].Bot.isbot = false; - botingame[i] = false; - } } if (ctf && teamplay == false) @@ -214,13 +207,12 @@ void FCajunMaster::End () getspawned.Clear(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot.isbot) + if (playeringame[i] && players[i].Bot != NULL) { if (deathmatch) { getspawned.Push(players[i].userinfo.GetName()); } - CleanBotstuff (&players[i]); } } if (deathmatch) @@ -335,8 +327,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color) } Net_WriteString (concat); } - - players[playernumber].Bot.skill = thebot->skill; + Net_WriteByte(thebot->skill.aiming); + Net_WriteByte(thebot->skill.perfection); + Net_WriteByte(thebot->skill.reaction); + Net_WriteByte(thebot->skill.isp); thebot->inuse = true; @@ -346,9 +340,17 @@ bool FCajunMaster::SpawnBot (const char *name, int color) return true; } -void FCajunMaster::DoAddBot (int bnum, char *info) +void FCajunMaster::DoAddBot (BYTE **stream) { + int bnum = ReadByte (stream); + char *info = ReadString (stream); BYTE *infob = (BYTE *)info; + botskill_t skill; + skill.aiming = ReadByte (stream); + skill.perfection = ReadByte (stream); + skill.reaction = ReadByte (stream); + skill.isp = ReadByte (stream); + D_ReadUserInfoStrings (bnum, &infob, false); if (!deathmatch && playerstarts[bnum].type == 0) { @@ -363,11 +365,11 @@ void FCajunMaster::DoAddBot (int bnum, char *info) else { multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). - players[bnum].Bot.isbot = true; + players[bnum].Bot = new DBot; + players[bnum].Bot->skill = skill; playeringame[bnum] = true; players[bnum].mo = NULL; players[bnum].playerstate = PST_ENTER; - botingame[bnum] = true; if (teamplay) Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName()); @@ -389,13 +391,13 @@ void FCajunMaster::RemoveAllBots (bool fromlist) for (i = 0; i < MAXPLAYERS; ++i) { - if (playeringame[i] && botingame[i]) + if (playeringame[i] && players[i].Bot != NULL) { // If a player is looking through this bot's eyes, make him // look through his own eyes instead. for (j = 0; j < MAXPLAYERS; ++j) { - if (i != j && playeringame[j] && !botingame[j]) + if (i != j && playeringame[j] && players[j].Bot == NULL) { if (players[j].camera == players[i].mo) { @@ -421,28 +423,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist) botnum = 0; } -//Clean the bot part of the player_t -//Used when bots are respawned or at level starts. -void FCajunMaster::CleanBotstuff (player_t *p) -{ - p->Bot.angle = ANG45; - p->Bot.dest = NULL; - p->Bot.enemy = NULL; //The dead meat. - p->Bot.missile = NULL; //A threatening missile that needs to be avoided. - p->Bot.mate = NULL; //Friend (used for grouping in templay or coop. - p->Bot.last_mate = NULL; //If bot's mate dissapeared (not if died) that mate is pointed to by this. Allows bot to roam to it if necessary. - //Tickers - p->Bot.t_active = 0; //Open door, lower lift stuff, door must open and lift must go down before bot does anything radical like try a stuckmove - p->Bot.t_respawn = 0; - p->Bot.t_strafe = 0; - p->Bot.t_react = 0; - //Misc bools - p->Bot.isbot = true; //Important. - p->Bot.first_shot = true; //Used for reaction skill. - p->Bot.sleft = false; //If false, strafe is right. - p->Bot.allround = false; -} - //------------------ //Reads data for bot from diff --git a/src/b_move.cpp b/src/b_move.cpp index 1f2e4c332..f020cf4fc 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -33,19 +33,19 @@ void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd) { int delta; - if (Reachable(actor, actor->player->Bot.dest)) + if (Reachable(actor, actor->player->Bot->dest)) { // Straight towards it. - actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot.dest->x, actor->player->Bot.dest->y); + actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot->dest->x, actor->player->Bot->dest->y); } else if (actor->movedir < 8) // turn towards movement direction if not there yet { - actor->player->Bot.angle &= (angle_t)(7<<29); - delta = actor->player->Bot.angle - (actor->movedir << 29); + actor->player->Bot->angle &= (angle_t)(7<<29); + delta = actor->player->Bot->angle - (actor->movedir << 29); if (delta > 0) - actor->player->Bot.angle -= ANG45; + actor->player->Bot->angle -= ANG45; else if (delta < 0) - actor->player->Bot.angle += ANG45; + actor->player->Bot->angle += ANG45; } // chase towards destination. @@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) dirtype_t turnaround; - if (!actor->player->Bot.dest) + if (!actor->player->Bot->dest) { #ifndef BOT_RELEASE_COMPILE Printf ("Bot tried move without destination\n"); @@ -145,8 +145,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) olddir = (dirtype_t)actor->movedir; turnaround = opposite[olddir]; - deltax = actor->player->Bot.dest->x - actor->x; - deltay = actor->player->Bot.dest->y - actor->y; + deltax = actor->player->Bot->dest->x - actor->x; + deltay = actor->player->Bot->dest->y - actor->y; if (deltax > 10*FRACUNIT) d[1] = DI_EAST; @@ -315,23 +315,23 @@ void FCajunMaster::TurnToAng (AActor *actor) { if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { - if (actor->player->Bot.t_roam && !actor->player->Bot.missile) + if (actor->player->Bot->t_roam && !actor->player->Bot->missile) { //Keep angle that where when shot where decided. return; } } - if(actor->player->Bot.enemy) - if(!actor->player->Bot.dest) //happens when running after item in combat situations, or normal, prevents weak turns + if(actor->player->Bot->enemy) + if(!actor->player->Bot->dest) //happens when running after item in combat situations, or normal, prevents weak turns if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) - if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV+5*ANGLE_1)) + if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV+5*ANGLE_1)) maxturn = 3; } - int distance = actor->player->Bot.angle - actor->angle; + int distance = actor->player->Bot->angle - actor->angle; - if (abs (distance) < OKAYRANGE && !actor->player->Bot.enemy) + if (abs (distance) < OKAYRANGE && !actor->player->Bot->enemy) return; distance /= TURNSENS; diff --git a/src/b_think.cpp b/src/b_think.cpp index a5deec372..e5ee8775c 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -28,13 +28,13 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) { memset (cmd, 0, sizeof(*cmd)); - if (actor->player->Bot.enemy && actor->player->Bot.enemy->health <= 0) - actor->player->Bot.enemy = NULL; + if (actor->player->Bot->enemy && actor->player->Bot->enemy->health <= 0) + actor->player->Bot->enemy = NULL; if (actor->health > 0) //Still alive { if (teamplay || !deathmatch) - actor->player->Bot.mate = Choose_Mate (actor); + actor->player->Bot->mate = Choose_Mate (actor); angle_t oldyaw = actor->angle; int oldpitch = actor->pitch; @@ -52,17 +52,17 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; } - if (actor->player->Bot.t_active) actor->player->Bot.t_active--; - if (actor->player->Bot.t_strafe) actor->player->Bot.t_strafe--; - if (actor->player->Bot.t_react) actor->player->Bot.t_react--; - if (actor->player->Bot.t_fight) actor->player->Bot.t_fight--; - if (actor->player->Bot.t_rocket) actor->player->Bot.t_rocket--; - if (actor->player->Bot.t_roam) actor->player->Bot.t_roam--; + if (actor->player->Bot->t_active) actor->player->Bot->t_active--; + if (actor->player->Bot->t_strafe) actor->player->Bot->t_strafe--; + if (actor->player->Bot->t_react) actor->player->Bot->t_react--; + if (actor->player->Bot->t_fight) actor->player->Bot->t_fight--; + if (actor->player->Bot->t_rocket) actor->player->Bot->t_rocket--; + if (actor->player->Bot->t_roam) actor->player->Bot->t_roam--; //Respawn ticker - if (actor->player->Bot.t_respawn) + if (actor->player->Bot->t_respawn) { - actor->player->Bot.t_respawn--; + actor->player->Bot->t_respawn--; } else if (actor->health <= 0) { // Time to respawn @@ -80,17 +80,17 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) int r; b = actor->player; - if (!b->Bot.isbot) + if (b->Bot == NULL) return; stuck = false; - dist = b->Bot.dest ? P_AproxDistance(actor->x-b->Bot.dest->x, actor->y-b->Bot.dest->y) : 0; + dist = b->Bot->dest ? P_AproxDistance(actor->x-b->Bot->dest->x, actor->y-b->Bot->dest->y) : 0; - if (b->Bot.missile && - ((!b->Bot.missile->velx || !b->Bot.missile->vely) || !Check_LOS(actor, b->Bot.missile, SHOOTFOV*3/2))) + if (b->Bot->missile && + ((!b->Bot->missile->velx || !b->Bot->missile->vely) || !Check_LOS(actor, b->Bot->missile, SHOOTFOV*3/2))) { - b->Bot.sleft = !b->Bot.sleft; - b->Bot.missile = NULL; //Probably ended its travel. + b->Bot->sleft = !b->Bot->sleft; + b->Bot->missile = NULL; //Probably ended its travel. } if (actor->pitch > 0) @@ -99,35 +99,35 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) actor->pitch += 80; //HOW TO MOVE: - if (b->Bot.missile && (P_AproxDistance(actor->x-b->Bot.missile->x, actor->y-b->Bot.missile->y)Bot->missile && (P_AproxDistance(actor->x-b->Bot->missile->x, actor->y-b->Bot->missile->y)Bot.missile); - actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.missile->x, b->Bot.missile->y); - cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; + Pitch (actor, b->Bot->missile); + actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->missile->x, b->Bot->missile->y); + cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000) - && b->Bot.t_strafe<=0) + if ((P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000) + && b->Bot->t_strafe<=0) { - b->Bot.t_strafe = 5; - b->Bot.sleft = !b->Bot.sleft; + b->Bot->t_strafe = 5; + b->Bot->sleft = !b->Bot->sleft; } //If able to see enemy while avoiding missile, still fire at enemy. - if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV)) + if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV)) Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->Bot.enemy && P_CheckSight (actor, b->Bot.enemy, 0)) //Fight! + else if (b->Bot->enemy && P_CheckSight (actor, b->Bot->enemy, 0)) //Fight! { - Pitch (actor, b->Bot.enemy); + Pitch (actor, b->Bot->enemy); //Check if it's more important to get an item than fight. - if (b->Bot.dest && (b->Bot.dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. + if (b->Bot->dest && (b->Bot->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. { -#define is(x) b->Bot.dest->IsKindOf (PClass::FindClass (#x)) +#define is(x) b->Bot->dest->IsKindOf (PClass::FindClass (#x)) if ( ( - (actor->health < b->Bot.skill.isp && + (actor->health < b->Bot->skill.isp && (is (Medikit) || is (Stimpack) || is (Soulsphere) || @@ -143,75 +143,75 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) ) && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - && Reachable (actor, b->Bot.dest)) + && Reachable (actor, b->Bot->dest)) #undef is { goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. } } - b->Bot.dest = NULL; //To let bot turn right + b->Bot->dest = NULL; //To let bot turn right if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - if (!(b->Bot.enemy->flags3 & MF3_ISMONSTER)) - b->Bot.t_fight = AFTERTICS; + if (!(b->Bot->enemy->flags3 & MF3_ISMONSTER)) + b->Bot->t_fight = AFTERTICS; - if (b->Bot.t_strafe <= 0 && - (P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000 + if (b->Bot->t_strafe <= 0 && + (P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000 || ((pr_botmove()%30)==10)) ) { stuck = true; - b->Bot.t_strafe = 5; - b->Bot.sleft = !b->Bot.sleft; + b->Bot->t_strafe = 5; + b->Bot->sleft = !b->Bot->sleft; } - b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); + b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); if (b->ReadyWeapon == NULL || - P_AproxDistance(actor->x-b->Bot.enemy->x, actor->y-b->Bot.enemy->y) > + P_AproxDistance(actor->x-b->Bot->enemy->x, actor->y-b->Bot->enemy->y) > b->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; + cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; } else if (!stuck) //Too close, so move away. { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; + cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; } //Strafing. - if (b->Bot.enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. + if (b->Bot->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. { - cmd->ucmd.sidemove = b->Bot.sleft ? -SIDEWALK : SIDEWALK; + cmd->ucmd.sidemove = b->Bot->sleft ? -SIDEWALK : SIDEWALK; } else { - cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; + cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; } Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->Bot.mate && !b->Bot.enemy && (!b->Bot.dest || b->Bot.dest==b->Bot.mate)) //Follow mate move. + else if (b->Bot->mate && !b->Bot->enemy && (!b->Bot->dest || b->Bot->dest==b->Bot->mate)) //Follow mate move. { fixed_t matedist; - Pitch (actor, b->Bot.mate); + Pitch (actor, b->Bot->mate); - if (!Reachable (actor, b->Bot.mate)) + if (!Reachable (actor, b->Bot->mate)) { - if (b->Bot.mate == b->Bot.dest && pr_botmove.Random() < 32) + if (b->Bot->mate == b->Bot->dest && pr_botmove.Random() < 32) { // [RH] If the mate is the dest, pick a new dest sometimes - b->Bot.dest = NULL; + b->Bot->dest = NULL; } goto roam; } - actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.mate->x, b->Bot.mate->y); + actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->mate->x, b->Bot->mate->y); - matedist = P_AproxDistance(actor->x - b->Bot.mate->x, actor->y - b->Bot.mate->y); + matedist = P_AproxDistance(actor->x - b->Bot->mate->x, actor->y - b->Bot->mate->y); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -221,33 +221,33 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) } else //Roam after something. { - b->Bot.first_shot = true; + b->Bot->first_shot = true; ///// roam: ///// - if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. + if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. Dofire (actor, cmd); //Order bot to fire current weapon - if (b->Bot.dest && !(b->Bot.dest->flags&MF_SPECIAL) && b->Bot.dest->health < 0) + if (b->Bot->dest && !(b->Bot->dest->flags&MF_SPECIAL) && b->Bot->dest->health < 0) { //Roaming after something dead. - b->Bot.dest = NULL; + b->Bot->dest = NULL; } - if (b->Bot.dest == NULL) + if (b->Bot->dest == NULL) { - if (b->Bot.t_fight && b->Bot.enemy) //Enemy/bot has jumped around corner. So what to do? + if (b->Bot->t_fight && b->Bot->enemy) //Enemy/bot has jumped around corner. So what to do? { - if (b->Bot.enemy->player) + if (b->Bot->enemy->player) { - if (((b->Bot.enemy->player->ReadyWeapon != NULL && b->Bot.enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>b->Bot.skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - b->Bot.dest = b->Bot.enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. + if (((b->Bot->enemy->player->ReadyWeapon != NULL && b->Bot->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || + (pr_botmove()%100)>b->Bot->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + b->Bot->dest = b->Bot->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. else //hide while b->t_fight, but keep view at enemy. - b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); + b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); } //Just a monster, so kill it. else - b->Bot.dest = b->Bot.enemy; + b->Bot->dest = b->Bot->enemy; //VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. } @@ -272,42 +272,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) item = it.Next(); } firstthing = item; - b->Bot.dest = item; + b->Bot->dest = item; } } - else if (b->Bot.mate && (r < 179 || P_CheckSight(actor, b->Bot.mate))) + else if (b->Bot->mate && (r < 179 || P_CheckSight(actor, b->Bot->mate))) { - b->Bot.dest = b->Bot.mate; + b->Bot->dest = b->Bot->mate; } else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) { - b->Bot.dest = players[(r&(MAXPLAYERS-1))].mo; + b->Bot->dest = players[(r&(MAXPLAYERS-1))].mo; } } - if (b->Bot.dest) + if (b->Bot->dest) { - b->Bot.t_roam = MAXROAM; + b->Bot->t_roam = MAXROAM; } } - if (b->Bot.dest) + if (b->Bot->dest) { //Bot has a target so roam after it. Roam (actor, cmd); } } //End of movement main part. - if (!b->Bot.t_roam && b->Bot.dest) + if (!b->Bot->t_roam && b->Bot->dest) { - b->Bot.prev = b->Bot.dest; - b->Bot.dest = NULL; + b->Bot->prev = b->Bot->dest; + b->Bot->dest = NULL; } - if (b->Bot.t_fight<(AFTERTICS/2)) + if (b->Bot->t_fight<(AFTERTICS/2)) actor->flags |= MF_DROPOFF; - b->Bot.oldx = actor->x; - b->Bot.oldy = actor->y; + b->Bot->oldx = actor->x; + b->Bot->oldy = actor->y; } //BOT_WhatToGet @@ -324,7 +324,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) #define typeis(x) item->IsKindOf (PClass::FindClass (#x)) if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == b->Bot.prev) + || item == b->Bot->prev) { return; } @@ -366,21 +366,21 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) return; - if ((b->Bot.dest == NULL || - !(b->Bot.dest->flags & MF_SPECIAL)/* || + if ((b->Bot->dest == NULL || + !(b->Bot->dest->flags & MF_SPECIAL)/* || !Reachable (actor, b->dest)*/)/* && Reachable (actor, item)*/) // Calling Reachable slows this down tremendously { - b->Bot.prev = b->Bot.dest; - b->Bot.dest = item; - b->Bot.t_roam = MAXROAM; + b->Bot->prev = b->Bot->dest; + b->Bot->dest = item; + b->Bot->t_roam = MAXROAM; } } void FCajunMaster::Set_enemy (AActor *actor) { AActor *oldenemy; - AActor **enemy = &actor->player->Bot.enemy; + AActor **enemy = &actor->player->Bot->enemy; if (*enemy && (*enemy)->health > 0 @@ -397,7 +397,7 @@ void FCajunMaster::Set_enemy (AActor *actor) // and we already have an existing enemy. if (deathmatch || !*enemy) { - actor->player->Bot.allround = !!*enemy; + actor->player->Bot->allround = !!*enemy; *enemy = NULL; *enemy = Find_enemy(actor); if (!*enemy) diff --git a/src/d_main.cpp b/src/d_main.cpp index 9ce300e01..030387118 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -978,19 +978,19 @@ void D_DoomLoop () int i; for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot.isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].Bot.savedyaw = players[i].mo->angle; - players[i].Bot.savedpitch = players[i].mo->pitch; + players[i].Bot->savedyaw = players[i].mo->angle; + players[i].Bot->savedpitch = players[i].mo->pitch; } } bglobal.Main (maketic%BACKUPTICS); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot.isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].mo->angle = players[i].Bot.savedyaw; - players[i].mo->pitch = players[i].Bot.savedpitch; + players[i].mo->angle = players[i].Bot->savedyaw; + players[i].mo->pitch = players[i].Bot->savedpitch; } } if (advancedemo) diff --git a/src/d_net.cpp b/src/d_net.cpp index dfc76f785..690a443ef 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole) // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { - if (i != netconsole && playeringame[i] && !players[i].Bot.isbot) + if (i != netconsole && playeringame[i] && players[i].Bot == NULL) { Net_Arbitrator = i; players[i].settings_controller = true; @@ -902,7 +902,7 @@ void GetPackets (void) for (i = 0; i < numplayers; ++i) { - int node = !players[playerbytes[i]].Bot.isbot ? + int node = (players[playerbytes[i]].Bot == NULL) ? nodeforplayer[playerbytes[i]] : netnode; SkipTicCmd (&start, nettics[node] - realstart); @@ -918,7 +918,7 @@ void GetPackets (void) // packet. for (i = 0; i < numplayers; ++i) { - if (!players[playerbytes[i]].Bot.isbot) + if (players[playerbytes[i]].Bot == NULL) { nettics[nodeforplayer[playerbytes[i]]] = realend; } @@ -935,10 +935,10 @@ void AdjustBots (int gameticdiv) // be in even when gametic lags behind maketic. for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot.isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].Bot.savedyaw = players[i].mo->angle; - players[i].Bot.savedpitch = players[i].mo->pitch; + players[i].Bot->savedyaw = players[i].mo->angle; + players[i].Bot->savedpitch = players[i].mo->pitch; for (int j = gameticdiv; j < maketic/ticdup; j++) { players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup; @@ -952,10 +952,10 @@ void UnadjustBots () { for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot.isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].mo->angle = players[i].Bot.savedyaw; - players[i].mo->pitch = players[i].Bot.savedpitch; + players[i].mo->angle = players[i].Bot->savedyaw; + players[i].mo->pitch = players[i].Bot->savedpitch; } } } @@ -1127,7 +1127,7 @@ void NetUpdate (void) { if (playeringame[j]) { - if (players[j].Bot.isbot || NetMode == NET_PacketServer) + if (players[j].Bot != NULL || NetMode == NET_PacketServer) { count++; } @@ -1269,7 +1269,7 @@ void NetUpdate (void) { if (playeringame[j] && j != playerfornode[i] && j != consoleplayer) { - if (players[j].Bot.isbot || NetMode == NET_PacketServer) + if (players[j].Bot != NULL || NetMode == NET_PacketServer) { playerbytes[l++] = j; netbuffer[k++] = j; @@ -1308,7 +1308,7 @@ void NetUpdate (void) } else if (i != 0) { - if (players[playerbytes[l]].Bot.isbot) + if (players[playerbytes[l]].Bot != NULL) { WriteWord (0, &cmddata); // fake consistancy word } @@ -2250,10 +2250,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) break; case DEM_ADDBOT: - { - BYTE num = ReadByte (stream); - bglobal.DoAddBot (num, s = ReadString (stream)); - } + bglobal.DoAddBot (stream); break; case DEM_KILLBOTS: @@ -2709,10 +2706,13 @@ void Net_SkipCommand (int type, BYTE **stream) switch (type) { case DEM_SAY: - case DEM_ADDBOT: skip = strlen ((char *)(*stream + 1)) + 2; break; + case DEM_ADDBOT: + skip = strlen ((char *)(*stream + 1)) + 6; + break; + case DEM_GIVECHEAT: case DEM_TAKECHEAT: skip = strlen ((char *)(*stream)) + 3; @@ -2874,7 +2874,7 @@ static void Network_Controller (int playernum, bool add) return; } - if (players[playernum].Bot.isbot) + if (players[playernum].Bot != NULL) { Printf ("Bots cannot be added to the controller list.\n"); return; diff --git a/src/d_player.h b/src/d_player.h index 055cb75b4..ceb1ff894 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -442,7 +442,7 @@ public: FName LastDamageType; // [RH] For damage-specific pain and death sounds //Added by MC: - FBot Bot; + TObjPtr Bot; bool settings_controller; // Player can control game settings. diff --git a/src/d_protocol.h b/src/d_protocol.h index 028ad1606..0d11c7760 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -112,7 +112,7 @@ enum EDemoCommand DEM_DROPPLAYER, // 13 Not implemented, takes a byte DEM_CHANGEMAP, // 14 Name of map to change to DEM_SUICIDE, // 15 Player wants to die - DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot + DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp) DEM_KILLBOTS, // 17 Remove all bots from the world DEM_INVUSEALL, // 18 Use every item (panic!) DEM_INVUSE, // 19 4 bytes: ID of item to use diff --git a/src/g_game.cpp b/src/g_game.cpp index 3d166492e..fad7d9eb1 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -875,7 +875,7 @@ static void ChangeSpy (int changespy) pnum &= MAXPLAYERS-1; if (playeringame[pnum] && (!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) || - (bot_allowspy && players[pnum].Bot.isbot))) + (bot_allowspy && players[pnum].Bot != NULL))) { break; } @@ -1156,7 +1156,7 @@ void G_Ticker () Printf ("%s is turbo!\n", players[i].userinfo.GetName()); } - if (netgame && !players[i].Bot.isbot && !demoplayback && (gametic%ticdup) == 0) + if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%ticdup) == 0) { //players[i].inconsistant = 0; if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy) @@ -1338,10 +1338,10 @@ void G_PlayerReborn (int player) int chasecam; BYTE currclass; userinfo_t userinfo; // [RH] Save userinfo - botskill_t b_skill; //Added by MC: APlayerPawn *actor; const PClass *cls; FString log; + DBot *OldBot; //Added by MC: p = &players[player]; @@ -1351,12 +1351,12 @@ void G_PlayerReborn (int player) itemcount = p->itemcount; secretcount = p->secretcount; currclass = p->CurrentPlayerClass; - b_skill = p->Bot.skill; //Added by MC: userinfo.TransferFrom(p->userinfo); actor = p->mo; cls = p->cls; log = p->LogText; chasecam = p->cheats & CF_CHASECAM; + OldBot = p->Bot; //Added by MC: // Reset player structure to its defaults p->~player_t(); @@ -1374,7 +1374,12 @@ void G_PlayerReborn (int player) p->LogText = log; p->cheats |= chasecam; - p->Bot.skill = b_skill; //Added by MC: + //Added by MC: Init bot structure. + if (OldBot != NULL) + { + p->Bot = new DBot; + p->Bot->skill = OldBot->skill; + } p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; @@ -1385,12 +1390,6 @@ void G_PlayerReborn (int player) actor->GiveDefaultInventory (); p->ReadyWeapon = p->PendingWeapon; } - - //Added by MC: Init bot structure. - if (bglobal.botingame[player]) - bglobal.CleanBotstuff (p); - else - p->Bot.isbot = false; } // diff --git a/src/g_level.cpp b/src/g_level.cpp index d84a48776..2d3aeee3a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1110,6 +1110,11 @@ void G_StartTravel () P_DelSector_List (); } } + + if (players[i].Bot != NULL) + { + players[i].Bot->ChangeStatNum (STAT_TRAVELLING); + } } } } @@ -1184,6 +1189,11 @@ void G_FinishTravel () pawn->SetState(pawn->SpawnState); pawn->player->SendPitchLimits(); + if (pawn->player->Bot != NULL) + { + pawn->player->Bot->ChangeStatNum (STAT_DEFAULT); + } + for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory) { inv->ChangeStatNum (STAT_INVENTORY); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 4836ae033..a16a1316f 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1024,8 +1024,8 @@ void AInventory::Touch (AActor *toucher) //Added by MC: Check if item taken was the roam destination of any bot for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && this == players[i].Bot.dest) - players[i].Bot.dest = NULL; + if (playeringame[i] && players[i].Bot != NULL && this == players[i].Bot->dest) + players[i].Bot->dest = NULL; } } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index d2780b4e4..afef9ffd3 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4228,7 +4228,7 @@ int DLevelScript::DoClassifyActor(int tid) { classify |= ACTOR_VOODOODOLL; } - if (actor->player->Bot.isbot) + if (actor->player->Bot != NULL) { classify |= ACTOR_BOT; } @@ -8632,7 +8632,7 @@ scriptwait: } else { - STACK(1) = players[STACK(1)].Bot.isbot; + STACK(1) = (players[STACK(1)].Bot != NULL); } break; diff --git a/src/p_doors.cpp b/src/p_doors.cpp index de4519505..844f23af9 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -460,7 +460,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, // run into them (otherwise opening them would be // a real pain). { - if (!thing->player || thing->player->Bot.isbot) + if (!thing->player || thing->player->Bot != NULL) return false; // JDC: bad guys never close doors //Added by MC: Neither do bots. diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 1dcf0ebad..6aa1a28f6 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -100,10 +100,10 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher) return; //Added by MC: Finished with this destination. - if (toucher->player != NULL && toucher->player->Bot.isbot && special == toucher->player->Bot.dest) + if (toucher->player != NULL && toucher->player->Bot != NULL && special == toucher->player->Bot->dest) { - toucher->player->Bot.prev = toucher->player->Bot.dest; - toucher->player->Bot.dest = NULL; + toucher->player->Bot->prev = toucher->player->Bot->dest; + toucher->player->Bot->dest = NULL; } special->Touch (toucher); @@ -608,17 +608,17 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) //Added by MC: Respawn bots if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) { - if (player->Bot.isbot) - player->Bot.t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; + if (player->Bot != NULL) + player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; //Added by MC: Discard enemies. for (int i = 0; i < MAXPLAYERS; i++) { - if (players[i].Bot.isbot && this == players[i].Bot.enemy) + if (players[i].Bot != NULL && this == players[i].Bot->enemy) { - if (players[i].Bot.dest == players[i].Bot.enemy) - players[i].Bot.dest = NULL; - players[i].Bot.enemy = NULL; + if (players[i].Bot->dest == players[i].Bot->enemy) + players[i].Bot->dest = NULL; + players[i].Bot->enemy = NULL; } } @@ -1194,9 +1194,9 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (player) { //Added by MC: Lets bots look allround for enemies if they survive an ambush. - if (player->Bot.isbot) + if (player->Bot != NULL) { - player->Bot.allround = true; + player->Bot->allround = true; } // end of game hell hack diff --git a/src/p_map.cpp b/src/p_map.cpp index 987d83293..e7c477955 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1919,13 +1919,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, } //Added by MC: To prevent bot from getting into dangerous sectors. - if (thing->player && thing->player->Bot.isbot && thing->flags & MF_SHOOTABLE) + if (thing->player && thing->player->Bot != NULL && thing->flags & MF_SHOOTABLE) { if (tm.sector != thing->Sector && bglobal.IsDangerous(tm.sector)) { - thing->player->Bot.prev = thing->player->Bot.dest; - thing->player->Bot.dest = NULL; + thing->player->Bot->prev = thing->player->Bot->dest; + thing->player->Bot->dest = NULL; thing->velx = 0; thing->vely = 0; thing->z = oldz; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e023a7181..a67b96dfb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3116,7 +3116,7 @@ void AActor::Tick () special2++; } //Added by MC: Freeze mode. - if (bglobal.freeze && !(player && !player->Bot.isbot)) + if (bglobal.freeze && !(player && player->Bot == NULL)) { return; } @@ -3237,18 +3237,18 @@ void AActor::Tick () bglobal.m_Thinking = true; for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || !players[i].Bot.isbot) + if (!playeringame[i] || players[i].Bot == NULL) continue; if (flags3 & MF3_ISMONSTER) { if (health > 0 - && !players[i].Bot.enemy + && !players[i].Bot->enemy && player ? !IsTeammate (players[i].mo) : true && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. - players[i].Bot.enemy = this; + players[i].Bot->enemy = this; } } else if (flags & MF_SPECIAL) @@ -3260,10 +3260,10 @@ void AActor::Tick () } else if (flags & MF_MISSILE) { - if (!players[i].Bot.missile && (flags3 & MF3_WARNBOT)) + if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) - players[i].Bot.missile = this; + players[i].Bot->missile = this; } } } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index e050bff16..6e699a41b 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -227,7 +227,7 @@ void P_FireWeapon (player_t *player, FState *state) // [SO] 9/2/02: People were able to do an awful lot of damage // when they were observers... - if (!player->Bot.isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } @@ -263,7 +263,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) // [SO] 9/2/02: People were able to do an awful lot of damage // when they were observers... - if (!player->Bot.isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } @@ -298,7 +298,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) void P_ReloadWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->Bot.isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } @@ -329,7 +329,7 @@ void P_ReloadWeapon (player_t *player, FState *state) void P_ZoomWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->Bot.isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index dd72a9cc8..5e9ecbdba 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -271,7 +271,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) dst->cheats |= chasecam; - if (dst->Bot.isbot) + if (dst->Bot != NULL) { botinfo_t *thebot = bglobal.botinfo; while (thebot && stricmp (name, thebot->name)) @@ -283,7 +283,6 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) thebot->inuse = true; } bglobal.botnum++; - bglobal.botingame[dst - players] = true; dst->userinfo.TransferFrom(uibackup2); } else diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 1a6e1e498..44fe88110 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -120,7 +120,7 @@ void P_Ticker (void) for (i = 0; iTick (); // [RH] moved this here diff --git a/src/p_user.cpp b/src/p_user.cpp index ee6957002..2036309ea 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -293,6 +293,7 @@ player_t::player_t() respawn_time(0), camera(0), air_finished(0), + Bot(0), BlendR(0), BlendG(0), BlendB(0), @@ -311,7 +312,6 @@ player_t::player_t() memset (&cmd, 0, sizeof(cmd)); memset (frags, 0, sizeof(frags)); memset (psprites, 0, sizeof(psprites)); - memset (&Bot, 0, sizeof(Bot)); } player_t &player_t::operator=(const player_t &p) @@ -379,29 +379,7 @@ player_t &player_t::operator=(const player_t &p) camera = p.camera; air_finished = p.air_finished; LastDamageType = p.LastDamageType; - Bot.savedyaw = p.Bot.savedyaw; - Bot.savedpitch = p.Bot.savedpitch; - Bot.angle = p.Bot.angle; - Bot.dest = p.Bot.dest; - Bot.prev = p.Bot.prev; - Bot.enemy = p.Bot.enemy; - Bot.missile = p.Bot.missile; - Bot.mate = p.Bot.mate; - Bot.last_mate = p.Bot.last_mate; - Bot.skill = p.Bot.skill; - Bot.t_active = p.Bot.t_active; - Bot.t_respawn = p.Bot.t_respawn; - Bot.t_strafe = p.Bot.t_strafe; - Bot.t_react = p.Bot.t_react; - Bot.t_fight = p.Bot.t_fight; - Bot.t_roam = p.Bot.t_roam; - Bot.t_rocket = p.Bot.t_rocket; - Bot.isbot = p.Bot.isbot; - Bot.first_shot = p.Bot.first_shot; - Bot.sleft = p.Bot.sleft; - Bot.allround = p.Bot.allround; - Bot.oldx = p.Bot.oldx; - Bot.oldy = p.Bot.oldy; + Bot = p.Bot; settings_controller = p.settings_controller; BlendR = p.BlendR; BlendG = p.BlendG; @@ -444,12 +422,7 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep) if (*&poisoner == old) poisoner = replacement, changed++; if (*&attacker == old) attacker = replacement, changed++; if (*&camera == old) camera = replacement, changed++; - if (*&Bot.dest == old) Bot.dest = replacement, changed++; - if (*&Bot.prev == old) Bot.prev = replacement, changed++; - if (*&Bot.enemy == old) Bot.enemy = replacement, changed++; - if (*&Bot.missile == old) Bot.missile = replacement, changed++; - if (*&Bot.mate == old) Bot.mate = replacement, changed++; - if (*&Bot.last_mate == old) Bot.last_mate = replacement, changed++; + if (*&Bot == old) Bot = static_cast(rep), changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; if (*&PremorphWeapon == old) PremorphWeapon = static_cast(rep), changed++; @@ -464,12 +437,7 @@ size_t player_t::PropagateMark() GC::Mark(poisoner); GC::Mark(attacker); GC::Mark(camera); - GC::Mark(Bot.dest); - GC::Mark(Bot.prev); - GC::Mark(Bot.enemy); - GC::Mark(Bot.missile); - GC::Mark(Bot.mate); - GC::Mark(Bot.last_mate); + GC::Mark(Bot); GC::Mark(ReadyWeapon); GC::Mark(ConversationNPC); GC::Mark(ConversationPC); @@ -718,10 +686,10 @@ void APlayerPawn::SetupWeaponSlots() // If we're the local player, then there's a bit more work to do. // This also applies if we're a bot and this is the net arbitrator. if (player - players == consoleplayer || - (player->Bot.isbot && consoleplayer == Net_Arbitrator)) + (player->Bot != NULL && consoleplayer == Net_Arbitrator)) { FWeaponSlots local_slots(player->weapons); - if (player->Bot.isbot) + if (player->Bot != NULL) { // Bots only need weapons from KEYCONF, not INI modifications. P_PlaybackKeyConfWeapons(&local_slots); } @@ -2153,7 +2121,7 @@ void P_DeathThink (player_t *player) if ((player->cmd.ucmd.buttons & BT_USE || ((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN)) { - if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->Bot.isbot)) + if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && player->Bot == NULL)) { player->cls = NULL; // Force a new class if the player is using a random class player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; @@ -2985,9 +2953,17 @@ void player_t::Serialize (FArchive &arc) << respawn_time << air_finished << turnticks - << oldbuttons - << Bot.isbot - << BlendR + << oldbuttons; + bool IsBot; + if (SaveVersion >= 4514) + { + arc << Bot; + } + else + { + arc << IsBot; + } + arc << BlendR << BlendG << BlendB << BlendA; @@ -3070,32 +3046,30 @@ void player_t::Serialize (FArchive &arc) onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2); } - if (Bot.isbot) + if (SaveVersion < 4514 && IsBot) { - arc << Bot.angle - << Bot.dest - << Bot.prev - << Bot.enemy - << Bot.missile - << Bot.mate - << Bot.last_mate - << Bot.skill - << Bot.t_active - << Bot.t_respawn - << Bot.t_strafe - << Bot.t_react - << Bot.t_fight - << Bot.t_roam - << Bot.t_rocket - << Bot.first_shot - << Bot.sleft - << Bot.allround - << Bot.oldx - << Bot.oldy; - } - else - { - Bot.dest = Bot.prev = Bot.enemy = Bot.missile = Bot.mate = Bot.last_mate = NULL; + Bot = new DBot; + + arc << Bot->angle + << Bot->dest + << Bot->prev + << Bot->enemy + << Bot->missile + << Bot->mate + << Bot->last_mate + << Bot->skill + << Bot->t_active + << Bot->t_respawn + << Bot->t_strafe + << Bot->t_react + << Bot->t_fight + << Bot->t_roam + << Bot->t_rocket + << Bot->first_shot + << Bot->sleft + << Bot->allround + << Bot->oldx + << Bot->oldy; } if (arc.IsLoading ()) { diff --git a/src/version.h b/src/version.h index a7226d8bb..d9597d2f4 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 4513 +#define SAVEVER 4514 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 9e773993b..02492f272 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1330,7 +1330,7 @@ void WI_updateDeathmatchStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) break; } @@ -1429,7 +1429,7 @@ void WI_drawDeathmatchStats () clamp(int(g*255.f), 0, 255), clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); - if (playerready[pnum] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion + if (playerready[pnum] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); @@ -1638,7 +1638,7 @@ void WI_updateNetgameStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) break; } @@ -1735,7 +1735,7 @@ void WI_drawNetgameStats () clamp(int(g*255.f), 0, 255), clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); - if (playerready[i] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion + if (playerready[i] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); @@ -2010,7 +2010,7 @@ void WI_checkForAccelerate(void) { if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && ((players[i].cmd.ucmd.buttons & players[i].oldbuttons) - == players[i].oldbuttons) && !player->Bot.isbot) + == players[i].oldbuttons) && player->Bot == NULL) { acceleratestage = 1; playerready[i] = true; From c400dcf7365f5624d8a18881244b6675c64389e4 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Wed, 15 Oct 2014 17:54:12 +0100 Subject: [PATCH 130/313] - Made DBot inherit from DObject. --- src/b_bot.cpp | 10 ++++++++++ src/b_bot.h | 6 ++++-- src/b_game.cpp | 15 +++++++++++++++ src/g_game.cpp | 20 +++++++++++--------- src/g_level.cpp | 10 ---------- src/p_setup.cpp | 1 + 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 30a8e4877..3494dc4c4 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -24,6 +24,11 @@ IMPLEMENT_POINTY_CLASS(DBot) END_POINTERS DBot::DBot () +{ + Clear (); +} + +void DBot::Clear () { savedyaw = 0; savedpitch = 0; @@ -123,6 +128,11 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) bot->inuse = false; bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE; } + if (players[i].Bot != NULL) + { + players[i].Bot->Destroy (); + players[i].Bot = NULL; + } players[i].~player_t(); ::new(&players[i]) player_t; players[i].userinfo.Reset(); diff --git a/src/b_bot.h b/src/b_bot.h index f414a9687..7844597cd 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -90,6 +90,7 @@ public: void ForgetBots (); void DoAddBot (BYTE **stream); void RemoveAllBots (bool fromlist); + void DestroyAllBots (); //(B_Func.c) bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); @@ -143,13 +144,14 @@ protected: bool observer; //Consoleplayer is observer. }; -class DBot : public DThinker +class DBot : public DObject { - DECLARE_CLASS(DBot,DThinker) + DECLARE_CLASS(DBot,DObject) HAS_OBJECT_POINTERS public: DBot (); + void Clear (); void Serialize (FArchive &arc); angle_t savedyaw; diff --git a/src/b_game.cpp b/src/b_game.cpp index 35de6ec86..30c2c483b 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -352,6 +352,9 @@ void FCajunMaster::DoAddBot (BYTE **stream) skill.isp = ReadByte (stream); D_ReadUserInfoStrings (bnum, &infob, false); + + delete[] info; + if (!deathmatch && playerstarts[bnum].type == 0) { Printf ("%s tried to join, but there was no player %d start\n", @@ -423,6 +426,18 @@ void FCajunMaster::RemoveAllBots (bool fromlist) botnum = 0; } +void FCajunMaster::DestroyAllBots () +{ + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (players[i].Bot != NULL) + { + players[i].Bot->Destroy (); + players[i].Bot = NULL; + } + } +} + //------------------ //Reads data for bot from diff --git a/src/g_game.cpp b/src/g_game.cpp index fad7d9eb1..a58753b86 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1341,7 +1341,7 @@ void G_PlayerReborn (int player) APlayerPawn *actor; const PClass *cls; FString log; - DBot *OldBot; //Added by MC: + DBot *Bot; //Added by MC: p = &players[player]; @@ -1356,7 +1356,7 @@ void G_PlayerReborn (int player) cls = p->cls; log = p->LogText; chasecam = p->cheats & CF_CHASECAM; - OldBot = p->Bot; //Added by MC: + Bot = p->Bot; //Added by MC: // Reset player structure to its defaults p->~player_t(); @@ -1373,13 +1373,7 @@ void G_PlayerReborn (int player) p->cls = cls; p->LogText = log; p->cheats |= chasecam; - - //Added by MC: Init bot structure. - if (OldBot != NULL) - { - p->Bot = new DBot; - p->Bot->skill = OldBot->skill; - } + p->Bot = Bot; //Added by MC: p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; @@ -1390,6 +1384,14 @@ void G_PlayerReborn (int player) actor->GiveDefaultInventory (); p->ReadyWeapon = p->PendingWeapon; } + + //Added by MC: Init bot structure. + if (p->Bot != NULL) + { + botskill_t skill = p->Bot->skill; + p->Bot->Clear (); + p->Bot->skill = skill; + } } // diff --git a/src/g_level.cpp b/src/g_level.cpp index 2d3aeee3a..d84a48776 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1110,11 +1110,6 @@ void G_StartTravel () P_DelSector_List (); } } - - if (players[i].Bot != NULL) - { - players[i].Bot->ChangeStatNum (STAT_TRAVELLING); - } } } } @@ -1189,11 +1184,6 @@ void G_FinishTravel () pawn->SetState(pawn->SpawnState); pawn->player->SendPitchLimits(); - if (pawn->player->Bot != NULL) - { - pawn->player->Bot->ChangeStatNum (STAT_DEFAULT); - } - for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory) { inv->ChangeStatNum (STAT_INVENTORY); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 6b5d9403f..53b288798 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4175,6 +4175,7 @@ static void P_Shutdown () P_FreeLevelData (); P_FreeExtraLevelData (); ST_Clear(); + bglobal.DestroyAllBots (); } #if 0 From 1715b25a3e8c5936d42420412498c64b8d119465 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 18 Oct 2014 12:33:39 +0300 Subject: [PATCH 131/313] Fixed Unknown CMake command "CHECK_LIBRARY_EXISTS" error on Unix targets Previously it worked somehow but not with CMake 3.0.2 --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1444f1cf3..e887775fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ endif( COMMAND cmake_policy ) include( CheckCXXSourceCompiles ) include( CheckFunctionExists ) include( CheckCXXCompilerFlag ) +include( CheckLibraryExists ) include( FindPkgConfig ) if( NOT APPLE ) From b2110db1f5ff6fdc93113ea48243a22ee53dac88 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 18 Oct 2014 14:55:27 +0300 Subject: [PATCH 132/313] Extended list of display resolutions --- src/cocoa/i_backend_cocoa.mm | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 32754a262..ffd95b5b3 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1541,36 +1541,52 @@ SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) ZD_UNUSED(format); ZD_UNUSED(flags); - static std::vector< SDL_Rect* > resolutions; + static std::vector resolutions; if (resolutions.empty()) { -#define DEFINE_RESOLUTION(WIDTH, HEIGHT) \ +#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, 600); + 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 From 82255c4dfd0a7a8bcc92cda6958f4d951147157b Mon Sep 17 00:00:00 2001 From: John Palomo Jr Date: Sun, 19 Oct 2014 20:52:48 -0400 Subject: [PATCH 133/313] Using PickActor will only set the TID if the picked actor doesn't have a TID already. An optional 8th parameter (bool forceTID) was added to revert to the old behavior. --- src/p_acs.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index bc4cbdb75..aeb47a807 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5619,15 +5619,27 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) wallMask = args[6]; } + bool forceTID = 0; + if (argCount >= 8) + { + if (args[7] != 0) + forceTID = 1; + } + AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); if (pickedActor == NULL) { return 0; } - pickedActor->RemoveFromHash(); - pickedActor->tid = args[4]; - pickedActor->AddToHash(); - + if (!(forceTID) && (args[4] == 0) && (pickedActor->tid == 0)) + return 0; + + if ((pickedActor->tid == 0) || (forceTID)) + { + pickedActor->RemoveFromHash(); + pickedActor->tid = args[4]; + pickedActor->AddToHash(); + } return 1; } break; From 759f1bb81bd0c91cdd4f8df72bb3a44f18fc4acd Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 21 Oct 2014 23:38:45 +1300 Subject: [PATCH 134/313] Fix handshakes between GCC and MSVC u_long has two different sizes between Linux GCC and MSVC Stopped netgames from starting up if player count wont fit --- src/i_net.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i_net.cpp b/src/i_net.cpp index 6fdcb52ba..03db5a15c 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -135,7 +135,7 @@ struct PreGamePacket }; struct { - u_long address; + DWORD address; u_short port; BYTE player; BYTE pad; @@ -660,6 +660,12 @@ void HostGame (int i) numplayers = 2; } + if (numplayers > MAXNETNODES) + { + I_FatalError("You cannot host a game with %d players. The limit is currently %d.", numplayers, MAXNETNODES); + return; + } + if (numplayers == 1) { // Special case: Only 1 player, so don't bother starting the network netgame = false; From ed5108357269f07964a876ba0e893090dc3b3f67 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 21 Oct 2014 23:44:14 +1300 Subject: [PATCH 135/313] Change u_short as well. --- src/i_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_net.cpp b/src/i_net.cpp index 03db5a15c..188d2871e 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -136,7 +136,7 @@ struct PreGamePacket struct { DWORD address; - u_short port; + WORD port; BYTE player; BYTE pad; } machines[MAXNETNODES]; From c9bc9b427ca3238324b3f89b690c1c1dbf4be1cf Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Tue, 21 Oct 2014 19:31:11 +0100 Subject: [PATCH 136/313] - Removed p_leader. --- src/b_bot.h | 1 + src/b_func.cpp | 33 ++++++++++++++------------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/b_bot.h b/src/b_bot.h index 7844597cd..51fe64129 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -125,6 +125,7 @@ private: //(B_Func.c) bool Reachable (AActor *actor, AActor *target); void Dofire (AActor *actor, ticcmd_t *cmd); + bool IsLeader (player_t *player); AActor *Choose_Mate (AActor *bot); AActor *Find_enemy (AActor *bot); void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); diff --git a/src/b_func.cpp b/src/b_func.cpp index 0c0ab1ebd..0303de8c6 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -270,6 +270,19 @@ shootmissile: //actor->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); } +bool FCajunMaster::IsLeader (player_t *player) +{ + for (int count = 0; count < MAXPLAYERS; count++) + { + if (players[count].Bot != NULL + && players[count].Bot->mate == player->mo) + { + return true; + } + } + + return false; +} //This function is called every //tick (for each bot) to set @@ -277,11 +290,9 @@ shootmissile: AActor *FCajunMaster::Choose_Mate (AActor *bot) { int count; - int count2; fixed_t closest_dist, test; AActor *target; AActor *observer; - bool p_leader[MAXPLAYERS]; //is mate alive? if (bot->player->Bot->mate) @@ -299,22 +310,6 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) if (bot->player->Bot->last_mate->health <= 0) bot->player->Bot->last_mate = NULL; - for (count = 0; count < MAXPLAYERS; count++) - { - if (!playeringame[count]) - continue; - p_leader[count] = false; - for (count2 = 0; count2 < MAXPLAYERS; count2++) - { - if (players[count2].Bot != NULL - && players[count2].Bot->mate == players[count].mo) - { - p_leader[count] = true; - break; - } - } - } - target = NULL; closest_dist = FIXED_MAX; if (bot_observer) @@ -334,7 +329,7 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) && client->mo->health > 0 && client->mo != observer && ((bot->health/2) <= client->mo->health || !deathmatch) - && !p_leader[count]) //taken? + && !IsLeader(client)) //taken? { if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) { From 6166e83d287136ecc9d68de4beae4b2d2644ec99 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Tue, 21 Oct 2014 19:32:15 +0100 Subject: [PATCH 137/313] - Fixed: Only setting controllers should be able to remove bots. --- src/b_bot.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 3494dc4c4..969d57a1b 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -141,6 +141,12 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) CCMD (removebots) { + if (!players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can remove bots\n"); + return; + } + Net_WriteByte (DEM_KILLBOTS); } From acd4a71de2c9cdc12fe37ac88cdb413b01169ea9 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Tue, 21 Oct 2014 19:33:48 +0100 Subject: [PATCH 138/313] - Fixed: New DBot instances were missing write barriers. --- src/b_game.cpp | 1 + src/p_user.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/b_game.cpp b/src/b_game.cpp index 30c2c483b..807ec2b46 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -369,6 +369,7 @@ void FCajunMaster::DoAddBot (BYTE **stream) { multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). players[bnum].Bot = new DBot; + GC::WriteBarrier (players[bnum].Bot); players[bnum].Bot->skill = skill; playeringame[bnum] = true; players[bnum].mo = NULL; diff --git a/src/p_user.cpp b/src/p_user.cpp index 8980050e7..51105cd3d 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3049,6 +3049,7 @@ void player_t::Serialize (FArchive &arc) if (SaveVersion < 4514 && IsBot) { Bot = new DBot; + GC::WriteBarrier (Bot); arc << Bot->angle << Bot->dest From d6a33a601d76748afd636ee77235a9b3cfe95601 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 23 Oct 2014 17:33:42 +0200 Subject: [PATCH 139/313] - fixed: The sorting of 3D floors was incomplete and missed cases where translucent volumes overlapped non-translucent ones only at their top. It also didn't account for one translucent volume being intersected by more than solid one. --- src/p_3dfloors.cpp | 83 +++++++++++++++++++++++++++++++++++++++++----- src/p_3dfloors.h | 7 ++++ 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index bdeb45d78..d13e46204 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -120,6 +120,7 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag //Add the floor ffloor = new F3DFloor; + ffloor->top.copied = ffloor->bottom.copied = false; ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2; ffloor->target = sec; ffloor->ceilingclip = ffloor->floorclip = NULL; @@ -420,6 +421,8 @@ void P_Recalculate3DFloors(sector_t * sector) F3DFloor * pick; unsigned pickindex; F3DFloor * clipped=NULL; + F3DFloor * solid=NULL; + fixed_t solid_bottom=0; fixed_t clipped_top; fixed_t clipped_bottom=0; fixed_t maxheight, minheight; @@ -477,6 +480,7 @@ void P_Recalculate3DFloors(sector_t * sector) } oldlist.Delete(pickindex); + fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); if (pick->flags & FF_THISINSIDE) { @@ -486,10 +490,38 @@ void P_Recalculate3DFloors(sector_t * sector) } else if (pick->flags&(FF_SWIMMABLE|FF_TRANSLUCENT) && pick->flags&FF_EXISTS) { - clipped=pick; - clipped_top=height; - clipped_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); - ffloors.Push(pick); + // We must check if this nonsolid segment gets clipped from the top by another 3D floor + if (solid != NULL && solid_bottom < height) + { + ffloors.Push(pick); + if (solid_bottom < pick_bottom) + { + // this one is fully covered + pick->flags|=FF_CLIPPED; + pick->flags&=~FF_EXISTS; + } + else + { + F3DFloor * dyn=new F3DFloor; + *dyn=*pick; + pick->flags|=FF_CLIPPED; + pick->flags&=~FF_EXISTS; + dyn->flags|=FF_DYNAMIC; + dyn->top.copyPlane(&solid->bottom); + ffloors.Push(dyn); + + clipped = dyn; + clipped_top = solid_bottom; + clipped_bottom = pick_bottom; + } + } + else + { + clipped = pick; + clipped_top = height; + clipped_bottom = pick_bottom; + ffloors.Push(pick); + } } else if (clipped && clipped_bottomflags|=FF_CLIPPED; clipped->flags&=~FF_EXISTS; dyn->flags|=FF_DYNAMIC; - dyn->bottom=pick->top; + dyn->bottom.copyPlane(&pick->top); ffloors.Push(dyn); ffloors.Push(pick); - fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); - if (pick_bottom<=clipped_bottom) { clipped=NULL; @@ -515,14 +545,25 @@ void P_Recalculate3DFloors(sector_t * sector) dyn=new F3DFloor; *dyn=*clipped; dyn->flags|=FF_DYNAMIC|FF_EXISTS; - dyn->top=pick->bottom; + dyn->top.copyPlane(&pick->bottom); ffloors.Push(dyn); + clipped = dyn; + clipped_top = pick_bottom; } + solid = pick; + solid_bottom = pick_bottom; } else { - clipped=NULL; + clipped = NULL; + if (solid == NULL || solid_bottom > pick_bottom) + { + // only if this one is lower + solid = pick; + solid_bottom = pick_bottom; + } ffloors.Push(pick); + } } @@ -910,3 +951,27 @@ int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, b } #endif + +#include "c_dispatch.h" + + +CCMD (dump3df) +{ + if (argv.argc() > 1) + { + int sec = strtol(argv[1], NULL, 10); + sector_t *sector = §ors[sec]; + TArray & ffloors=sector->e->XFloor.ffloors; + + for (unsigned int i = 0; i < ffloors.Size(); i++) + { + fixed_t height=ffloors[i]->top.plane->ZatPoint(CenterSpot(sector)); + fixed_t bheight=ffloors[i]->bottom.plane->ZatPoint(CenterSpot(sector)); + + 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":""); + } + } +} diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index d0bca5e8f..8d42560af 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -77,6 +77,13 @@ struct F3DFloor sector_t * model; int isceiling; int vindex; + bool copied; + + void copyPlane(planeref * other) + { + *this = *other; + copied = true; + } }; planeref bottom; From 68246051548602182df480cc280384246b5b2b07 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 00:06:18 +0200 Subject: [PATCH 140/313] - added pickup sprite and editor number for Heretic's gold wand. --- wadsrc/static/actors/heretic/hereticweaps.txt | 6 +++++- wadsrc/static/language.enu | 1 + wadsrc/static/sprites/GWANA0.png | Bin 0 -> 315 bytes 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 wadsrc/static/sprites/GWANA0.png diff --git a/wadsrc/static/actors/heretic/hereticweaps.txt b/wadsrc/static/actors/heretic/hereticweaps.txt index 972fba5fb..ca85437e0 100644 --- a/wadsrc/static/actors/heretic/hereticweaps.txt +++ b/wadsrc/static/actors/heretic/hereticweaps.txt @@ -110,7 +110,7 @@ ACTOR StaffPuff2 // Gold wand ---------------------------------------------------------------- -ACTOR GoldWand : HereticWeapon +ACTOR GoldWand : HereticWeapon 9042 { Game Heretic +BLOODSPLATTER @@ -120,6 +120,7 @@ ACTOR GoldWand : HereticWeapon Weapon.AmmoType "GoldWandAmmo" Weapon.SisterWeapon "GoldWandPowered" Weapon.YAdjust 5 + Inventory.PickupMessage "$TXT_WPNGOLDWAND" Obituary "$OB_MPGOLDWAND" Tag "$TAG_GOLDWAND" @@ -127,6 +128,9 @@ ACTOR GoldWand : HereticWeapon States { + Spawn: + GWAN A -1 + Stop Ready: GWND A 1 A_WeaponReady Loop diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index a92e22dfe..17b56f6f3 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1242,6 +1242,7 @@ TXT_AMMOPHOENIXROD2 = "INFERNO ORB"; // Weapons +TXT_WPNGOLDWAND = "GOLD WAND"; TXT_WPNMACE = "FIREMACE"; TXT_WPNCROSSBOW = "ETHEREAL CROSSBOW"; TXT_WPNBLASTER = "DRAGON CLAW"; diff --git a/wadsrc/static/sprites/GWANA0.png b/wadsrc/static/sprites/GWANA0.png new file mode 100644 index 0000000000000000000000000000000000000000..f887f04c65d070bff04ff7b7d19354a91daf471a GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^oItG3!3-o{y_}Q+q&U)x9Fu@FNJvdwaFREWjtcMz zaTO2{U}9oYQBiSobF;RwUYRbHnwn~EZf+>eXDiR==jT^bQ`6nuZEtTM78Yh;VBn=H z9B(Y0ZY`galXGW^Ot`+dj*d=ANlCT0dVjQWY;3HAgv6iSmcQ>-e_Wr?2DF8-B*-tA z!Qt7BG$5zi)5S5QB5v-v>s(HTJZulntlQjPyyt)Rt(Uz@NeXY-pZasH6v=wIj`#bC zCijIYoDSljvR^bvgo?~$IH9z-mqB9Pas!oy(8XriulxT_V_huC%^ Date: Fri, 24 Oct 2014 04:46:43 -0400 Subject: [PATCH 141/313] Added Inventory.AlwaysRespawn flag. --- src/g_shared/a_pickups.cpp | 2 +- src/g_shared/a_pickups.h | 1 + src/thingdef/thingdef_data.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index a16a1316f..fc3e863a2 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -499,7 +499,7 @@ bool AInventory::ShouldRespawn () { if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false; if (ItemFlags & IF_NEVERRESPAWN) return false; - return !!(dmflags & DF_ITEMS_RESPAWN); + return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN)); } //=========================================================================== diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 72548776a..8616393e7 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -135,6 +135,7 @@ enum IF_NEVERRESPAWN = 1<<20, // Never, ever respawns IF_NOSCREENFLASH = 1<<21, // No pickup flash on the player's screen IF_TOSSED = 1<<22, // Was spawned by P_DropItem (i.e. as a monster drop) + IF_ALWAYSRESPAWN = 1<<23, // Always respawn, regardless of dmflag }; diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 013415cc7..5c52c45e3 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -311,6 +311,7 @@ static FFlagDef InventoryFlags[] = DEFINE_FLAG(IF, NEVERRESPAWN, AInventory, ItemFlags), DEFINE_FLAG(IF, NOSCREENFLASH, AInventory, ItemFlags), DEFINE_FLAG(IF, TOSSED, AInventory, ItemFlags), + DEFINE_FLAG(IF, ALWAYSRESPAWN, AInventory, ItemFlags), DEFINE_DEPRECATED_FLAG(PICKUPFLASH), DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),}; From 9402bcf6cd654091f9ae2fdb848b04f9654d08dc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 22:47:40 +0200 Subject: [PATCH 142/313] - fixed: The targeter must reset its HUD sprites when being reactivated during the blinking phase. --- src/g_shared/a_artifacts.cpp | 12 ++++++++++++ src/g_shared/a_artifacts.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 8891c8674..7fd15fa21 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -1296,6 +1296,18 @@ void APowerTargeter::InitEffect () PositionAccuracy (); } +bool APowerTargeter::HandlePickup(AInventory *item) +{ + if (Super::HandlePickup(item)) + { + InitEffect(); // reset the HUD sprites + return true; + } + return false; +} + + + void APowerTargeter::DoEffect () { Super::DoEffect (); diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 0efd64ab8..a216cf74c 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -173,6 +173,7 @@ protected: void EndEffect (); void PositionAccuracy (); void Travelled (); + bool HandlePickup(AInventory *item); }; class APowerFrightener : public APowerup From e662e4321ac114fc507da48075ea9e66a990419e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 22:56:10 +0200 Subject: [PATCH 143/313] - fixed: Creating an OPL MIDI device can fail if no GENMIDI lump is found, and an exception is thrown. We need to handle this exception to prevent an abort. --- src/sound/music_midistream.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 15f4caec8..5cb04ed0e 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -38,6 +38,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "doomerrors.h" // MACROS ------------------------------------------------------------------ @@ -277,7 +278,16 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const return new TimidityMIDIDevice; case MDEV_OPL: - return new OPLMIDIDevice; + try + { + return new OPLMIDIDevice; + } + catch (CRecoverableError &err) + { + // The creation of an OPL MIDI device can abort with an error if no GENMIDI lump can be found. + Printf("Unable to create OPL MIDI device: %s\nFalling back to FModEx playback", err.GetMessage()); + return new FMODMIDIDevice; + } case MDEV_TIMIDITY: return new TimidityPPMIDIDevice; From 15b1c712578351fa523597216fbebcdf9de682a9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 23:02:02 +0200 Subject: [PATCH 144/313] - use proper CheckCheatMode semantics for throwing players out of chasecam mode if DF2_CHASECAM gets unset. --- src/d_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 030387118..255994fb5 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -466,7 +466,7 @@ CUSTOM_CVAR (Int, dmflags2, 0, CVAR_SERVERINFO) } // Come out of chasecam mode if we're not allowed to use chasecam. - if (!(dmflags2 & DF2_CHASECAM) && !G_SkillProperty (SKILLP_DisableCheats) && !sv_cheats) + if (!(dmflags2 & DF2_CHASECAM) && CheckCheatmode(false)) { // Take us out of chasecam mode only. if (p->cheats & CF_CHASECAM) From 6f7b83faa1383be3eee2ca841c67850568093c65 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 23:12:38 +0200 Subject: [PATCH 145/313] - fixed: EV_DoFloor was a bit too selective about which movement types were allowed to crush. Changed so that the crush parameter is always used. Also changed all non-crushing calls to explicitly pass -1 as crush damage instead of relying on EV_DoFloor to ignore the passed value. --- src/g_strife/a_alienspectres.cpp | 4 ++-- src/g_strife/a_crusader.cpp | 2 +- src/g_strife/a_strifestuff.cpp | 2 +- src/p_enemy.cpp | 8 +++---- src/p_floor.cpp | 4 +--- src/p_lnspec.cpp | 36 ++++++++++++++++---------------- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/g_strife/a_alienspectres.cpp b/src/g_strife/a_alienspectres.cpp index 4626891bb..2b9f499e9 100644 --- a/src/g_strife/a_alienspectres.cpp +++ b/src/g_strife/a_alienspectres.cpp @@ -106,7 +106,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) switch (self->GetClass()->TypeName) { case NAME_AlienSpectre1: - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, -1, 0, false); log = 95; break; @@ -180,7 +180,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) { // Another Sigil piece. Woohoo! log = 83; } - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); break; default: diff --git a/src/g_strife/a_crusader.cpp b/src/g_strife/a_crusader.cpp index 6d34182f4..7ef0b24a1 100644 --- a/src/g_strife/a_crusader.cpp +++ b/src/g_strife/a_crusader.cpp @@ -80,6 +80,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CrusaderDeath) { if (CheckBossDeath (self)) { - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, -1, 0, false); } } diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index d51b618b7..95b0030a0 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -548,7 +548,7 @@ void APowerCoupling::Die (AActor *source, AActor *inflictor, int dmgflags) P_NoiseAlert (source, this); } EV_DoDoor (DDoor::doorClose, NULL, players[i].mo, 225, 2*FRACUNIT, 0, 0, 0); - EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, -1, 0, false); players[i].mo->GiveInventoryType (QuestItemClasses[5]); S_Sound (CHAN_VOICE, "svox/voc13", 1, ATTN_NORM); players[i].SetLogNumber (13); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 00e2a3dc2..20b61200f 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3275,13 +3275,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) { if (type == NAME_Fatso) { - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; } if (type == NAME_Arachnotron) { - EV_DoFloor (DFloor::floorRaiseByTexture, NULL, 667, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorRaiseByTexture, NULL, 667, FRACUNIT, 0, -1, 0, false); return; } } @@ -3290,11 +3290,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) switch (level.flags & LEVEL_SPECACTIONSMASK) { case LEVEL_SPECLOWERFLOOR: - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; case LEVEL_SPECLOWERFLOORTOHIGHEST: - EV_DoFloor (DFloor::floorLowerToHighest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToHighest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; case LEVEL_SPECOPENDOOR: diff --git a/src/p_floor.cpp b/src/p_floor.cpp index f63087619..9a5a6eaf8 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -320,7 +320,7 @@ manual_floor: rtn = true; floor = new DFloor (sec); floor->m_Type = floortype; - floor->m_Crush = -1; + floor->m_Crush = crush; floor->m_Hexencrush = hexencrush; floor->m_Speed = speed; floor->m_ResetCount = 0; // [RH] @@ -374,7 +374,6 @@ manual_floor: break; case DFloor::floorRaiseAndCrushDoom: - floor->m_Crush = crush; case DFloor::floorRaiseToLowestCeiling: floor->m_Direction = 1; newheight = sec->FindLowestCeilingSurrounding (&spot); @@ -406,7 +405,6 @@ manual_floor: break; case DFloor::floorRaiseAndCrush: - floor->m_Crush = crush; floor->m_Direction = 1; newheight = sec->FindLowestCeilingPoint (&spot) - 8*FRACUNIT; floor->m_FloorDestDist = sec->floorplane.PointToDist (spot, newheight); diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 14bcd430c..0d74b4d42 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -281,43 +281,43 @@ FUNC(LS_Generic_Door) FUNC(LS_Floor_LowerByValue) // Floor_LowerByValue (tag, speed, height) { - return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false); } FUNC(LS_Floor_LowerToLowest) // Floor_LowerToLowest (tag, speed) { - return EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_LowerToHighest) // Floor_LowerToHighest (tag, speed, adjust, hereticlower) { - return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), (arg2-128)*FRACUNIT, 0, 0, false, arg3==1); + return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), (arg2-128)*FRACUNIT, -1, 0, false, arg3==1); } FUNC(LS_Floor_LowerToNearest) // Floor_LowerToNearest (tag, speed) { - return EV_DoFloor (DFloor::floorLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByValue) // Floor_RaiseByValue (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false); } FUNC(LS_Floor_RaiseToHighest) // Floor_RaiseToHighest (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToHighest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseToNearest) // Floor_RaiseToNearest (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseAndCrush) @@ -335,13 +335,13 @@ FUNC(LS_Floor_RaiseAndCrushDoom) FUNC(LS_Floor_RaiseByValueTimes8) // FLoor_RaiseByValueTimes8 (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false); } FUNC(LS_Floor_LowerByValueTimes8) // Floor_LowerByValueTimes8 (tag, speed, height) { - return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false); } FUNC(LS_Floor_CrushStop) @@ -353,51 +353,51 @@ FUNC(LS_Floor_CrushStop) FUNC(LS_Floor_LowerInstant) // Floor_LowerInstant (tag, unused, height) { - return EV_DoFloor (DFloor::floorLowerInstant, ln, arg0, 0, arg2*FRACUNIT*8, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false); } FUNC(LS_Floor_RaiseInstant) // Floor_RaiseInstant (tag, unused, height) { - return EV_DoFloor (DFloor::floorRaiseInstant, ln, arg0, 0, arg2*FRACUNIT*8, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false); } FUNC(LS_Floor_MoveToValueTimes8) // Floor_MoveToValueTimes8 (tag, speed, height, negative) { return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1), - arg2*FRACUNIT*8*(arg3?-1:1), 0, 0, false); + arg2*FRACUNIT*8*(arg3?-1:1), -1, 0, false); } FUNC(LS_Floor_MoveToValue) // Floor_MoveToValue (tag, speed, height, negative) { return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1), - arg2*FRACUNIT*(arg3?-1:1), 0, 0, false); + arg2*FRACUNIT*(arg3?-1:1), -1, 0, false); } FUNC(LS_Floor_RaiseToLowestCeiling) // Floor_RaiseToLowestCeiling (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToLowestCeiling, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToLowestCeiling, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByTexture) // Floor_RaiseByTexture (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByTexture, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByValueTxTy) // Floor_RaiseByValueTxTy (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false); } FUNC(LS_Floor_LowerToLowestTxTy) // Floor_LowerToLowestTxTy (tag, speed) { - return EV_DoFloor (DFloor::floorLowerAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false); } FUNC(LS_Floor_Waggle) @@ -1764,7 +1764,7 @@ FUNC(LS_FloorAndCeiling_LowerRaise) // more or less unintuitive value for the fourth arg to trigger Boom's broken behavior if (arg3 != 1998 || !res) // (1998 for the year in which Boom was released... :P) { - res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } return res; } From e28234a8b847e2701a596caf40261a8d615e0c29 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Oct 2014 23:19:57 +0200 Subject: [PATCH 146/313] - replaced pistol pickup sprite. --- wadsrc/static/sprites/pista0.png | Bin 407 -> 377 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/wadsrc/static/sprites/pista0.png b/wadsrc/static/sprites/pista0.png index b3d9cd09a5831dadb93984064d0c877f46592bbc..9e3a4a05242a9e3fb25cab0c3fcce08d02cb0797 100644 GIT binary patch delta 350 zcmV-k0iphv1Nj1wB!38JazSDM000O8000hEbPJ#W003Q3OjJbx|Nj6002dDj8yg!7 z3kxG7BPS;(H$NvYHyisOeMaiNpvD_qMu71&Ziv>1XMl2slL;|neWoY4{!@=aqU7(JZzL$< wJ7{$A$RyGtU?l6rG)?QWv}qgE5s+H)1hJ0@AUrJvGynhq07*qoM6N<$f>dLHj{pDw delta 361 zcmV-v0ha#x0+$1jB!7)iOjJbxFE2MYHzy}29~TQRFE1-24?8Ew- zcV9nOS64qT7Yi2$Q$H_TTU$RX8(&K|BO@b2GaF}9Gh<_8Z*Omli;IJ6OGigXM>8Ws zFB=yh2TL~_BP$PsgM*WklS@lWYey%`%gZk}9~Tb?r>Cc%pCq4ue}7Xy9~&DRcXxN6 zgJU-%6ZpA2p#T5?0d!JMQvg8b*k+L-7k{KlL_t(|UX{s3a>Fnb0ML(OnIQ;e?){Hf zH>-F?o4oCV#g|SW7M(NBpqTlxd4GPJh2V;+sv_r7vW+mwqy2u^G Date: Sat, 25 Oct 2014 00:01:04 +0200 Subject: [PATCH 147/313] - fixed some bad logic operations. --- src/c_cvars.cpp | 7 ++++--- src/p_interaction.cpp | 2 +- src/p_map.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 19187353f..ce4e88632 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -500,9 +500,10 @@ UCVarValue FBaseCVar::FromString (const char *value, ECVarType type) goodv = false; break; default: - if (value[i] < '0' && value[i] > '9' && - value[i] < 'A' && value[i] > 'F' && - value[i] < 'a' && value[i] > 'f') + if (value[i] < '0' || + (value[i] > '9' && value[i] < 'A') || + (value[i] > 'F' && value[i] < 'a') || + value[i] > 'f') { goodv = false; } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 6aa1a28f6..fad7a42e5 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1672,7 +1672,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->health -= damage; if (target->health <= 0) { // Death - if (player->cheats & CF_BUDDHA) + if (player->cheats & CF_BUDDHA && damage < TELEFRAG_DAMAGE) { // [SP] Save the player... player->health = target->health = 1; } diff --git a/src/p_map.cpp b/src/p_map.cpp index e7c477955..3a4187c9a 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -198,7 +198,7 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) if (ff_top > tmf.floorz) { - if (ff_top <= tmf.z || (!(flags && FFCF_3DRESTRICT) && (tmf.thing != NULL && ff_bottom < tmf.z && ff_top < tmf.z + tmf.thing->MaxStepHeight))) + if (ff_top <= tmf.z || (!(flags & FFCF_3DRESTRICT) && (tmf.thing != NULL && ff_bottom < tmf.z && ff_top < tmf.z + tmf.thing->MaxStepHeight))) { tmf.dropoffz = tmf.floorz = ff_top; tmf.floorpic = *rover->top.texture; @@ -2157,7 +2157,7 @@ bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y) { // too big a step up return false; } - else if ((thing->flags & MF_MISSILE) && !(thing->flags6 && MF6_STEPMISSILE) && tm.floorz > newz) + else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz) { // [RH] Don't let normal missiles climb steps return false; } From b6f4862024d8c9b09145731892a11d9f39aa3548 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 00:05:52 +0200 Subject: [PATCH 148/313] - fixed: A_Warp didn't initialize oldx/y/z in all situations. --- src/thingdef/thingdef_codeptr.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index d17bbc75f..1fb3827db 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4348,12 +4348,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) ACTION_PARAM_INT(flags, 5); ACTION_PARAM_STATE(success_state, 6); - fixed_t - - oldx, - oldy, - oldz; - AActor *reference = COPY_AAPTR(self, destination_selector); if (!reference) @@ -4362,6 +4356,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) return; } + fixed_t oldx = self->x; + fixed_t oldy = self->y; + fixed_t oldz = self->z; + if (!(flags & WARPF_ABSOLUTEANGLE)) { angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; @@ -4371,20 +4369,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) if (!(flags & WARPF_ABSOLUTEOFFSET)) { angle_t fineangle = angle >> ANGLETOFINESHIFT; - oldx = xofs; + 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(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); } - oldx = self->x; - oldy = self->y; - oldz = self->z; - if (flags & WARPF_TOFLOOR) { // set correct xy From 5ac7e4fc386ed7850a18b9b9d2f1a748553ef062 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 00:30:36 +0200 Subject: [PATCH 149/313] - added HARMFRIENDS flag. --- src/actor.h | 1 + src/p_map.cpp | 31 +++++++++++++++++-------------- src/thingdef/thingdef_data.cpp | 1 + 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/actor.h b/src/actor.h index 9a3c6844a..11d1a6429 100644 --- a/src/actor.h +++ b/src/actor.h @@ -341,6 +341,7 @@ enum MF7_ALWAYSTELEFRAG = 0x00000004, // will unconditionally be telefragged when in the way. Overrides all other settings. MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag + MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. // --- mobj.renderflags --- diff --git a/src/p_map.cpp b/src/p_map.cpp index 3a4187c9a..43b16a506 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1111,24 +1111,27 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) // cases where they are clearly supposed to do that if (thing->IsFriend(tm.thing->target)) { - // Friends never harm each other - return false; + // Friends never harm each other, unless the shooter has the HARMFRIENDS set. + if (!(thing->flags7 & MF7_HARMFRIENDS)) return false; } - if (thing->TIDtoHate != 0 && thing->TIDtoHate == tm.thing->target->TIDtoHate) + else { - // [RH] Don't hurt monsters that hate the same thing as you do - return false; - } - if (thing->GetSpecies() == tm.thing->target->GetSpecies() && !(thing->flags6 & MF6_DOHARMSPECIES)) - { - // Don't hurt same species or any relative - - // but only if the target isn't one's hostile. - if (!thing->IsHostile(tm.thing->target)) + if (thing->TIDtoHate != 0 && thing->TIDtoHate == tm.thing->target->TIDtoHate) { - // Allow hurting monsters the shooter hates. - if (thing->tid == 0 || tm.thing->target->TIDtoHate != thing->tid) + // [RH] Don't hurt monsters that hate the same thing as you do + return false; + } + if (thing->GetSpecies() == tm.thing->target->GetSpecies() && !(thing->flags6 & MF6_DOHARMSPECIES)) + { + // Don't hurt same species or any relative - + // but only if the target isn't one's hostile. + if (!thing->IsHostile(tm.thing->target)) { - return false; + // Allow hurting monsters the shooter hates. + if (thing->tid == 0 || tm.thing->target->TIDtoHate != thing->tid) + { + return false; + } } } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 5c52c45e3..b8ee13fd5 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -241,6 +241,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, NOTELESTOMP, AActor, flags7), DEFINE_FLAG(MF7, ALWAYSTELEFRAG, AActor, flags7), DEFINE_FLAG(MF7, WEAPONSPAWN, AActor, flags7), + DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From 83be901ad6f3254b1cd470ec1bf5faa7e60a36ea Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 00:52:27 +0200 Subject: [PATCH 150/313] - fixed: damage of amount TELEFRAG_DAMAGE should not be reduced by armors absorbing some damage, it'd lose its magic-ness, that is needed to ensure a guaranteed kill. The same applies to autousing health items. --- src/p_interaction.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index fad7a42e5..4a960cc17 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1218,8 +1218,13 @@ 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); - damage = newdam; + 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. + damage = newdam; + } + if (damage <= 0) { // If MF6_FORCEPAIN is set, make the player enter the pain state. @@ -1232,7 +1237,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (damage >= player->health + if (damage >= player->health && damage < TELEFRAG_DAMAGE && && (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch) && !player->morphTics) { // Try to use some inventory health From aa338a4dc674128db4899942bc92e03539f8f53f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 01:00:40 +0200 Subject: [PATCH 151/313] - fixed: G_FinishTravel must synchronize the CF_FLY flag with MF2_FLY. --- src/g_level.cpp | 9 +++++++++ src/p_interaction.cpp | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index d84a48776..525f67318 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1183,6 +1183,15 @@ void G_FinishTravel () pawn->AddToHash (); pawn->SetState(pawn->SpawnState); pawn->player->SendPitchLimits(); + // Sync the FLY flags. + if (pawn->flags2 & MF2_FLY) + { + pawn->player->cheats |= CF_FLY; + } + else + { + pawn->player->cheats &= ~CF_FLY; + } for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory) { diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 4a960cc17..5e1fbd49f 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1237,7 +1237,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (damage >= player->health && damage < TELEFRAG_DAMAGE && + if (damage >= player->health && damage < TELEFRAG_DAMAGE && (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch) && !player->morphTics) { // Try to use some inventory health From 5513966165ce6b773558fecfddf6d05dd42b4dce Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 01:15:39 +0200 Subject: [PATCH 152/313] - fixed: Strife's peasants couldn't harm the player because they were defined as friendly. --- wadsrc/static/actors/strife/peasants.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/actors/strife/peasants.txt b/wadsrc/static/actors/strife/peasants.txt index e7bbdadcf..af12b75df 100644 --- a/wadsrc/static/actors/strife/peasants.txt +++ b/wadsrc/static/actors/strife/peasants.txt @@ -9,7 +9,7 @@ ACTOR Peasant : StrifeHumanoid Radius 20 Height 56 Monster - +FRIENDLY + +NEVERTARGET -COUNTKILL +NOSPLASHALERT +FLOORCLIP From 1c500cead6c06d56416f27db414f5f247869c54b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 01:27:34 +0200 Subject: [PATCH 153/313] - fixed: A_Respawn did not restore the flags6 and flags7 fields. --- src/thingdef/thingdef_codeptr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 1fb3827db..2b382f989 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2912,6 +2912,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) 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; From b9800693679a860477713ef06880edd8ee4d0d09 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 24 Oct 2014 19:31:28 -0500 Subject: [PATCH 154/313] - Added GOD2 and BUDDHA2 cheats. - Ensures that not even telefrag damage can kill the player. - Fixed: Players with NODAMAGE could still hurt and kill themselves via voodoo dolls. --- src/c_cmds.cpp | 18 ++++++++++++++++++ src/d_player.h | 2 ++ src/d_protocol.h | 4 +++- src/m_cheat.cpp | 17 +++++++++++++++++ src/p_interaction.cpp | 15 ++++++++------- wadsrc/static/language.enu | 4 ++++ 6 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 89005dbc7..3dd97434e 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -124,6 +124,15 @@ CCMD (god) Net_WriteByte (CHT_GOD); } +CCMD(god2) +{ + if (CheckCheatmode()) + return; + + Net_WriteByte(DEM_GENERICCHEAT); + Net_WriteByte(CHT_GOD2); +} + CCMD (iddqd) { if (CheckCheatmode ()) @@ -142,6 +151,15 @@ CCMD (buddha) Net_WriteByte(CHT_BUDDHA); } +CCMD(buddha2) +{ + if (CheckCheatmode()) + return; + + Net_WriteByte(DEM_GENERICCHEAT); + Net_WriteByte(CHT_BUDDHA2); +} + CCMD (notarget) { if (CheckCheatmode ()) diff --git a/src/d_player.h b/src/d_player.h index ceb1ff894..e5644e8cb 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -205,6 +205,8 @@ typedef enum CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact + CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either. + CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either. CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip } cheat_t; diff --git a/src/d_protocol.h b/src/d_protocol.h index 0d11c7760..da54d3f2b 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -219,7 +219,9 @@ enum ECheatCommand CHT_GIMMIEJ, CHT_GIMMIEZ, CHT_BUDDHA, - CHT_NOCLIP2 + CHT_NOCLIP2, + CHT_BUDDHA2, + CHT_GOD2 }; void StartChunk (int id, BYTE **stream); diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 5d9d48bea..ac4eb140d 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -99,6 +99,23 @@ void cht_DoCheat (player_t *player, int cheat) msg = GStrings("TXT_BUDDHAOFF"); break; + case CHT_GOD2: + player->cheats ^= CF_GODMODE2; + if (player->cheats & CF_GODMODE2) + msg = GStrings("STSTR_DQD2ON"); + else + msg = GStrings("STSTR_DQD2OFF"); + ST_SetNeedRefresh(); + break; + + case CHT_BUDDHA2: + player->cheats ^= CF_BUDDHA2; + if (player->cheats & CF_BUDDHA2) + msg = GStrings("TXT_BUDDHA2ON"); + else + msg = GStrings("TXT_BUDDHA2OFF"); + break; + case CHT_NOCLIP: player->cheats ^= CF_NOCLIP; if (player->cheats & CF_NOCLIP) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 5e1fbd49f..5ce20f598 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -938,7 +938,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FState * woundstate = NULL; PainChanceList * pc = NULL; bool justhit = false; - + if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE))) { // Shouldn't happen return -1; @@ -1209,8 +1209,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_FORCED)) { // check the real player, not a voodoo doll here for invulnerability effects - if (damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || - (player->cheats & CF_GODMODE))) + if ((damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || + (player->cheats & CF_GODMODE))) || + (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) + //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him return -1; } @@ -1259,9 +1261,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // This does not save the player if damage >= TELEFRAG_DAMAGE, still need to // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) - if ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE - // Ignore players that are already dead. - && player->playerstate != PST_DEAD) + // Ignore players that are already dead. + if ((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE) && player->playerstate != PST_DEAD) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1677,7 +1678,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->health -= damage; if (target->health <= 0) { // Death - if (player->cheats & CF_BUDDHA && damage < TELEFRAG_DAMAGE) + if ((player->cheats & CF_BUDDHA && damage < TELEFRAG_DAMAGE) || (player->cheats & CF_BUDDHA2)) { // [SP] Save the player... player->health = target->health = 1; } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 17b56f6f3..a01a03559 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -292,6 +292,8 @@ STSTR_MUS = "Music Change"; STSTR_NOMUS = "IMPOSSIBLE SELECTION"; STSTR_DQDON = "Degreelessness Mode ON"; STSTR_DQDOFF = "Degreelessness Mode OFF"; +STSTR_DQD2ON = "Ultimate Degreelessness Mode ON"; +STSTR_DQD2OFF = "Ultimate Degreelessness Mode OFF"; STSTR_KFAADDED = "Very Happy Ammo Added"; STSTR_FAADDED = "Ammo (no keys) Added"; STSTR_NCON = "No Clipping Mode ON"; @@ -303,6 +305,8 @@ STSTR_CHOPPERS = "... doesn't suck - GM"; STSTR_CLEV = "Changing Level...\n"; TXT_BUDDHAON = "Buddha mode ON"; TXT_BUDDHAOFF = "Buddha mode OFF"; +TXT_BUDDHA2ON = "Ultimate Buddha Mode ON"; +TXT_BUDDHA2OFF = "Ultimate Buddha Mode OFF"; TXT_DEFAULTPICKUPMSG = "You got a pickup"; E1TEXT = From d1dc6fd59aa6ff64fc3b39924b173f3ac7f55172 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 24 Oct 2014 23:09:39 -0500 Subject: [PATCH 155/313] - Added: BUDDHA and FOILBUDDHA. - Fixed: mugshot didn't take the god2 cheat into account. --- src/actor.h | 2 ++ src/g_shared/sbar_mugshot.cpp | 2 +- src/p_interaction.cpp | 63 +++++++++++++++++++--------------- src/thingdef/thingdef_data.cpp | 2 ++ 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/actor.h b/src/actor.h index 11d1a6429..f4d1604e5 100644 --- a/src/actor.h +++ b/src/actor.h @@ -342,6 +342,8 @@ enum MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. + MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. + MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. // --- mobj.renderflags --- diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index 96dd07265..b8f419ef0 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -444,7 +444,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) if (bNormal) { bool good; - if ((player->cheats & CF_GODMODE) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE)) + if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE)) { good = SetState((stateflags & ANIMATEDGODMODE) ? "godanimated" : "god"); } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 5ce20f598..252e8e849 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1322,43 +1322,52 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) - { // Death - target->special1 = damage; - - // use inflictor's death type if it got one. - if (inflictor && inflictor->DeathType != NAME_None) mod = inflictor->DeathType; - - // check for special fire damage or ice damage deaths - if (mod == NAME_Fire) + { + if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && (!(inflictor->flags3 & MF7_FOILBUDDHA) && !(flags & DMG_FOILBUDDHA))) + { //Make sure FOILINVUL flags work here too for monsters. Or perhaps consider a FOILBUDDHA flag... + target->health = 1; + } + else { - if (player && !player->morphTics) - { // Check for flame death - if (!inflictor || - ((target->health > -50) && (damage > 25)) || - !(inflictor->flags5 & MF5_SPECIALFIREDAMAGE)) + + // Death + target->special1 = damage; + + // use inflictor's death type if it got one. + if (inflictor && inflictor->DeathType != NAME_None) mod = inflictor->DeathType; + + // check for special fire damage or ice damage deaths + if (mod == NAME_Fire) + { + if (player && !player->morphTics) + { // Check for flame death + if (!inflictor || + ((target->health > -50) && (damage > 25)) || + !(inflictor->flags5 & MF5_SPECIALFIREDAMAGE)) + { + target->DamageType = NAME_Fire; + } + } + else { target->DamageType = NAME_Fire; } } else { - target->DamageType = NAME_Fire; + target->DamageType = mod; } - } - else - { - target->DamageType = mod; - } - if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER)) - { // Minotaur's kills go to his master - // Make sure still alive and not a pointer to fighter head - if (source->tracer->player && (source->tracer->player->mo == source->tracer)) - { - source = source->tracer; + if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER)) + { // Minotaur's kills go to his master + // Make sure still alive and not a pointer to fighter head + if (source->tracer->player && (source->tracer->player->mo == source->tracer)) + { + source = source->tracer; + } } + target->Die (source, inflictor, flags); + return damage; } - target->Die (source, inflictor, flags); - return damage; } woundstate = target->FindState(NAME_Wound, mod); diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index b8ee13fd5..90c3313a6 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -242,6 +242,8 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, ALWAYSTELEFRAG, AActor, flags7), DEFINE_FLAG(MF7, WEAPONSPAWN, AActor, flags7), DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7), + DEFINE_FLAG(MF7, BUDDHA, AActor, flags7), + DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From a19620968d92123eac5788492b57fa76fbfb7e0a Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 24 Oct 2014 23:14:07 -0500 Subject: [PATCH 156/313] - Follow-up of the previous commit. - Cleaned up the DoDamage and DoKill functionality. - Added DMSS_FOILBUDDHA and KILS_FOILBUDDHA. --- src/p_local.h | 1 + src/thingdef/thingdef_codeptr.cpp | 69 ++++++++++++------------------ wadsrc/static/actors/constants.txt | 2 + 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index 2dc8c773e..f7e473845 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -559,6 +559,7 @@ enum EDmgFlags DMG_NO_FACTOR = 16, DMG_PLAYERATTACK = 32, DMG_FOILINVUL = 64, + DMG_FOILBUDDHA = 128, }; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2b382f989..6a1cf6e67 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4831,43 +4831,26 @@ enum DMSS DMSS_AFFECTARMOR = 2, DMSS_KILL = 4, DMSS_NOFACTOR = 8, + DMSS_FOILBUDDHA = 16, }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - if (flags & DMSS_NOFACTOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_FACTOR); - } - else - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); - } - } - else - { - if (flags & DMSS_NOFACTOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR | DMG_NO_FACTOR); - } - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - else - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - } + 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 (amount > 0) + P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. + else if (amount < 0) { amount = -amount; @@ -4991,30 +4974,32 @@ enum KILS KILS_FOILINVUL = 1 << 0, KILS_KILLMISSILES = 1 << 1, KILS_NOMONSTERS = 1 << 2, + KILS_FOILBUDDHA = 1 << 3, }; static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) { + 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->flags5 & MF5_NODAMAGE)) + 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_FOILINVUL) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index da289dc50..f806f8224 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -375,12 +375,14 @@ enum const int KILS_FOILINVUL = 1; const int KILS_KILLMISSILES = 2; const int KILS_NOMONSTERS = 4; +const int KILS_FOILBUDDHA = 8; // 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; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From 313245dd77e7bd56cda09da105302e1ce51ded81 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 09:53:23 +0200 Subject: [PATCH 157/313] - added a Buddha powerup --- src/g_shared/a_artifacts.cpp | 36 +++++++++++++++++++++++ src/g_shared/a_artifacts.h | 8 +++++ wadsrc/static/actors/shared/inventory.txt | 5 ++++ 3 files changed, 49 insertions(+) diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 7fd15fa21..1fa3ca985 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -1396,6 +1396,42 @@ void APowerFrightener::EndEffect () Owner->player->cheats &= ~CF_FRIGHTENING; } +// Buddha Powerup -------------------------------- + +IMPLEMENT_CLASS (APowerBuddha) + +//=========================================================================== +// +// APowerBuddha :: InitEffect +// +//=========================================================================== + +void APowerBuddha::InitEffect () +{ + Super::InitEffect(); + + if (Owner== NULL || Owner->player == NULL) + return; + + Owner->player->cheats |= CF_FRIGHTENING; +} + +//=========================================================================== +// +// APowerBuddha :: EndEffect +// +//=========================================================================== + +void APowerBuddha::EndEffect () +{ + Super::EndEffect(); + + if (Owner== NULL || Owner->player == NULL) + return; + + Owner->player->cheats &= ~CF_FRIGHTENING; +} + // Scanner powerup ---------------------------------------------------------- IMPLEMENT_CLASS (APowerScanner) diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index a216cf74c..4e1823f5a 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -184,6 +184,14 @@ protected: void EndEffect (); }; +class APowerBuddha : public APowerup +{ + DECLARE_CLASS (APowerBuddha, APowerup) +protected: + void InitEffect (); + void EndEffect (); +}; + class APowerTimeFreezer : public APowerup { DECLARE_CLASS( APowerTimeFreezer, APowerup ) diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index e293282b8..e9f57a607 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -271,6 +271,11 @@ ACTOR PowerFrightener : Powerup native Powerup.Duration -60 } +ACTOR PowerBuddha : Powerup native +{ + Powerup.Duration -60 +} + ACTOR PowerScanner : Powerup native { Powerup.Duration -80 From 8edace83c5b089530f6c34814ba08dbb2e748feb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 10:19:14 +0200 Subject: [PATCH 158/313] - made CheatKey and EasyKey vector graphics configurable through MAPINFO. --- src/am_map.cpp | 4 ++-- src/gi.cpp | 3 +++ src/gi.h | 1 + wadsrc/static/mapinfo/common.txt | 5 +++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 904593dfb..29cd60793 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -908,8 +908,8 @@ void AM_StaticInit() if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); - AM_ParseArrow(CheatKey, "maparrows/key.txt"); - AM_ParseArrow(EasyKey, "maparrows/ravenkey.txt"); + AM_ParseArrow(CheatKey, gameinfo.mCheatKey); + AM_ParseArrow(EasyKey, gameinfo.mEasyKey); if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); char namebuf[9]; diff --git a/src/gi.cpp b/src/gi.cpp index 4e64dd425..a17e97631 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -290,6 +290,9 @@ void FMapInfoParser::ParseGameInfo() else gameinfo.mCheatMapArrow = ""; } // Insert valid keys here. + GAMEINFOKEY_STRING(mCheatKey, "cheatKey") + GAMEINFOKEY_STRING(mEasyKey, "easyKey") + GAMEINFOKEY_STRING(TitlePage, "titlePage") GAMEINFOKEY_STRING(TitlePage, "titlePage") GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false) GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true) diff --git a/src/gi.h b/src/gi.h index bbfbe73ff..d8d19a14b 100644 --- a/src/gi.h +++ b/src/gi.h @@ -169,6 +169,7 @@ struct gameinfo_t int TextScreenY; FName DefaultEndSequence; FString mMapArrow, mCheatMapArrow; + FString mEasyKey, mCheatKey; FGIFont mStatscreenMapNameFont; FGIFont mStatscreenFinishedFont; FGIFont mStatscreenEnteringFont; diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index 8e7886987..fc6379d01 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -1,3 +1,8 @@ +Gameinfo +{ + CheatKey = "maparrows/key.txt" + EasyKey = "maparrows/ravenkey.txt" +} Intermission Inter_Titlescreen { From 0d3908a4f38bd9618fa5cbc150808daea0f423c2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 10:44:00 +0200 Subject: [PATCH 159/313] - fixed: Hitscan traces crossing a two sided line right at the height of the back sector's floor or ceiling must not be considered a hit. --- src/p_trace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_trace.cpp b/src/p_trace.cpp index d1b9f63ac..26852269a 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -366,7 +366,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } else if (entersector == NULL || - hitz <= bf || hitz >= bc || + hitz < bf || hitz > bc || in->d.line->flags & WallMask) { // hit the wall Results->HitType = TRACE_HitWall; From cfe97b0f0f596d19bdaea88e4571ee95f25967fc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 11:33:28 +0200 Subject: [PATCH 160/313] - Fixed: Killing a morphed monster must trigger A_BossDeath on its unmorphed self. --- src/g_shared/a_morph.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 2b3a6040d..6014d6fee 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -12,6 +12,7 @@ #include "doomstat.h" #include "g_level.h" #include "farchive.h" +#include "p_enemy.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -527,11 +528,11 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) { AMorphedMonster *fakeme = static_cast(actor); + AActor *realme = fakeme->UnmorphedMe; if ((fakeme->UnmorphTime) && (fakeme->MorphStyle & MORPH_UNDOBYDEATH) && - (fakeme->UnmorphedMe)) + (realme)) { - AActor *realme = fakeme->UnmorphedMe; int realstyle = fakeme->MorphStyle; int realhealth = fakeme->health; if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) @@ -542,6 +543,11 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor return true; } } + 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 fc40e9723a2b907e083783705a7b17106773c905 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 11:51:29 +0200 Subject: [PATCH 161/313] - fixed: CHANGELEVEL_RESETINVENTORY may not change the player's health. APlayerPawn::GiveDefaultInventory altered the player_t's health value which was ok as long as this function was only called from G_PlayerReborn. For all other cases this initialization was wrong and G_PlayerReborn should do this itself. --- src/g_game.cpp | 3 +++ src/m_cheat.cpp | 1 - src/p_user.cpp | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index a58753b86..8419fc017 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1363,6 +1363,7 @@ void G_PlayerReborn (int player) ::new(p) player_t; memcpy (p->frags, frags, sizeof(p->frags)); + p->health = actor->health; p->fragcount = fragcount; p->killcount = killcount; p->itemcount = itemcount; @@ -1381,6 +1382,8 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { + + // [GRB] Give inventory specified in DECORATE actor->GiveDefaultInventory (); p->ReadyWeapon = p->PendingWeapon; } diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index ac4eb140d..e48b79980 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -340,7 +340,6 @@ void cht_DoCheat (player_t *player, int cheat) player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players)); } player->mo->DamageType = NAME_None; -// player->mo->GiveDefaultInventory(); if (player->ReadyWeapon != NULL) { P_SetPsprite(player, ps_weapon, player->ReadyWeapon->GetUpState()); diff --git a/src/p_user.cpp b/src/p_user.cpp index 51105cd3d..045e61969 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1203,9 +1203,6 @@ void APlayerPawn::GiveDefaultInventory () { if (player == NULL) return; - // [GRB] Give inventory specified in DECORATE - player->health = GetDefault ()->health; - // HexenArmor must always be the first item in the inventory because // it provides player class based protection that should not affect // any other protection item. From ef5707d73ba453968f61ac10f2bb31f80152697b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 12:12:06 +0200 Subject: [PATCH 162/313] - added a compatibility option for stopping sounds when the owning actor is destroyed. --- src/compatibility.cpp | 1 + src/d_main.cpp | 1 + src/doomdef.h | 1 + src/g_mapinfo.cpp | 1 + src/s_sound.cpp | 2 +- wadsrc/static/menudef.txt | 1 + 6 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 351c2672a..7bd28e5d9 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -141,6 +141,7 @@ static FCompatOption Options[] = { "maskedmidtex", COMPATF_MASKEDMIDTEX, SLOT_COMPAT }, { "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 }, { "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 }, + { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/d_main.cpp b/src/d_main.cpp index 255994fb5..79c899085 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -620,6 +620,7 @@ CVAR (Flag, compat_polyobj, compatflags, COMPATF_POLYOBJ); CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX); CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES); CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); +CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); //========================================================================== // diff --git a/src/doomdef.h b/src/doomdef.h index 3e00975cf..767f97661 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -339,6 +339,7 @@ enum COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW. COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom. + COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 5a451dfaa..b68408237 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1306,6 +1306,7 @@ MapFlagHandlers[] = { "compat_maskedmidtex", MITYPE_COMPATFLAG, COMPATF_MASKEDMIDTEX, 0 }, { "compat_badangles", MITYPE_COMPATFLAG, 0, COMPATF2_BADANGLES }, { "compat_floormove", MITYPE_COMPATFLAG, 0, COMPATF2_FLOORMOVE }, + { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 035b71c74..75bd33d47 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1575,7 +1575,7 @@ void S_RelinkSound (AActor *from, AActor *to) { chan->Actor = to; } - else if (!(chan->ChanFlags & CHAN_LOOP)) + else if (!(chan->ChanFlags & CHAN_LOOP) && !(compatflags2 & COMPATF2_SOUNDCUTOFF)) { chan->Actor = NULL; chan->SourceType = SOURCE_Unattached; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 13565da7f..3d5a3ea3c 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1300,6 +1300,7 @@ OptionMenu "CompatibilityOptions" Option "Ignore Y offsets on masked midtextures", "compat_MASKEDMIDTEX", "YesNo" Option "Cannot travel straight NSEW", "compat_badangles", "YesNo" Option "Use Doom's floor motion behavior", "compat_floormove", "YesNo" + Option "Sounds stop when actor vanishes", "compat_soundcutoff", "YesNo" Class "CompatibilityMenu" } From dcc318fdee62eec607978a8864c780ec4575ab62 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 12:33:08 +0200 Subject: [PATCH 163/313] - print a warning when a state label can't be found. Preferably this would be an error but the oversight had been in the engine for too long so nobody knows --- src/p_states.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_states.cpp b/src/p_states.cpp index cfe455716..3ba0ae14f 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -39,6 +39,7 @@ #include "cmdlib.h" #include "i_system.h" #include "c_dispatch.h" +#include "v_text.h" #include "thingdef/thingdef.h" // Each state is owned by an actor. Actors can own any number of @@ -699,6 +700,10 @@ FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, const PClass *mytype { I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars()); } + else + { + Printf (TEXTCOLOR_RED "Attempt to get invalid state %s from actor %s.\n", label, type->TypeName.GetChars()); + } delete[] namestart; // free the allocated string buffer return state; } From edd53f22a1d67fcc3d7bf2114543e852bcebc540 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 12:34:46 +0200 Subject: [PATCH 164/313] - fixed: The buddha powerup set the wrong flag. --- src/g_shared/a_artifacts.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 1fa3ca985..4a2d066cd 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -1413,7 +1413,7 @@ void APowerBuddha::InitEffect () if (Owner== NULL || Owner->player == NULL) return; - Owner->player->cheats |= CF_FRIGHTENING; + Owner->player->cheats |= CF_BUDDHA; } //=========================================================================== @@ -1429,7 +1429,7 @@ void APowerBuddha::EndEffect () if (Owner== NULL || Owner->player == NULL) return; - Owner->player->cheats &= ~CF_FRIGHTENING; + Owner->player->cheats &= ~CF_BUDDHA; } // Scanner powerup ---------------------------------------------------------- From ad2e16576c078ead9c67219af691f4754d527969 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 13:10:38 +0200 Subject: [PATCH 165/313] - fixed_ P_ExplodeMissile skipped most of its logic when the missile got destroyed by having no death state. Apparently a band-aid fix was applied to this function because AActor::Destroy could not be called on an already destroyed actor again which could happen here due to incorrect ordering of actions. Moving the state change to a later point in this function - after hitting a sky has been checked and decals have been spawned - returns everything to a safe state and ensures that nothing gets skipped. --- src/p_mobj.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a67b96dfb..2b0782b8f 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1199,13 +1199,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); } if (nextstate == NULL) nextstate = mo->FindState(NAME_Death); - mo->SetState (nextstate); - if (mo->ObjectFlags & OF_EuthanizeMe) - { - return; - } - if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE)) { // [RH] Don't explode missiles on horizon lines. @@ -1280,8 +1274,17 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } } - if (nextstate != NULL) + // play the sound before changing the state, so that AActor::Destroy can call S_RelinkSounds on it and the death state can override it. + if (mo->DeathSound) { + S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1, + (mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM); + } + + mo->SetState (nextstate); + if (!(mo->ObjectFlags & OF_EuthanizeMe)) + { + // The rest only applies if the missile actor still exists. // [RH] Change render style of exploding rockets if (mo->flags5 & MF5_DEHEXPLOSION) { @@ -1314,11 +1317,6 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) mo->flags &= ~MF_MISSILE; - if (mo->DeathSound) - { - S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1, - (mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM); - } } } From 2e1fa70cbf45e3b3bd89c686265aa9753ce41e96 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 13:12:57 +0200 Subject: [PATCH 166/313] - some parentheses to silence GCC. --- src/p_interaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 252e8e849..2ac29e11c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1262,7 +1262,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. - if ((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE) && player->playerstate != PST_DEAD) + if (((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE)) && player->playerstate != PST_DEAD) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; From 9435cdc5c9d18100fa8a8a7cbe37cd705080220b Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 26 Oct 2014 01:09:19 +1300 Subject: [PATCH 167/313] Fixed NULL refrence in P_SpawnBlood --- src/p_mobj.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a67b96dfb..829e172cb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5018,10 +5018,11 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc cls = cls->ParentClass; } } + + statedone: + if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE; } -statedone: - if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE; if (bloodtype >= 1) P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor); } From d4c50b1662706b252ecbdceb95cf61a1d528d7f8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 14:59:30 +0200 Subject: [PATCH 168/313] - fixed: AActor::IsTeammate must consider monsters friendly to a specific player as members of the same team as the owning player. Such monsters cannot be made members of a designated team, though, because their association needs to change if the player changes teams. --- src/actor.h | 3 +++ src/p_enemy.cpp | 2 +- src/p_mobj.cpp | 35 +++++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/actor.h b/src/actor.h index f4d1604e5..f1c733ab2 100644 --- a/src/actor.h +++ b/src/actor.h @@ -716,6 +716,9 @@ public: // Transforms the actor into a finely-ground paste virtual bool Grind(bool items); + // Get this actor's team + int GetTeam(); + // Is the other actor on my team? bool IsTeammate (AActor *other); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 20b61200f..e51d601f3 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1592,7 +1592,7 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) } #endif // [SP] If you don't see any enemies in deathmatch, look for players (but only when friend to a specific player.) - if (actor->FriendPlayer == 0 && (!teamplay || actor->DesignatedTeam == TEAM_NONE)) return result; + if (actor->FriendPlayer == 0 && (!teamplay || actor->GetTeam() == TEAM_NONE)) return result; if (result || !deathmatch) return true; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 2b0782b8f..16d83943f 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5855,22 +5855,41 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, return NULL; } +int AActor::GetTeam() +{ + if (player) + { + return player->userinfo.GetTeam(); + } + + int myTeam = DesignatedTeam; + + // Check for monsters that belong to a player on the team but aren't part of the team themselves. + if (myTeam == TEAM_NONE && FriendPlayer != 0) + { + myTeam = players[FriendPlayer - 1].userinfo.GetTeam(); + } + return myTeam; + +} + bool AActor::IsTeammate (AActor *other) { if (!other) + { return false; + } else if (!deathmatch && player && other->player) - return true; - int myTeam = DesignatedTeam; - int otherTeam = other->DesignatedTeam; - if (player) - myTeam = player->userinfo.GetTeam(); - if (other->player) - otherTeam = other->player->userinfo.GetTeam(); - if (teamplay && myTeam != TEAM_NONE && myTeam == otherTeam) { return true; } + else if (teamplay) + { + int myTeam = GetTeam(); + int otherTeam = other->GetTeam(); + + return (myTeam != TEAM_NONE && myTeam == otherTeam); + } return false; } From 1b7c07df652b1142853706b7b296546f122a4907 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sat, 25 Oct 2014 14:58:10 +0100 Subject: [PATCH 169/313] - Fixed: Bots added by different setting controllers could be assigned the same player number. --- src/b_bot.cpp | 4 +- src/b_bot.h | 14 ++++- src/b_game.cpp | 159 ++++++++++++++++++++++++++++------------------- src/d_net.cpp | 2 +- src/d_protocol.h | 2 +- src/p_saveg.cpp | 2 +- 6 files changed, 111 insertions(+), 72 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 969d57a1b..eb2052a26 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -125,7 +125,7 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) bot = bot->next; if (bot) { - bot->inuse = false; + bot->inuse = BOTINUSE_No; bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE; } if (players[i].Bot != NULL) @@ -172,7 +172,7 @@ CCMD (listbots) while (thebot) { - Printf ("%s%s\n", thebot->name, thebot->inuse ? " (active)" : ""); + Printf ("%s%s\n", thebot->name, thebot->inuse == BOTINUSE_Yes ? " (active)" : ""); thebot = thebot->next; count++; } diff --git a/src/b_bot.h b/src/b_bot.h index 51fe64129..375b09abb 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -60,6 +60,13 @@ struct botskill_t FArchive &operator<< (FArchive &arc, botskill_t &skill); +enum +{ + BOTINUSE_No, + BOTINUSE_Waiting, + BOTINUSE_Yes, +}; + //Info about all bots in the bots.cfg //Updated during each level start. //Info given to bots when they're spawned. @@ -69,7 +76,7 @@ struct botinfo_t char *name; char *info; botskill_t skill; - bool inuse; + int inuse; int lastteam; }; @@ -88,7 +95,7 @@ public: bool SpawnBot (const char *name, int color = NOCOLOR); bool LoadBots (); void ForgetBots (); - void DoAddBot (BYTE **stream); + void TryAddBot (BYTE **stream, int player); void RemoveAllBots (bool fromlist); void DestroyAllBots (); @@ -122,6 +129,9 @@ public: bool m_Thinking; private: + //(B_Game.c) + bool DoAddBot (BYTE *info, botskill_t skill); + //(B_Func.c) bool Reachable (AActor *actor, AActor *target); void Dofire (AActor *actor, ticcmd_t *cmd); diff --git a/src/b_game.cpp b/src/b_game.cpp index 807ec2b46..f13c168db 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -89,8 +89,6 @@ enum BOTCFG_TEAM }; -static bool waitingforspawn[MAXPLAYERS]; - FCajunMaster::~FCajunMaster() { ForgetBots(); @@ -162,8 +160,6 @@ void FCajunMaster::Main (int buf) void FCajunMaster::Init () { - int i; - botnum = 0; firstthing = NULL; spawn_tries = 0; @@ -172,11 +168,6 @@ void FCajunMaster::Init () body1 = NULL; body2 = NULL; - for (i = 0; i < MAXPLAYERS; i++) - { - waitingforspawn[i] = false; - } - if (ctf && teamplay == false) teamplay = true; //Need teamplay for ctf. (which is not done yet) @@ -192,7 +183,7 @@ void FCajunMaster::Init () while (thebot != NULL) { - thebot->inuse = false; + thebot->inuse = BOTINUSE_No; thebot = thebot->next; } } @@ -232,12 +223,10 @@ void FCajunMaster::End () //The color parameter can be either a //color (range from 0-10), or = NOCOLOR. //The color parameter overides bots -//induvidual colors if not = NOCOLOR. +//individual colors if not = NOCOLOR. bool FCajunMaster::SpawnBot (const char *name, int color) { - int playernumber; - //COLORS static const char colors[11][17] = { @@ -254,36 +243,31 @@ bool FCajunMaster::SpawnBot (const char *name, int color) "\\color\\cf df 90" //10 = Bleached Bone }; - for (playernumber = 0; playernumber < MAXPLAYERS; playernumber++) - { - if (!playeringame[playernumber] && !waitingforspawn[playernumber]) - { - break; - } - } - - if (playernumber == MAXPLAYERS) - { - Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS); - return false; - } - botinfo_t *thebot; + int botshift; if (name) { thebot = botinfo; // Check if exist or already in the game. + botshift = 0; while (thebot && stricmp (name, thebot->name)) + { thebot = thebot->next; + botshift++; + } if (thebot == NULL) { Printf ("couldn't find %s in %s\n", name, BOTFILENAME); return false; } - else if (thebot->inuse) + else if (thebot->inuse == BOTINUSE_Waiting) + { + return false; + } + else if (thebot->inuse == BOTINUSE_Yes) { Printf ("%s is already in the thick\n", name); return false; @@ -296,9 +280,13 @@ bool FCajunMaster::SpawnBot (const char *name, int color) { int rnum = (pr_botspawn() % loaded_bots); thebot = botinfo; + botshift = 0; while (rnum) + { --rnum, thebot = thebot->next; - if (!thebot->inuse) + botshift++; + } + if (thebot->inuse == BOTINUSE_No) vacant = true; } } @@ -308,10 +296,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color) return false; } - waitingforspawn[playernumber] = true; + thebot->inuse = BOTINUSE_Waiting; Net_WriteByte (DEM_ADDBOT); - Net_WriteByte (playernumber); + Net_WriteByte (botshift); { //Set color. char concat[512]; @@ -332,61 +320,104 @@ bool FCajunMaster::SpawnBot (const char *name, int color) Net_WriteByte(thebot->skill.reaction); Net_WriteByte(thebot->skill.isp); - thebot->inuse = true; - - //Increment this. - botnum++; - return true; } -void FCajunMaster::DoAddBot (BYTE **stream) +void FCajunMaster::TryAddBot (BYTE **stream, int player) { - int bnum = ReadByte (stream); + int botshift = ReadByte (stream); char *info = ReadString (stream); - BYTE *infob = (BYTE *)info; botskill_t skill; skill.aiming = ReadByte (stream); skill.perfection = ReadByte (stream); skill.reaction = ReadByte (stream); skill.isp = ReadByte (stream); - D_ReadUserInfoStrings (bnum, &infob, false); + botinfo_t *thebot = NULL; + + if (consoleplayer == player) + { + thebot = botinfo; + + while (botshift > 0) + { + thebot = thebot->next; + botshift--; + } + } + + if (DoAddBot ((BYTE *)info, skill)) + { + if (consoleplayer == Net_Arbitrator) + { + //Increment this. + botnum++; + } + + if (thebot != NULL) + { + thebot->inuse = BOTINUSE_Yes; + } + } + else + { + if (thebot != NULL) + { + thebot->inuse = BOTINUSE_No; + } + } delete[] info; +} + +bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill) +{ + int bnum; + + for (bnum = 0; bnum < MAXPLAYERS; bnum++) + { + if (!playeringame[bnum]) + { + break; + } + } + + if (bnum == MAXPLAYERS) + { + Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS); + return false; + } + + D_ReadUserInfoStrings (bnum, &info, false); if (!deathmatch && playerstarts[bnum].type == 0) { Printf ("%s tried to join, but there was no player %d start\n", players[bnum].userinfo.GetName(), bnum+1); ClearPlayer (bnum, false); // Make the bot inactive again - if (botnum > 0) - { - botnum--; - } + return false; } + + multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). + players[bnum].Bot = new DBot; + GC::WriteBarrier (players[bnum].Bot); + players[bnum].Bot->skill = skill; + playeringame[bnum] = true; + players[bnum].mo = NULL; + players[bnum].playerstate = PST_ENTER; + + if (teamplay) + Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName()); else + Printf ("%s joined the game\n", players[bnum].userinfo.GetName()); + + G_DoReborn (bnum, true); + if (StatusBar != NULL) { - multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). - players[bnum].Bot = new DBot; - GC::WriteBarrier (players[bnum].Bot); - players[bnum].Bot->skill = skill; - playeringame[bnum] = true; - players[bnum].mo = NULL; - players[bnum].playerstate = PST_ENTER; - - if (teamplay) - Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName()); - else - Printf ("%s joined the game\n", players[bnum].userinfo.GetName()); - - G_DoReborn (bnum, true); - if (StatusBar != NULL) - { - StatusBar->MultiplayerChanged (); - } + StatusBar->MultiplayerChanged (); } - waitingforspawn[bnum] = false; + + return true; } void FCajunMaster::RemoveAllBots (bool fromlist) @@ -421,8 +452,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist) if (fromlist) { wanted_botnum = 0; - for (i = 0; i < MAXPLAYERS; i++) - waitingforspawn[i] = false; } botnum = 0; } diff --git a/src/d_net.cpp b/src/d_net.cpp index 89dc36d69..3d0d20b41 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2250,7 +2250,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) break; case DEM_ADDBOT: - bglobal.DoAddBot (stream); + bglobal.TryAddBot (stream, player); break; case DEM_KILLBOTS: diff --git a/src/d_protocol.h b/src/d_protocol.h index 0d11c7760..f75ee1b59 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -112,7 +112,7 @@ enum EDemoCommand DEM_DROPPLAYER, // 13 Not implemented, takes a byte DEM_CHANGEMAP, // 14 Name of map to change to DEM_SUICIDE, // 15 Player wants to die - DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp) + DEM_ADDBOT, // 16 Byte: botshift, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp) DEM_KILLBOTS, // 17 Remove all bots from the world DEM_INVUSEALL, // 18 Use every item (panic!) DEM_INVUSE, // 19 4 bytes: ID of item to use diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 5e9ecbdba..a677420f5 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -280,7 +280,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) } if (thebot) { - thebot->inuse = true; + thebot->inuse = BOTINUSE_Yes; } bglobal.botnum++; dst->userinfo.TransferFrom(uibackup2); From 82ac6c999ef0edd7d2123205252ac152b7d34a41 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 26 Oct 2014 04:14:52 +1300 Subject: [PATCH 170/313] Destroy old player mobjs when starting new games --- src/g_level.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/g_level.cpp b/src/g_level.cpp index 525f67318..f21a1dfe5 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -236,6 +236,18 @@ void G_NewInit () { int i; + // Destory all old player refrences that may still exist + TThinkerIterator it(STAT_TRAVELLING); + APlayerPawn *pawn, *next; + + next = it.Next(); + while ((pawn = next) != NULL) + { + next = it.Next(); + pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP; + pawn->Destroy(); + } + G_ClearSnapshots (); ST_SetNeedRefresh(); netgame = false; From b58595734ded02258c269bf4fc42677a74932a65 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 17:37:45 +0200 Subject: [PATCH 171/313] - removed accidental line duplication. --- src/gi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gi.cpp b/src/gi.cpp index a17e97631..b0edf2a04 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -293,7 +293,6 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRING(mCheatKey, "cheatKey") GAMEINFOKEY_STRING(mEasyKey, "easyKey") GAMEINFOKEY_STRING(TitlePage, "titlePage") - GAMEINFOKEY_STRING(TitlePage, "titlePage") GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false) GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true) GAMEINFOKEY_STRINGARRAY(PlayerClasses, "addplayerclasses", 0, false) From be4b253215be99ae0de90b7b53bce267db9a685f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2014 18:18:35 +0200 Subject: [PATCH 172/313] - use a separate sound for the net notches on the startup screen. --- src/win32/st_start.cpp | 2 +- wadsrc/static/sndinfo.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/win32/st_start.cpp b/src/win32/st_start.cpp index 04b52925e..af7f7ad98 100644 --- a/src/win32/st_start.cpp +++ b/src/win32/st_start.cpp @@ -767,7 +767,7 @@ void FHexenStartupScreen::NetProgress(int count) y = ST_NETPROGRESS_Y; ST_Util_DrawBlock (StartupBitmap, NetNotchBits, x, y, ST_NETNOTCH_WIDTH / 2, ST_NETNOTCH_HEIGHT); } - S_Sound (CHAN_BODY, "Drip", 1, ATTN_NONE); + S_Sound (CHAN_BODY, "misc/netnotch", 1, ATTN_NONE); I_GetEvent (); } } diff --git a/wadsrc/static/sndinfo.txt b/wadsrc/static/sndinfo.txt index 5508cd89b..be2ecd987 100644 --- a/wadsrc/static/sndinfo.txt +++ b/wadsrc/static/sndinfo.txt @@ -985,6 +985,7 @@ $alias menu/clear PlatformStop // Hexen does not have ripslop sound like Heretic misc/ripslop dsempty +misc/netnotch blddrp1 $alias intermission/cooptotal *death $alias intermission/nextstage DoorCloseLight From f802d7a44c172709daccef7364a237960c06d748 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 27 Oct 2014 21:35:55 -0500 Subject: [PATCH 173/313] - Added +FULLMASS. Actors will be excluded from damage/radius thrusting of all sorts by explosions or damage of any kind. They will also never deal impact damage to other enemies, nor will they damage themselves from being too close to a wall. --- src/actor.h | 1 + src/p_interaction.cpp | 3 ++- src/p_map.cpp | 40 +++++++++++++++++++--------------- src/thingdef/thingdef_data.cpp | 1 + 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/actor.h b/src/actor.h index f1c733ab2..6ae45f4b7 100644 --- a/src/actor.h +++ b/src/actor.h @@ -344,6 +344,7 @@ enum MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. + MF7_FULLMASS = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. // --- mobj.renderflags --- diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 2ac29e11c..0a615d3cc 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1097,6 +1097,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, && !(target->flags & MF_NOCLIP) && !(inflictor->flags2 & MF2_NODMGTHRUST) && !(flags & DMG_THRUSTLESS) + && !(target->flags7 & MF7_FULLMASS) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { int kickback; @@ -1324,7 +1325,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && (!(inflictor->flags3 & MF7_FOILBUDDHA) && !(flags & DMG_FOILBUDDHA))) - { //Make sure FOILINVUL flags work here too for monsters. Or perhaps consider a FOILBUDDHA flag... + { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; } else diff --git a/src/p_map.cpp b/src/p_map.cpp index 43b16a506..3c17115ff 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4679,7 +4679,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo if (!(flags & RADF_NODAMAGE)) newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); - else if (thing->player == NULL && !(flags & RADF_NOIMPACTDAMAGE)) + else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_FULLMASS))) thing->flags2 |= MF2_BLASTED; if (!(thing->flags & MF_ICECORPSE)) @@ -4691,25 +4691,29 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo { if (bombsource == NULL || !(bombsource->flags2 & MF2_NODMGTHRUST)) { - thrust = points * 0.5f / (double)thing->Mass; - if (bombsource == thing) + if (!(thing->flags7 & MF7_FULLMASS)) { - thrust *= selfthrustscale; + + thrust = points * 0.5f / (double)thing->Mass; + if (bombsource == thing) + { + thrust *= selfthrustscale; + } + velz = (double)(thing->z + (thing->height >> 1) - bombspot->z) * thrust; + if (bombsource != thing) + { + velz *= 0.5f; + } + else + { + velz *= 0.8f; + } + angle_t ang = R_PointToAngle2(bombspot->x, bombspot->y, thing->x, thing->y) >> ANGLETOFINESHIFT; + thing->velx += fixed_t(finecosine[ang] * thrust); + thing->vely += fixed_t(finesine[ang] * thrust); + if (!(flags & RADF_NODAMAGE)) + thing->velz += (fixed_t)velz; // this really doesn't work well } - velz = (double)(thing->z + (thing->height >> 1) - bombspot->z) * thrust; - if (bombsource != thing) - { - velz *= 0.5f; - } - else - { - velz *= 0.8f; - } - angle_t ang = R_PointToAngle2(bombspot->x, bombspot->y, thing->x, thing->y) >> ANGLETOFINESHIFT; - thing->velx += fixed_t(finecosine[ang] * thrust); - thing->vely += fixed_t(finesine[ang] * thrust); - if (!(flags & RADF_NODAMAGE)) - thing->velz += (fixed_t)velz; // this really doesn't work well } } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 90c3313a6..fe1cec588 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -244,6 +244,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7), DEFINE_FLAG(MF7, BUDDHA, AActor, flags7), DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), + DEFINE_FLAG(MF7, FULLMASS, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From c01d1a8003dcfb90abd3170545ec3891e5f67f79 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 27 Oct 2014 22:29:10 -0500 Subject: [PATCH 174/313] - Added DMSS_NOPROTECT. Bypasses PowerProtection inventory items. --- src/p_interaction.cpp | 10 +++++----- src/p_local.h | 1 + src/thingdef/thingdef_codeptr.cpp | 3 +++ wadsrc/static/actors/constants.txt | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 0a615d3cc..db6dcdc7d 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1053,8 +1053,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } } - // Handle passive damage modifiers (e.g. PowerProtection) - if (target->Inventory != NULL) + // Handle passive damage modifiers (e.g. PowerProtection), provided they are not afflicted with protection penetrating powers. + if ((target->Inventory != NULL) && !(flags & DMG_NO_PROTECT)) { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); @@ -1592,7 +1592,7 @@ bool AActor::OkayToSwitchTarget (AActor *other) bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison) { - if((player->cheats&CF_GODMODE) || (player->mo->flags2 & MF2_INVULNERABLE)) + if((player->cheats&CF_GODMODE) || (player->mo->flags2 & MF2_INVULNERABLE) || (player->cheats & CF_GODMODE2)) { return false; } @@ -1643,8 +1643,8 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, { return; } - if (damage < TELEFRAG_DAMAGE && ((target->flags2 & MF2_INVULNERABLE) || - (player->cheats & CF_GODMODE))) + if ((damage < TELEFRAG_DAMAGE && ((target->flags2 & MF2_INVULNERABLE) || + (player->cheats & CF_GODMODE))) || (player->cheats & CF_GODMODE2)) { // target is invulnerable return; } diff --git a/src/p_local.h b/src/p_local.h index f7e473845..dec80ecaa 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -560,6 +560,7 @@ enum EDmgFlags DMG_PLAYERATTACK = 32, DMG_FOILINVUL = 64, DMG_FOILBUDDHA = 128, + DMG_NO_PROTECT = 256, }; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6a1cf6e67..a8f0a4d40 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4832,6 +4832,7 @@ enum DMSS DMSS_KILL = 4, DMSS_NOFACTOR = 8, DMSS_FOILBUDDHA = 16, + DMSS_NOPROTECT = 32, }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) @@ -4847,6 +4848,8 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy 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. diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index f806f8224..278d4b90f 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -383,6 +383,7 @@ 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; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From 774db445ec815be0099e7b3fba08c4ffb1bc3eb0 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 27 Oct 2014 22:40:25 -0500 Subject: [PATCH 175/313] -Fixed: WhirlWind was still able to affect actors with the FULLMASS flag. --- src/g_heretic/a_ironlich.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp index d8c1fd285..d2e49a0f3 100644 --- a/src/g_heretic/a_ironlich.cpp +++ b/src/g_heretic/a_ironlich.cpp @@ -28,10 +28,14 @@ int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype) { int randVal; - target->angle += pr_foo.Random2() << 20; - target->velx += pr_foo.Random2() << 10; - target->vely += pr_foo.Random2() << 10; - if ((level.time & 16) && !(target->flags2 & MF2_BOSS)) + if (!(target->flags7 & MF7_FULLMASS)) + { + target->angle += pr_foo.Random2() << 20; + target->velx += pr_foo.Random2() << 10; + target->vely += pr_foo.Random2() << 10; + } + + if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_FULLMASS)) { randVal = pr_foo(); if (randVal > 160) From 6073adbeef6cb497084bad8371134648a18fe1a3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 28 Oct 2014 08:40:34 +0100 Subject: [PATCH 176/313] - renamed FULLMASS to DONTTHRUST. --- src/actor.h | 2 +- src/g_heretic/a_ironlich.cpp | 4 ++-- src/p_interaction.cpp | 2 +- src/p_map.cpp | 4 ++-- src/thingdef/thingdef_data.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/actor.h b/src/actor.h index 6ae45f4b7..6c9d53a4f 100644 --- a/src/actor.h +++ b/src/actor.h @@ -344,7 +344,7 @@ enum MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. - MF7_FULLMASS = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. + MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. // --- mobj.renderflags --- diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp index d2e49a0f3..dba16b622 100644 --- a/src/g_heretic/a_ironlich.cpp +++ b/src/g_heretic/a_ironlich.cpp @@ -28,14 +28,14 @@ int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype) { int randVal; - if (!(target->flags7 & MF7_FULLMASS)) + if (!(target->flags7 & MF7_DONTTHRUST)) { target->angle += pr_foo.Random2() << 20; target->velx += pr_foo.Random2() << 10; target->vely += pr_foo.Random2() << 10; } - if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_FULLMASS)) + if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_DONTTHRUST)) { randVal = pr_foo(); if (randVal > 160) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index db6dcdc7d..860e30d0f 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1097,7 +1097,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, && !(target->flags & MF_NOCLIP) && !(inflictor->flags2 & MF2_NODMGTHRUST) && !(flags & DMG_THRUSTLESS) - && !(target->flags7 & MF7_FULLMASS) + && !(target->flags7 & MF7_DONTTHRUST) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { int kickback; diff --git a/src/p_map.cpp b/src/p_map.cpp index 3c17115ff..b688ef43d 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4679,7 +4679,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo if (!(flags & RADF_NODAMAGE)) newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); - else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_FULLMASS))) + else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_DONTTHRUST))) thing->flags2 |= MF2_BLASTED; if (!(thing->flags & MF_ICECORPSE)) @@ -4691,7 +4691,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo { if (bombsource == NULL || !(bombsource->flags2 & MF2_NODMGTHRUST)) { - if (!(thing->flags7 & MF7_FULLMASS)) + if (!(thing->flags7 & MF7_DONTTHRUST)) { thrust = points * 0.5f / (double)thing->Mass; diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index fe1cec588..dbd1f1031 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -244,7 +244,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7), DEFINE_FLAG(MF7, BUDDHA, AActor, flags7), DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), - DEFINE_FLAG(MF7, FULLMASS, AActor, flags7), + DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From 65cb662678c29c0d66e817a0d77a18e516994cf8 Mon Sep 17 00:00:00 2001 From: Gaerzi Date: Tue, 28 Oct 2014 21:19:01 +0100 Subject: [PATCH 177/313] Missing break in case NAME_FillColor This caused weirdness with invulnerable monsters when their fillcolor was changed. --- src/p_udmf.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 6af777b36..926e65dcc 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -690,6 +690,7 @@ public: case NAME_FillColor: th->fillcolor = CheckInt(key); + break; case NAME_Health: th->health = CheckInt(key); From 978667143c4acc49e323cb68cad0483a0b665ad8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Oct 2014 08:54:14 +0100 Subject: [PATCH 178/313] - fixed: P_RemoveThing must not remove owned inventory items. --- src/p_things.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_things.cpp b/src/p_things.cpp index 2a93ee31e..a8d0404b6 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -402,10 +402,14 @@ void P_RemoveThing(AActor * actor) // Don't remove live players. if (actor->player == NULL || actor != actor->player->mo) { + // Don't also remove owned inventory items + if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast(actor)->Owner == NULL) return; + // be friendly to the level statistics. ;) actor->ClearCounters(); actor->Destroy (); } + } bool P_Thing_Raise(AActor *thing) From c1a0ee9623637984ad01c75521b80bc410d895d2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Oct 2014 10:40:08 +0100 Subject: [PATCH 179/313] - fixed last commit. --- src/p_things.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_things.cpp b/src/p_things.cpp index a8d0404b6..0189848e4 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -403,7 +403,7 @@ void P_RemoveThing(AActor * actor) if (actor->player == NULL || actor != actor->player->mo) { // Don't also remove owned inventory items - if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast(actor)->Owner == NULL) return; + if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast(actor)->Owner != NULL) return; // be friendly to the level statistics. ;) actor->ClearCounters(); From 5977cb04d9d24265b823168ccbb663e9ff9c65a2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 29 Oct 2014 12:33:25 -0500 Subject: [PATCH 180/313] - Fixed: A_Die didn't consider missiles for the function. --- src/p_enemy.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index e51d601f3..ef6136fdc 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3161,7 +3161,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Die) ACTION_PARAM_START(1); ACTION_PARAM_NAME(damagetype, 0); - P_DamageMobj (self, NULL, NULL, self->health, damagetype, DMG_FORCED); + if (self->flags & MF_MISSILE) + P_ExplodeMissile(self, NULL, NULL); + else + P_DamageMobj (self, NULL, NULL, self->health, damagetype, DMG_FORCED); } // From 165d2887fd8bdab5e4f388c5db54e99c83e27488 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 29 Oct 2014 14:01:31 -0500 Subject: [PATCH 181/313] - Added: A_GiveToChildren - Added: A_TakeFromChildren - Added: A_GiveToSiblings - Added: A_TakeFromSiblings - Added the following flags for A_RadiusGive: - RGF_NOSIGHT: Exclude sight check from distance. - RGF_MISSILES: Missiles can take inventory items. --- src/thingdef/thingdef_codeptr.cpp | 85 +++++++++++++++++++++++++----- wadsrc/static/actors/actor.txt | 4 ++ wadsrc/static/actors/constants.txt | 20 +++---- 3 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a8f0a4d40..6fafbddcf 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1677,6 +1677,31 @@ 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 @@ -1737,6 +1762,31 @@ 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 @@ -4608,17 +4658,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) //========================================================================== enum RadiusGiveFlags { - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_MASK = 63, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, + 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 = 63, + 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) @@ -4699,6 +4751,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) continue; } } + else if (thing->flags & MF_MISSILE) + { + if (!(flags & RGF_MISSILES)) + { + continue; + } + } else { continue; @@ -4724,8 +4783,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) } fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); - if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path + if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) || (RGF_NOSIGHT)) + { // 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))) { diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 280321ad0..e253f53ac 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -312,6 +312,10 @@ ACTOR Actor native //: Thinker 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_GiveToChildren(class itemtype, int amount = 0); + action native A_GiveToSiblings(class itemtype, int amount = 0); + action native A_TakeFromChildren(class itemtype, int amount = 0); + action native A_TakeFromSiblings(class itemtype, int amount = 0); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 278d4b90f..a43d90f96 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -191,15 +191,17 @@ const int WAF_USEPUFF = 2; enum { RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, + RGF_PLAYERS = 1 << 1, + RGF_MONSTERS = 1 << 2, + RGF_OBJECTS = 1 << 3, + RGF_VOODOO = 1 << 4, + RGF_CORPSES = 1 << 5, + RGF_NOTARGET = 1 << 6, + RGF_NOTRACER = 1 << 7, + RGF_NOMASTER = 1 << 8, + RGF_CUBE = 1 << 9, + RGF_NOSIGHT = 1 << 10, + RGF_MISSILES = 1 << 11, }; // Activation flags From 31611c17d572e2aab109099d047e157fd51682cc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Oct 2014 22:00:15 +0100 Subject: [PATCH 182/313] - fixed RGF_NOSIGHT checking for A_RadiusGive. --- 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 6fafbddcf..80583b587 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4783,7 +4783,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) } fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); - if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) || (RGF_NOSIGHT)) + 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))) From 2f11a59be0d743b04d7a7885624ad7925a8f545c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 31 Oct 2014 08:57:43 +0100 Subject: [PATCH 183/313] - fixed: UDMF ceiling plane properties set the map's floor plane values. --- src/p_udmf.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index b4700e856..541d7d396 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1541,11 +1541,11 @@ public: double ulen = TVector3(cp[0], cp[1], cp[2]).Length(); // normalize the vector, it must have a length of 1 - sec->floorplane.a = FLOAT2FIXED(cp[0] / ulen); - sec->floorplane.b = FLOAT2FIXED(cp[1] / ulen); - sec->floorplane.c = FLOAT2FIXED(cp[2] / ulen); - sec->floorplane.d = FLOAT2FIXED(cp[3] / ulen); - sec->floorplane.ic = FLOAT2FIXED(ulen / cp[2]); + sec->ceilingplane.a = FLOAT2FIXED(cp[0] / ulen); + sec->ceilingplane.b = FLOAT2FIXED(cp[1] / ulen); + sec->ceilingplane.c = FLOAT2FIXED(cp[2] / ulen); + sec->ceilingplane.d = FLOAT2FIXED(cp[3] / ulen); + sec->ceilingplane.ic = FLOAT2FIXED(ulen / cp[2]); } if (lightcolor == -1 && fadecolor == -1 && desaturation == -1) From c85105f5520f873f8c32bfddbf612260025b2ae4 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 31 Oct 2014 22:50:23 +1300 Subject: [PATCH 184/313] Added cl_showsecretmessage Controls if secret notifications are displayed (def. true) --- src/p_spec.cpp | 3 ++- wadsrc/static/menudef.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index d58062a7e..676191555 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -714,6 +714,7 @@ void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, //============================================================================ CVAR(Bool, showsecretsector, false, 0) +CVAR(Bool, cl_showsecretmessage, true, CVAR_ARCHIVE) void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum) { @@ -723,7 +724,7 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornu { actor->player->secretcount++; } - if (actor->CheckLocalView (consoleplayer)) + if (cl_showsecretmessage && actor->CheckLocalView(consoleplayer)) { if (printmessage) { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 3d5a3ea3c..408e534a8 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1098,6 +1098,7 @@ OptionMenu MessageOptions Title "MESSAGES" Option "Show messages", "show_messages", "OnOff" Option "Show obituaries", "show_obituaries", "OnOff" + Option "Show secret notifications", "cl_showsecretmessage", "OnOff" Option "Scale text in high res", "con_scaletext", "ScaleValues" Option "Minimum message level", "msg", "MessageLevels" Option "Center messages", "con_centernotify", "OnOff" From 0ff65bb43089fcae61c272b65a43987a82e11820 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 31 Oct 2014 21:08:13 +0100 Subject: [PATCH 185/313] - fixed: AActor::Massacre must return true only when it actually kills a monster. --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 465a4d745..a92eed8f8 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1167,7 +1167,7 @@ bool AActor::Massacre () P_DamageMobj (this, NULL, NULL, TELEFRAG_DAMAGE, NAME_Massacre); } while (health != prevhealth && health > 0); //abort if the actor wasn't hurt. - return true; + return health <= 0; } return false; } From 938b54ccb5f62f864edcc1edf26fcd42304ddb84 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 31 Oct 2014 15:41:23 -0500 Subject: [PATCH 186/313] - Added TF_FORCED for A_Teleport. Forces the actor to move to the spot. --- src/thingdef/thingdef_codeptr.cpp | 18 ++++++++++++++++-- wadsrc/static/actors/constants.txt | 4 +++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 80583b587..2732337c7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4054,6 +4054,7 @@ enum T_Flags { TF_TELEFRAG = 1, // Allow telefrag in order to teleport. TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) + TF_FORCED = 4, // Forget what's in the way. TF_Telefrag takes precedence though. }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) @@ -4103,7 +4104,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) fixed_t prevX = self->x; fixed_t prevY = self->y; fixed_t prevZ = self->z; - if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) + bool teleResult = false; + + //Take precedence and cooperate with telefragging first. + if (P_TeleportMove(self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) + teleResult = true; + + 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; + } + + if (teleResult) { ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! @@ -4509,7 +4523,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) self->PrevY = self->y + reference->PrevY - reference->y; self->PrevZ = self->z + reference->PrevZ - reference->z; } - else if (! (flags & WARPF_INTERPOLATE)) + else if (!(flags & WARPF_INTERPOLATE)) { self->PrevX = self->x; self->PrevY = self->y; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index a43d90f96..78741e059 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -181,7 +181,9 @@ const int FPF_AIMATANGLE = 1; const int FPF_TRANSFERTRANSLATION = 2; // Flags for A_Teleport -const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; +const int TF_TELEFRAG = 1; +const int TF_RANDOMDECIDE = 2; +const int TF_FORCED = 4; // Flags for A_WolfAttack const int WAF_NORANDOM = 1; From eceb37aa64d2286ab181768aefbab470de17f257 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sat, 1 Nov 2014 17:47:29 +1300 Subject: [PATCH 187/313] Added recordmap for recording demos from console recordmap Starts a new game from the specified map recording to the specified filename --- src/d_event.h | 1 + src/d_main.cpp | 1 + src/doomstat.h | 2 ++ src/g_game.cpp | 16 ++++++++++++++++ src/g_level.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+) diff --git a/src/d_event.h b/src/d_event.h index 5bd4b02e0..7a9b48eb8 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -61,6 +61,7 @@ typedef enum ga_loadlevel, ga_newgame, ga_newgame2, + ga_recordgame, ga_loadgame, ga_loadgamehidecon, ga_loadgameplaydemo, diff --git a/src/d_main.cpp b/src/d_main.cpp index 79c899085..e08b1539e 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1338,6 +1338,7 @@ CCMD (endgame) { gameaction = ga_fullconsole; demosequence = -1; + G_CheckDemoStatus(); } } diff --git a/src/doomstat.h b/src/doomstat.h index 92ab5b8f8..1559609f1 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -136,6 +136,8 @@ extern int consoleplayer; // Disable save/end game? extern bool usergame; +extern FString newdemoname; +extern FString newdemomap; extern bool demoplayback; extern bool demorecording; extern int demover; diff --git a/src/g_game.cpp b/src/g_game.cpp index 8419fc017..1ea1c6b68 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -162,6 +162,8 @@ int consoleplayer; // player taking events int gametic; CVAR(Bool, demo_compress, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +FString newdemoname; +FString newdemomap; FString demoname; bool demorecording; bool demoplayback; @@ -1048,6 +1050,10 @@ void G_Ticker () case ga_loadlevel: G_DoLoadLevel (-1, false); break; + case ga_recordgame: + G_CheckDemoStatus(); + G_RecordDemo(newdemoname); + G_BeginRecording(newdemomap); case ga_newgame2: // Silence GCC (see above) case ga_newgame: G_DoNewGame (); @@ -2434,6 +2440,16 @@ void G_DeferedPlayDemo (const char *name) CCMD (playdemo) { + if (netgame) + { + Printf("End your current netgame first!"); + return; + } + if (demorecording) + { + Printf("End your current demo first!"); + return; + } if (argv.argc() > 1) { G_DeferedPlayDemo (argv[1]); diff --git a/src/g_level.cpp b/src/g_level.cpp index f21a1dfe5..2a2d6471a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -199,6 +199,46 @@ CCMD (map) // //========================================================================== +CCMD(recordmap) +{ + if (netgame) + { + Printf("You cannot record a new game while in a netgame."); + return; + } + if (argv.argc() > 2) + { + try + { + if (!P_CheckMapData(argv[2])) + { + Printf("No map %s\n", argv[2]); + } + else + { + G_DeferedInitNew(argv[2]); + gameaction = ga_recordgame; + newdemoname = argv[1]; + newdemomap = argv[2]; + } + } + catch (CRecoverableError &error) + { + if (error.GetMessage()) + Printf("%s", error.GetMessage()); + } + } + else + { + Printf("Usage: recordmap \n"); + } +} + +//========================================================================== +// +// +//========================================================================== + CCMD (open) { if (netgame) From 2e085b23188b3472f1595340fe04eb85bf19f459 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 1 Nov 2014 00:00:29 -0500 Subject: [PATCH 188/313] - Added ALLOWPAIN flag. Monsters with this flag can enter pain states, regardless of invulnerability or damage absorption. - Fixed: god2 cheat wasn't being considered for drowning and thrusting. --- src/actor.h | 1 + src/p_interaction.cpp | 88 +++++++++++++++++++++++++++------- src/p_mobj.cpp | 2 +- src/p_user.cpp | 3 +- src/thingdef/thingdef_data.cpp | 1 + 5 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/actor.h b/src/actor.h index 6c9d53a4f..22f4cb776 100644 --- a/src/actor.h +++ b/src/actor.h @@ -345,6 +345,7 @@ enum MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. + MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. // --- mobj.renderflags --- diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 860e30d0f..f318ed3b3 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -938,6 +938,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FState * woundstate = NULL; PainChanceList * pc = NULL; bool justhit = false; + bool plrDontThrust = false; + bool invulpain = false; + int fakeDamage = 0; + int holdDamage = 0; if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE))) { // Shouldn't happen @@ -972,7 +976,14 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { - return -1; + if (target->flags7 & MF7_ALLOWPAIN) + { + invulpain = true; //This returns -1 later. + fakeDamage = damage; + goto fakepain; //The label is above the massive pile of checks. + } + else + return -1; } } else @@ -980,11 +991,21 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Players are optionally excluded from getting thrust by damage. if (static_cast(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { - return -1; + if (target->flags7 & MF7_ALLOWPAIN) + plrDontThrust = 1; + else + return -1; } } } + if ((target->flags7 & MF7_ALLOWPAIN) && (damage < TELEFRAG_DAMAGE)) + { + //Intentionally do not jump to fakepain because the damage hasn't been dished out yet. + //Once it's dished out, THEN we can disregard damage factors affecting pain chances. + fakeDamage = damage; + } + if (inflictor != NULL) { if (inflictor->flags5 & MF5_PIERCEARMOR) @@ -1010,6 +1031,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Invulnerable, and won't wake up return -1; } + player = target->player; if (player && damage > 1 && damage < TELEFRAG_DAMAGE) { @@ -1032,10 +1054,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } } + if (damage > 0) + damage = inflictor->DoSpecialDamage (target, damage, mod); - damage = inflictor->DoSpecialDamage (target, damage, mod); if (damage == -1) { + if (target->flags7 & MF7_ALLOWPAIN) //Hold off ending the function before we can deal the pain chances. + goto fakepain; return -1; } } @@ -1058,12 +1083,15 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); - if (olddam != damage && damage <= 0) - { // Still allow FORCEPAIN + if (((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0)) + { // Still allow FORCEPAIN and make sure we're still passing along fake damage to hit enemies for their pain states. if (MustForcePain(target, inflictor)) { goto dopain; } + else if (target->flags7 & MF7_ALLOWPAIN) + goto fakepain; + return -1; } } @@ -1081,19 +1109,25 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { goto dopain; } + else if (target->flags7 & MF7_ALLOWPAIN) + goto fakepain; + return -1; } } - - damage = target->TakeSpecialDamage (inflictor, source, damage, mod); + if (damage > 0) + damage = target->TakeSpecialDamage (inflictor, source, damage, mod); } if (damage == -1) { + if (target->flags7 & MF7_ALLOWPAIN) + goto fakepain; + return -1; } // Push the target unless the source's weapon's kickback is 0. // (i.e. Gauntlets/Chainsaw) - if (inflictor && inflictor != target // [RH] Not if hurting own self + if (!(plrDontThrust) && inflictor && inflictor != target // [RH] Not if hurting own self && !(target->flags & MF_NOCLIP) && !(inflictor->flags2 & MF2_NODMGTHRUST) && !(flags & DMG_THRUSTLESS) @@ -1134,11 +1168,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust); } - thrust = FLOAT2FIXED(fltthrust); - - // Don't apply ultra-small damage thrust - if (thrust < FRACUNIT/100) thrust = 0; + // Don't apply ultra-small damage thrust. + if (thrust < FRACUNIT / 100) + thrust = 0; // make fall forwards sometimes if ((damage < 40) && (damage > target->health) @@ -1147,8 +1180,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // [RH] But only if not too fast and not flying && thrust < 10*FRACUNIT && !(target->flags & MF_NOGRAVITY) - && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) - ) + && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL))) { ang += ANG180; thrust *= 4; @@ -1215,6 +1247,12 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him + if (player->mo->flags7 & MF7_ALLOWPAIN) + { + invulpain = true; + fakeDamage = damage; + goto fakepain; + } return -1; } @@ -1232,7 +1270,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { // If MF6_FORCEPAIN is set, make the player enter the pain state. if (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL && - (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS)) + (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS) && + (!(player->mo->flags2 & MF2_INVULNERABLE))) { goto dopain; } @@ -1296,7 +1335,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = newdam; if (damage <= 0) { - return damage; + if (target->flags7 & MF7_ALLOWPAIN) + goto fakepain; + else + return damage; } } @@ -1383,6 +1425,12 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } +fakepain: //Needed so we can skip the rest of the above, but still obey the original rules. + if (target->flags7 & MF7_ALLOWPAIN && (fakeDamage != damage)) + { + holdDamage = damage; + damage = fakeDamage; + } if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) && (target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY)) @@ -1474,6 +1522,14 @@ dopain: if (justhit && (target->target == source || !target->target || !target->IsFriend(target->target))) target->flags |= MF_JUSTHIT; // fight back! + if (invulpain) //Note that this takes into account all the cheats a player has, in terms of invulnerability. + { + return -1; //NOW we return -1! + } + else if (target->flags7 & MF7_ALLOWPAIN) + { + return holdDamage; + } return damage; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a92eed8f8..bae4563b3 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5986,7 +5986,7 @@ bool AActor::IsHostile (AActor *other) int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype) { if (target->player && target->player->mo == target && damage < 1000 && - (target->player->cheats & CF_GODMODE)) + (target->player->cheats & CF_GODMODE || target->player->cheats & CF_GODMODE2)) { return -1; } diff --git a/src/p_user.cpp b/src/p_user.cpp index 045e61969..8eddf3135 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2588,7 +2588,8 @@ void P_PlayerThink (player_t *player) { if (player->mo->waterlevel < 3 || (player->mo->flags2 & MF2_INVULNERABLE) || - (player->cheats & (CF_GODMODE | CF_NOCLIP2))) + (player->cheats & (CF_GODMODE | CF_NOCLIP2)) || + (player->cheats & CF_GODMODE2)) { player->mo->ResetAirSupply (); } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index dbd1f1031..347b2e01a 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -245,6 +245,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, BUDDHA, AActor, flags7), DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), + DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From 043b28f4ba69f681a279ae02f752f33e10e6b54d Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 2 Nov 2014 17:58:59 +1300 Subject: [PATCH 189/313] Make Prediction lerping less pick + debug - Lerping uses int rather than fixed/float comparisons - Added debug information --- src/p_user.cpp | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 045e61969..c59e35122 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -78,7 +78,9 @@ CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCO struct PredictPos { int gametic; - FVector3 point; + fixed_t x; + fixed_t y; + fixed_t z; fixed_t pitch; fixed_t yaw; } static PredictionLerpFrom, PredictionLerpResult, PredictionLast; @@ -2605,12 +2607,19 @@ void P_PredictionLerpReset() PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0; } -bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale) +bool P_LerpCalculate(PredictPos from, PredictPos to, PredictPos &result, float scale) { - result = to - from; - result *= scale; - result = result + from; - FVector3 delta = result - to; + FVector3 vecFrom(FIXED2DBL(from.x), FIXED2DBL(from.y), FIXED2DBL(from.z)); + FVector3 vecTo(FIXED2DBL(to.x), FIXED2DBL(to.y), FIXED2DBL(to.z)); + FVector3 vecResult; + vecResult = vecTo - vecFrom; + vecResult *= scale; + vecResult = vecResult + vecFrom; + FVector3 delta = vecResult - vecTo; + + result.x = FLOAT2FIXED(vecResult.X); + result.y = FLOAT2FIXED(vecResult.Y); + result.z = FLOAT2FIXED(vecResult.Z); // As a fail safe, assume extrapolation is the threshold. return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f); @@ -2715,8 +2724,18 @@ void P_PredictPlayer (player_t *player) if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld) { // Z is not compared as lifts will alter this with no apparent change - DoLerp = (PredictionLast.point.X != FIXED2FLOAT(player->mo->x) || - PredictionLast.point.Y != FIXED2FLOAT(player->mo->y)); + // Make lerping less picky by only testing whole units + DoLerp = ((PredictionLast.x >> 16) != (player->mo->x >> 16) || + (PredictionLast.y >> 16) != (player->mo->y >> 16)); + + // Aditional Debug information + if (developer && DoLerp) + { + DPrintf("Lerp! Ltic (%d) && Ptic (%d) | Lx (%d) && Px (%d) | Ly (%d) && Py (%d)\n", + PredictionLast.gametic, i, + (PredictionLast.x >> 16), (player->mo->x >> 16), + (PredictionLast.y >> 16), (player->mo->y >> 16)); + } } } @@ -2733,19 +2752,19 @@ void P_PredictPlayer (player_t *player) } PredictionLast.gametic = maxtic - 1; - PredictionLast.point.X = FIXED2FLOAT(player->mo->x); - PredictionLast.point.Y = FIXED2FLOAT(player->mo->y); - PredictionLast.point.Z = FIXED2FLOAT(player->mo->z); + PredictionLast.x = player->mo->x; + PredictionLast.y = player->mo->y; + PredictionLast.z = player->mo->z; if (PredictionLerptics > 0) { if (PredictionLerpFrom.gametic > 0 && - P_LerpCalculate(PredictionLerpFrom.point, PredictionLast.point, PredictionLerpResult.point, (float)PredictionLerptics * cl_predict_lerpscale)) + P_LerpCalculate(PredictionLerpFrom, PredictionLast, PredictionLerpResult, (float)PredictionLerptics * cl_predict_lerpscale)) { PredictionLerptics++; - player->mo->x = FLOAT2FIXED(PredictionLerpResult.point.X); - player->mo->y = FLOAT2FIXED(PredictionLerpResult.point.Y); - player->mo->z = FLOAT2FIXED(PredictionLerpResult.point.Z); + player->mo->x = PredictionLerpResult.x; + player->mo->y = PredictionLerpResult.y; + player->mo->z = PredictionLerpResult.z; } else { From 0569acd5d6550788eca06e80e40432d13785c205 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 10:49:50 +0200 Subject: [PATCH 190/313] Fixed compilation issues with GCC 4.0.1 PowerPC --- src/cocoa/i_backend_cocoa.mm | 1 + src/cocoa/i_timer.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index ffd95b5b3..d2c8b3093 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -41,6 +41,7 @@ #include #include +#include #include #include diff --git a/src/cocoa/i_timer.cpp b/src/cocoa/i_timer.cpp index db6811212..0699a9967 100644 --- a/src/cocoa/i_timer.cpp +++ b/src/cocoa/i_timer.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -92,7 +93,12 @@ void* TimerThreadFunc(void*) if (!g_isTicFrozen) { - __sync_add_and_fetch(&s_tics, 1); + // The following GCC/Clang intrinsic can be used instead of OS X specific function: + // __sync_add_and_fetch(&s_tics, 1); + // Although it's not supported on all platform/compiler combination, + // e.g. GCC 4.0.1 with PowerPC target architecture + + OSAtomicIncrement32(&s_tics); } s_timerStart = SDL_GetTicks(); From d8be168474e47a8a9060a8a9bad920d0894c00cf Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 11:32:09 +0200 Subject: [PATCH 191/313] Set opaque clear color --- src/cocoa/i_backend_cocoa.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index d2c8b3093..ff602cb9d 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1181,7 +1181,7 @@ static ApplicationController* appCtrl; const NSSize viewSize = GetRealContentViewSize(m_window); glViewport(0, 0, viewSize.width, viewSize.height); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); CGLFlushDrawable(CGLGetCurrentContext()); From 2f59a8792af1c08dfc0acac424e45ab9ab0feb89 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 15:50:43 +0200 Subject: [PATCH 192/313] Use rectangle texture for render buffer Game is rendered correctly even if hardware doesn't support non-power of two textures The ARB_texture_rectangle extension is supported on almost all Macs running 10.4 Tiger --- src/cocoa/i_backend_cocoa.mm | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index ff602cb9d..873b34c03 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -1214,16 +1215,16 @@ static ApplicationController* appCtrl; { if (0 == m_softwareRenderingTexture) { - glEnable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_RECTANGLE_ARB); glGenTextures(1, &m_softwareRenderingTexture); - glBindTexture(GL_TEXTURE_2D, m_softwareRenderingTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_softwareRenderingTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + 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_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + 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; @@ -1750,18 +1751,18 @@ int SDL_Flip(SDL_Surface* screen) const int width = screen->w; const int height = screen->h; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, 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(1.0f, 0.0f); + glTexCoord2f(width, 0.0f); glVertex2f(width, 0.0f); - glTexCoord2f(1.0f, 1.0f); + glTexCoord2f(width, height); glVertex2f(width, height); - glTexCoord2f(0.0f, 1.0f); + glTexCoord2f(0.0f, height); glVertex2f(0.0f, height); glEnd(); From c0ec1d5114693c4053f091882af8b675a18606d8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 15:56:22 +0200 Subject: [PATCH 193/313] Fixed color channels displacement on Big Endian targets --- src/cocoa/i_backend_cocoa.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 873b34c03..594f7fca6 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1750,7 +1750,13 @@ int SDL_Flip(SDL_Surface* screen) 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, GL_RGBA, GL_UNSIGNED_BYTE, screen->pixels); From 8f915c9dcc0f1874a4bbdb92418e29f67933e534 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 15:58:47 +0200 Subject: [PATCH 194/313] Fixed broken ACS on Big Endian platforms This fixes maps transition in Strife http://forum.zdoom.org/viewtopic.php?f=2&t=36754 --- 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 e48e97e86..4150ee231 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -7296,7 +7296,7 @@ scriptwait: while (min <= max) { int mid = (min + max) / 2; - SDWORD caseval = pc[mid*2]; + SDWORD caseval = LittleLong(pc[mid*2]); if (caseval == STACK(1)) { pc = activeBehavior->Ofs2PC (LittleLong(pc[mid*2+1])); From eb98a999b25457e1c1f54d0bcd3e49ae4590c561 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 2 Nov 2014 16:18:28 +0200 Subject: [PATCH 195/313] Fixed color channels displacement on Big Endian targets, take two :( --- src/cocoa/i_backend_cocoa.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 594f7fca6..9c5f1eea7 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1758,7 +1758,7 @@ int SDL_Flip(SDL_Surface* screen) #endif // __LITTLE_ENDIAN__ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, screen->pixels); + width, height, 0, format, GL_UNSIGNED_BYTE, screen->pixels); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); From c63adf920a5ed52c4380c8cc32449a1b06686863 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 5 Nov 2014 22:05:21 -0600 Subject: [PATCH 196/313] - Fixed: BUDDHA flag on a player wasn't considered. --- src/p_interaction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index f318ed3b3..d0d115217 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1302,7 +1302,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. - if (((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE)) && player->playerstate != PST_DEAD) + if (((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA) && damage < TELEFRAG_DAMAGE)) && player->playerstate != PST_DEAD) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1744,7 +1744,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->health -= damage; if (target->health <= 0) { // Death - if ((player->cheats & CF_BUDDHA && damage < TELEFRAG_DAMAGE) || (player->cheats & CF_BUDDHA2)) + if ((((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && damage < TELEFRAG_DAMAGE) || (player->cheats & CF_BUDDHA2)) { // [SP] Save the player... player->health = target->health = 1; } From 95bd6bde9a754a8f696ba470e28c33d2edb14bc2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 5 Nov 2014 23:06:28 -0600 Subject: [PATCH 197/313] - Added FOILBUDDHA check for A_BFGSpray. --- src/g_doom/a_doomweaps.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 38c823e18..976ffbfc4 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -609,9 +609,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) damage = defdamage; } + int dmgFlagPass = 0; + dmgFlagPass += (spray != NULL && (spray->flags3 & MF3_FOILINVUL)) ? DMG_FOILINVUL : 0; //[MC]Because the original foilinvul wasn't working. + dmgFlagPass += (spray != NULL && (spray->flags7 & MF7_FOILBUDDHA)) ? DMG_FOILBUDDHA : 0; thingToHit = linetarget; int newdam = P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash), - spray != NULL && (spray->flags3 & MF3_FOILINVUL)? DMG_FOILINVUL : 0); + dmgFlagPass); P_TraceBleed (newdam > 0 ? newdam : damage, thingToHit, self->target); } } From 71ce4bcf06a09ad2bc7b903ee8082946b3de96d6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 5 Nov 2014 23:16:01 -0600 Subject: [PATCH 198/313] - Fixed: Rail attacks didn't properly respect FOILINVUL. - Added: FOILBUDDHA support for rail attacks. --- src/p_map.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index b688ef43d..44db5f4d8 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4111,7 +4111,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i z = shootz + FixedMul(hitdist, vz); if ((hitactor->flags & MF_NOBLOOD) || - (hitactor->flags2 & (MF2_DORMANT | MF2_INVULNERABLE))) + (hitactor->flags2 & MF2_DORMANT || ((hitactor->flags2 & MF2_INVULNERABLE) && !(puffDefaults->flags3 & MF3_FOILINVUL)))) { spawnpuff = (puffclass != NULL); } @@ -4132,7 +4132,10 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i { P_PoisonMobj(hitactor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod, puffDefaults->PoisonDamageType); } - int newdam = P_DamageMobj(hitactor, thepuff ? thepuff : source, source, damage, damagetype, DMG_INFLICTOR_IS_PUFF); + int dmgFlagPass = DMG_INFLICTOR_IS_PUFF; + dmgFlagPass += (puffDefaults->flags3 & MF3_FOILINVUL) ? DMG_FOILINVUL : 0; //[MC]Because the original foilinvul check wasn't working. + dmgFlagPass += (puffDefaults->flags7 & MF7_FOILBUDDHA) ? DMG_FOILBUDDHA : 0; + int newdam = P_DamageMobj(hitactor, thepuff ? thepuff : source, source, damage, damagetype, dmgFlagPass); if (bleed) { P_SpawnBlood(x, y, z, (source->angle + angleoffset) - ANG180, newdam > 0 ? newdam : damage, hitactor); From 848225e9ee01621357dc7ecee9e6c27f990d079d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 7 Nov 2014 17:12:03 -0600 Subject: [PATCH 199/313] - Fixed typo on WARPF_ABSOLUTEPOSITION --- wadsrc/static/actors/constants.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 78741e059..dec149399 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -327,7 +327,7 @@ Const Int WARPF_COPYINTERPOLATION = 0x40; Const Int WARPF_STOP = 0x80; Const Int WARPF_TOFLOOR = 0x100; Const Int WARPF_TESTONLY = 0x200; -Const Int WAPRF_ABSOLUTEPOSITION = 0x400; +Const Int WARPF_ABSOLUTEPOSITION = 0x400; // flags for A_SetPitch/SetAngle const int SPF_FORCECLAMP = 1; From 364d9069bd9a68b8f08d8714df4e92f2bffb018a Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 7 Nov 2014 17:20:12 -0600 Subject: [PATCH 200/313] In fact, let's go ahead and ensure compatibility doesn't break since it's already in there. --- wadsrc/static/actors/constants.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index dec149399..8baf2e65c 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -327,6 +327,7 @@ Const Int WARPF_COPYINTERPOLATION = 0x40; Const Int WARPF_STOP = 0x80; Const Int WARPF_TOFLOOR = 0x100; Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_ABSOLUTEPOSITION = 0x400; // flags for A_SetPitch/SetAngle From eab971500b43b6bac77af29aec1c299d68f2710e Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sat, 8 Nov 2014 00:31:16 +0100 Subject: [PATCH 201/313] Backport 'A_FaceConsolePlayer' from zandronum. By Dusk, who authorized me to do this. --- src/thingdef/thingdef_codeptr.cpp | 30 ++++++++++++++++++++++++++++++ wadsrc/static/actors/actor.txt | 1 + 2 files changed, 31 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2732337c7..28affbb0f 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3781,6 +3781,36 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) } } +//=========================================================================== +// +// [TP] A_FaceConsolePlayer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { + ACTION_PARAM_START (1); + ACTION_PARAM_ANGLE (MaxTurnAngle, 0); + + angle_t Angle; + angle_t DeltaAngle; + AActor *pConsolePlayer; + + // Always watch the consoleplayer. + pConsolePlayer = players[consoleplayer].mo; + if (( playeringame[consoleplayer] == false ) || ( pConsolePlayer == NULL )) + return; + + // Find the angle between the actor and the console player. + Angle = R_PointToAngle2( self->x, self->y, pConsolePlayer->x, pConsolePlayer->y ); + DeltaAngle = Angle - self->angle; + + if (( MaxTurnAngle == 0 ) || ( DeltaAngle < MaxTurnAngle ) || ( DeltaAngle > (unsigned)-MaxTurnAngle )) + self->angle = Angle; + else if ( DeltaAngle < ANG180 ) + self->angle += MaxTurnAngle; + else + self->angle -= MaxTurnAngle; +} + //=========================================================================== // // A_MonsterRefire diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e253f53ac..468b0f7b5 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -186,6 +186,7 @@ ACTOR Actor native //: Thinker action native A_ClearSoundTarget(); action native A_FireAssaultGun(); action native A_CheckTerrain(); + action native A_FaceConsolePlayer(float MaxTurnAngle = 0); // [TP] action native A_MissileAttack(); action native A_MeleeAttack(); From c28c0b8f0b54c05dff75abe440f94a4ee0be4657 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 8 Nov 2014 08:53:32 +0100 Subject: [PATCH 202/313] Revert "Backport 'A_FaceConsolePlayer' from zandronum." This reverts commit eab971500b43b6bac77af29aec1c299d68f2710e. As Edward850 pointed out, this feature is broken by design and therefore completely useless. --- src/thingdef/thingdef_codeptr.cpp | 30 ------------------------------ wadsrc/static/actors/actor.txt | 1 - 2 files changed, 31 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 28affbb0f..2732337c7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3781,36 +3781,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) } } -//=========================================================================== -// -// [TP] A_FaceConsolePlayer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_FaceConsolePlayer) { - ACTION_PARAM_START (1); - ACTION_PARAM_ANGLE (MaxTurnAngle, 0); - - angle_t Angle; - angle_t DeltaAngle; - AActor *pConsolePlayer; - - // Always watch the consoleplayer. - pConsolePlayer = players[consoleplayer].mo; - if (( playeringame[consoleplayer] == false ) || ( pConsolePlayer == NULL )) - return; - - // Find the angle between the actor and the console player. - Angle = R_PointToAngle2( self->x, self->y, pConsolePlayer->x, pConsolePlayer->y ); - DeltaAngle = Angle - self->angle; - - if (( MaxTurnAngle == 0 ) || ( DeltaAngle < MaxTurnAngle ) || ( DeltaAngle > (unsigned)-MaxTurnAngle )) - self->angle = Angle; - else if ( DeltaAngle < ANG180 ) - self->angle += MaxTurnAngle; - else - self->angle -= MaxTurnAngle; -} - //=========================================================================== // // A_MonsterRefire diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 468b0f7b5..e253f53ac 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -186,7 +186,6 @@ ACTOR Actor native //: Thinker action native A_ClearSoundTarget(); action native A_FireAssaultGun(); action native A_CheckTerrain(); - action native A_FaceConsolePlayer(float MaxTurnAngle = 0); // [TP] action native A_MissileAttack(); action native A_MeleeAttack(); From d2d6d2a7f8c1d501953d0ea4ab97d457c9c99d68 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 8 Nov 2014 13:30:41 +0200 Subject: [PATCH 203/313] Lowered minimum OS requirement to 10.5 Leopard --- src/CMakeLists.txt | 2 +- src/cocoa/i_backend_cocoa.mm | 187 ++++++++++++++++++++++++----------- 2 files changed, 132 insertions(+), 57 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a1630bed..ef252fc3a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1212,7 +1212,7 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) if( APPLE ) set_target_properties(zdoom PROPERTIES - LINK_FLAGS "-framework Cocoa -framework IOKit -framework OpenGL" + LINK_FLAGS "-framework Carbon -framework Cocoa -framework IOKit -framework OpenGL" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/zdoom-info.plist" ) # Fix fmod link so that it can be found in the app bundle. diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 9c5f1eea7..c9ef6b9da 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -781,6 +781,19 @@ const Uint16 BYTES_PER_PIXEL = 4; // --------------------------------------------------------------------------- +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 FullscreenWindow : NSWindow { @@ -788,27 +801,8 @@ const Uint16 BYTES_PER_PIXEL = 4; - (bool)canBecomeKeyWindow; -- (void)close; - -@end - - -@implementation FullscreenWindow - -- (bool)canBecomeKeyWindow -{ - return true; -} - - -- (void)close -{ - [super close]; - - I_ShutdownJoysticks(); - - [NSApp terminate:self]; -} +- (void)setLevel:(NSInteger)level; +- (void)setStyleMask:(NSUInteger)styleMask; @end @@ -892,12 +886,51 @@ const Uint16 BYTES_PER_PIXEL = 4; - (void)setMainWindowVisible:(bool)visible; +- (void)setWindowStyleMask:(NSUInteger)styleMask; + @end static ApplicationController* appCtrl; +// --------------------------------------------------------------------------- + + +@implementation FullscreenWindow + +- (bool)canBecomeKeyWindow +{ + return true; +} + +- (void)setLevel:(NSInteger)level +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + [super setLevel:level]; +#else // 10.5 or earlier + // 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); +#endif // 10.6 or higher +} + +- (void)setStyleMask:(NSUInteger)styleMask +{ + [appCtrl setWindowStyleMask:styleMask]; +} + +@end + + +// --------------------------------------------------------------------------- + + @implementation ApplicationController - (id)init @@ -1040,6 +1073,23 @@ static ApplicationController* appCtrl; } +- (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]; + + NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(closeWindow:)]; + [closeButton setTarget:self]; + + return window; +} + - (void)initializeOpenGL { if (m_openGLInitialized) @@ -1047,15 +1097,7 @@ static ApplicationController* appCtrl; return; } - // Create window - - m_window = [[FullscreenWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) - styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - [m_window setOpaque:YES]; - [m_window makeFirstResponder:self]; - [m_window setAcceptsMouseMovedEvents:YES]; + m_window = [self createWindow:STYLE_MASK_WINDOWED]; // Create OpenGL context and view @@ -1093,7 +1135,7 @@ static ApplicationController* appCtrl; m_openGLInitialized = true; } -- (void)switchToFullscreen +- (void)setFullscreenModeWidth:(int)width height:(int)height { NSScreen* screen = [m_window screen]; @@ -1105,42 +1147,50 @@ static ApplicationController* appCtrl; const float displayWidth = displayRect.size.width; const float displayHeight = displayRect.size.height; - const float pixelScaleFactorX = displayWidth / static_cast(m_width ); - const float pixelScaleFactorY = displayHeight / static_cast(m_height); + const float pixelScaleFactorX = displayWidth / static_cast(width ); + const float pixelScaleFactorY = displayHeight / static_cast(height); rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); - rbOpts.width = m_width * rbOpts.pixelScale; - rbOpts.height = m_height * rbOpts.pixelScale; + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; - [m_window setLevel:NSMainMenuWindowLevel + 1]; - [m_window setStyleMask:NSBorderlessWindowMask]; - [m_window setHidesOnDeactivate:YES]; + 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)switchToWindowed +- (void)setWindowedModeWidth:(int)width height:(int)height { rbOpts.pixelScale = 1.0f; - rbOpts.width = static_cast(m_width ); - rbOpts.height = static_cast(m_height); + rbOpts.width = static_cast(width ); + rbOpts.height = static_cast(height); rbOpts.shiftX = 0.0f; rbOpts.shiftY = 0.0f; - const NSSize windowPixelSize = NSMakeSize(m_width, m_height); + const NSSize windowPixelSize = NSMakeSize(width, height); const NSSize windowSize = vid_hidpi ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] : windowPixelSize; - [m_window setLevel:NSNormalWindowLevel]; - [m_window setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask]; - [m_window setHidesOnDeactivate:NO]; + 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]; } @@ -1155,33 +1205,28 @@ static ApplicationController* appCtrl; return; } - m_fullscreen = fullscreen; - m_width = width; - m_height = height; - m_hiDPI = hiDPI; - [self initializeOpenGL]; if (IsHiDPISupported()) { NSOpenGLView* const glView = [m_window contentView]; - [glView setWantsBestResolutionOpenGLSurface:m_hiDPI]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; } - if (m_fullscreen) + if (fullscreen) { - [self switchToFullscreen]; + [self setFullscreenModeWidth:width height:height]; } else { - [self switchToWindowed]; + [self setWindowedModeWidth:width height:height]; } rbOpts.dirty = true; const NSSize viewSize = GetRealContentViewSize(m_window); - glViewport(0, 0, viewSize.width, viewSize.height); + 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); @@ -1194,7 +1239,12 @@ static ApplicationController* appCtrl; if (![m_window isKeyWindow]) { [m_window makeKeyAndOrderFront:nil]; - } + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; } - (void)useHiDPI:(bool)hiDPI @@ -1321,6 +1371,31 @@ static ApplicationController* appCtrl; } } + +- (void)setWindowStyleMask:(NSUInteger)styleMask +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + [m_window setStyleMask:styleMask]; +#else // 10.5 or earlier + // Before 10.6 it's impossible to change window's style mask + // To workaround this new window should be created with required style mask + + FullscreenWindow* tempWindow = [self createWindow:styleMask]; + [tempWindow setContentView:[m_window contentView]]; + + [m_window close]; + m_window = tempWindow; +#endif // 10.6 or higher +} + + +- (void)closeWindow:(id)sender +{ + I_ShutdownJoysticks(); + + [NSApp terminate:sender]; +} + @end From a613da43dd99ce21b4ceab325c7bfaabe9783e94 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sat, 8 Nov 2014 17:38:09 +0000 Subject: [PATCH 204/313] - Bot movement is now calculated locally. --- src/b_bot.cpp | 14 ++++-- src/b_bot.h | 3 -- src/b_game.cpp | 66 +++++++++++++------------- src/d_main.cpp | 19 -------- src/d_net.cpp | 108 ++++++++---------------------------------- src/g_game.cpp | 6 +++ src/p_interaction.cpp | 2 +- src/p_mobj.cpp | 2 +- src/version.h | 2 +- 9 files changed, 71 insertions(+), 151 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index eb2052a26..dc6f2cdc4 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -30,8 +30,6 @@ DBot::DBot () void DBot::Clear () { - savedyaw = 0; - savedpitch = 0; angle = 0; dest = NULL; prev = NULL; @@ -59,9 +57,15 @@ void DBot::Serialize (FArchive &arc) { Super::Serialize (arc); - arc << savedyaw - << savedpitch - << angle + if (SaveVersion < 4515) + { + angle_t savedyaw; + int savedpitch; + arc << savedyaw + << savedpitch; + } + + arc << angle << dest << prev << enemy diff --git a/src/b_bot.h b/src/b_bot.h index 375b09abb..1862faee5 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -165,9 +165,6 @@ public: void Clear (); void Serialize (FArchive &arc); - angle_t savedyaw; - int savedpitch; - angle_t angle; // The wanted angle that the bot try to get every tic. // (used to get a smooth view movement) TObjPtr dest; // Move Destination. diff --git a/src/b_game.cpp b/src/b_game.cpp index f13c168db..853ba7f23 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -102,7 +102,7 @@ void FCajunMaster::Main (int buf) BotThinkCycles.Reset(); - if (consoleplayer != Net_Arbitrator || demoplayback) + if (demoplayback) return; if (gamestate != GS_LEVEL) @@ -122,37 +122,40 @@ void FCajunMaster::Main (int buf) BotThinkCycles.Unclock(); } - //Add new bots? - if (wanted_botnum > botnum && !freeze) + if (consoleplayer == Net_Arbitrator) { - if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) + //Add new bots? + if (wanted_botnum > botnum && !freeze) { - if (!SpawnBot (getspawned[spawn_tries])) - wanted_botnum--; - spawn_tries++; + if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) + { + if (!SpawnBot (getspawned[spawn_tries])) + wanted_botnum--; + spawn_tries++; + } + + t_join--; } - t_join--; - } - - //Check if player should go observer. Or un observe - if (bot_observer && !observer && !netgame) - { - Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); - observer = true; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; - players[consoleplayer].mo->flags2 |= MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); - } - else if (!bot_observer && observer && !netgame) //Go back - { - Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); - observer = false; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; - players[consoleplayer].mo->flags2 &= ~MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); + //Check if player should go observer. Or un observe + if (bot_observer && !observer && !netgame) + { + Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); + observer = true; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; + players[consoleplayer].mo->flags2 |= MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); + } + else if (!bot_observer && observer && !netgame) //Go back + { + Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); + observer = false; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; + players[consoleplayer].mo->flags2 &= ~MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); + } } m_Thinking = false; @@ -348,11 +351,8 @@ void FCajunMaster::TryAddBot (BYTE **stream, int player) if (DoAddBot ((BYTE *)info, skill)) { - if (consoleplayer == Net_Arbitrator) - { - //Increment this. - botnum++; - } + //Increment this. + botnum++; if (thebot != NULL) { diff --git a/src/d_main.cpp b/src/d_main.cpp index e08b1539e..7eb09c4e4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -975,25 +975,6 @@ void D_DoomLoop () I_StartTic (); D_ProcessEvents (); G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]); - //Added by MC: For some of that bot stuff. The main bot function. - int i; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL && players[i].mo) - { - players[i].Bot->savedyaw = players[i].mo->angle; - players[i].Bot->savedpitch = players[i].mo->pitch; - } - } - bglobal.Main (maketic%BACKUPTICS); - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL && players[i].mo) - { - players[i].mo->angle = players[i].Bot->savedyaw; - players[i].mo->pitch = players[i].Bot->savedpitch; - } - } if (advancedemo) D_DoAdvanceDemo (); C_Ticker (); diff --git a/src/d_net.cpp b/src/d_net.cpp index 3d0d20b41..c3e656c45 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -824,7 +824,6 @@ void GetPackets (void) } if (netbuffer[0] & NCMD_QUITTERS) - { numplayers = netbuffer[k++]; for (int i = 0; i < numplayers; ++i) @@ -902,64 +901,18 @@ void GetPackets (void) for (i = 0; i < numplayers; ++i) { - int node = (players[playerbytes[i]].Bot == NULL) ? - nodeforplayer[playerbytes[i]] : netnode; + int node = nodeforplayer[playerbytes[i]]; SkipTicCmd (&start, nettics[node] - realstart); for (tics = nettics[node]; tics < realend; tics++) ReadTicCmd (&start, playerbytes[i], tics); - } - // Update the number of tics received from each node. This must - // be separate from the above loop in case the master is also - // sending bot movements. If it's not separate, then the bots - // will only move on the master, because the other players will - // read the master's tics and then think they already got all - // the tics for the bots and skip the bot tics included in the - // packet. - for (i = 0; i < numplayers; ++i) - { - if (players[playerbytes[i]].Bot == NULL) - { - nettics[nodeforplayer[playerbytes[i]]] = realend; - } - } - } - } -} -void AdjustBots (int gameticdiv) -{ - // [RH] This loop adjusts the bots' rotations for ticcmds that have - // been already created but not yet executed. This way, the bot is still - // able to create ticcmds that accurately reflect the state it wants to - // be in even when gametic lags behind maketic. - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL && players[i].mo) - { - players[i].Bot->savedyaw = players[i].mo->angle; - players[i].Bot->savedpitch = players[i].mo->pitch; - for (int j = gameticdiv; j < maketic/ticdup; j++) - { - players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup; - players[i].mo->pitch -= (netcmds[i][j%BACKUPTICS].ucmd.pitch << 16) * ticdup; + nettics[nodeforplayer[playerbytes[i]]] = realend; } } } } -void UnadjustBots () -{ - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL && players[i].mo) - { - players[i].mo->angle = players[i].Bot->savedyaw; - players[i].mo->pitch = players[i].Bot->savedpitch; - } - } -} - // // NetUpdate // Builds ticcmds for console player, @@ -1006,9 +959,7 @@ void NetUpdate (void) newtics = 0; } - // build new ticcmds for console player (and bots if I am the arbitrator) - AdjustBots (gametic / ticdup); - + // build new ticcmds for console player for (i = 0; i < newtics; i++) { I_StartTic (); @@ -1018,11 +969,6 @@ void NetUpdate (void) //Printf ("mk:%i ",maketic); G_BuildTiccmd (&localcmds[maketic % LOCALCMDTICS]); - if (maketic % ticdup == 0) - { - //Added by MC: For some of that bot stuff. The main bot function. - bglobal.Main ((maketic / ticdup) % BACKUPTICS); - } maketic++; if (ticdup == 1 || maketic == 0) @@ -1102,8 +1048,6 @@ void NetUpdate (void) } } - UnadjustBots (); - if (singletics) return; // singletic update is synchronous @@ -1123,14 +1067,11 @@ void NetUpdate (void) if (consoleplayer == Net_Arbitrator) { - for (j = 0; j < MAXPLAYERS; j++) + for (j = 0; j < doomcom.numnodes; j++) { - if (playeringame[j]) + if (nodeingame[j] && NetMode == NET_PacketServer) { - if (players[j].Bot != NULL || NetMode == NET_PacketServer) - { - count++; - } + count++; } } @@ -1265,15 +1206,12 @@ void NetUpdate (void) netbuffer[0] |= NCMD_MULTI; netbuffer[k++] = count; - for (l = 1, j = 0; j < MAXPLAYERS; j++) + for (l = 1, j = 0; j < doomcom.numnodes; j++) { - if (playeringame[j] && j != playerfornode[i] && j != consoleplayer) + if (nodeingame[j] && j != i && j != nodeforplayer[consoleplayer] && NetMode == NET_PacketServer) { - if (players[j].Bot != NULL || NetMode == NET_PacketServer) - { - playerbytes[l++] = j; - netbuffer[k++] = j; - } + playerbytes[l++] = playerfornode[j]; + netbuffer[k++] = playerfornode[j]; } } } @@ -1293,7 +1231,7 @@ void NetUpdate (void) prev %= BACKUPTICS; // The local player has their tics sent first, followed by - // the other players/bots. + // the other players. if (l == 0) { WriteWord (localcmds[localstart].consistancy, &cmddata); @@ -1308,23 +1246,17 @@ void NetUpdate (void) } else if (i != 0) { - if (players[playerbytes[l]].Bot != NULL) - { - WriteWord (0, &cmddata); // fake consistancy word - } - else - { - int len; - BYTE *spec; + int len; + BYTE *spec; - WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata); - spec = NetSpecs[playerbytes[l]][start].GetData (&len); - if (spec != NULL) - { - memcpy (cmddata, spec, len); - cmddata += len; - } + WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata); + spec = NetSpecs[playerbytes[l]][start].GetData (&len); + if (spec != NULL) + { + memcpy (cmddata, spec, len); + cmddata += len; } + WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd, prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata); } diff --git a/src/g_game.cpp b/src/g_game.cpp index 1ea1c6b68..af4a5411e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1127,6 +1127,12 @@ void G_Ticker () // check, not just the player's x position like BOOM. DWORD rngsum = FRandom::StaticSumSeeds (); + if ((gametic % ticdup) == 0) + { + //Added by MC: For some of that bot stuff. The main bot function. + bglobal.Main (buf); + } + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index d0d115217..88d95b25e 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -606,7 +606,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) player->respawn_time = level.time + TICRATE; //Added by MC: Respawn bots - if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) + if (bglobal.botnum && !demoplayback) { if (player->Bot != NULL) player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index bae4563b3..c7bfcea21 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3228,7 +3228,7 @@ void AActor::Tick () } } - if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback && + if (bglobal.botnum && !demoplayback && ((flags & (MF_SPECIAL|MF_MISSILE)) || (flags3 & MF3_ISMONSTER))) { BotSupportCycles.Clock(); diff --git a/src/version.h b/src/version.h index d9597d2f4..f091f416a 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 4514 +#define SAVEVER 4515 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 8532b52ffb38f99f8f1374fab0d99ff85e4a1cec Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sat, 8 Nov 2014 17:38:56 +0000 Subject: [PATCH 205/313] - Bots are no longer removed when the net arbitrator disconnects. --- src/d_net.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index c3e656c45..4fcd66bd4 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -691,9 +691,6 @@ void PlayerIsGone (int netnode, int netconsole) if (netconsole == Net_Arbitrator) { - bglobal.RemoveAllBots(true); - Printf("Removed all bots\n"); - // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { From 8b85e6d00b2545b6e8c60c8edacd1e84d08d2e68 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sat, 8 Nov 2014 17:59:45 +0100 Subject: [PATCH 206/313] - Port an empty version of 'A_FaceConsolePlayer'. Hissies will be happy now. --- src/thingdef/thingdef_codeptr.cpp | 11 +++++++++++ wadsrc/static/actors/actor.txt | 1 + 2 files changed, 12 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2732337c7..81d0fc4b7 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3781,6 +3781,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) } } +//=========================================================================== +// +// [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 diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e253f53ac..468b0f7b5 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -186,6 +186,7 @@ ACTOR Actor native //: Thinker action native A_ClearSoundTarget(); action native A_FireAssaultGun(); action native A_CheckTerrain(); + action native A_FaceConsolePlayer(float MaxTurnAngle = 0); // [TP] action native A_MissileAttack(); action native A_MeleeAttack(); From 87207bdb6d5c02c332c30ab941cba19951cc546e Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 9 Nov 2014 00:16:10 -0600 Subject: [PATCH 207/313] - Fixed: The mask didn't incorporate RGF_MISSILES, causing it to fail. --- 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 2732337c7..ff7fb9dd2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4678,7 +4678,7 @@ enum RadiusGiveFlags RGF_OBJECTS = 1 << 3, RGF_VOODOO = 1 << 4, RGF_CORPSES = 1 << 5, - RGF_MASK = 63, + RGF_MASK = 2111, RGF_NOTARGET = 1 << 6, RGF_NOTRACER = 1 << 7, RGF_NOMASTER = 1 << 8, From e3640b5bf510cec6ec726c5060fe709d97fbb8c0 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 9 Nov 2014 21:06:50 +1300 Subject: [PATCH 208/313] Stop DLevelScript from recursively Serializing --- src/p_acs.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/version.h | 2 +- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 4150ee231..59a5840fd 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2881,9 +2881,55 @@ DACSThinker::~DACSThinker () void DACSThinker::Serialize (FArchive &arc) { int scriptnum; + int scriptcount = 0; Super::Serialize (arc); - arc << Scripts << LastScript; + if (SaveVersion < 4515) + arc << Scripts << LastScript; + else + { + if (arc.IsStoring()) + { + DLevelScript *script; + script = Scripts; + while (true) + { + scriptcount++; + + // We want to store this list backwards, so we can't loose the last pointer + if (script->next == NULL) + break; + script = script->next; + } + arc << scriptcount; + + while (script) + { + arc << script; + script = script->prev; + } + } + else + { + // We are running through this list backwards, so the next entry is the last processed + DLevelScript *next = NULL; + arc << scriptcount; + for (int i = 0; i < scriptcount; i++) + { + arc << Scripts; + + Scripts->next = next; + Scripts->prev = NULL; + if (next != NULL) + next->prev = Scripts; + + next = Scripts; + + if (i == 0) + LastScript = Scripts; + } + } + } if (arc.IsStoring ()) { ScriptMap::Iterator it(RunningScripts); @@ -2969,7 +3015,8 @@ void DLevelScript::Serialize (FArchive &arc) DWORD i; Super::Serialize (arc); - arc << next << prev; + if (SaveVersion < 4515) + arc << next << prev; P_SerializeACSScriptNumber(arc, script, false); diff --git a/src/version.h b/src/version.h index d9597d2f4..f091f416a 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 4514 +#define SAVEVER 4515 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From d061adfbd666857bf861739e4232e18e44295621 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 11:18:20 +0200 Subject: [PATCH 209/313] Fixed all compilation issues with 10.4 SDK except for HID Utilities --- src/cocoa/i_backend_cocoa.mm | 99 ++++++++++++++++++++++++++++++++---- src/sdl/iwadpicker_cocoa.mm | 6 +++ src/sdl/sdlvideo.cpp | 7 +++ 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index c9ef6b9da..859fcc495 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -45,6 +45,80 @@ #include #include +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + +// Missing definitions for 10.4 and earlier + +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 + #include // Avoid collision between DObject class and Objective-C @@ -272,7 +346,7 @@ void I_ProcessJoysticks(); void I_GetEvent() { - [[NSRunLoop mainRunLoop] limitDateForMode:NSDefaultRunLoopMode]; + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; } void I_StartTic() @@ -634,6 +708,7 @@ void ProcessMouseButtonEvent(NSEvent* theEvent) 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); @@ -655,6 +730,9 @@ void ProcessMouseButtonEvent(NSEvent* theEvent) case NSOtherMouseUp: event.type = EV_KeyUp; break; + + default: + break; } event.data1 = std::min(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8)); @@ -1011,8 +1089,8 @@ static ApplicationController* appCtrl; selector:@selector(processEvents:) userInfo:nil repeats:YES]; - [[NSRunLoop mainRunLoop] addTimer:timer - forMode:NSDefaultRunLoopMode]; + [[NSRunLoop currentRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; exit(SDL_main(s_argc, s_argv)); } @@ -1106,22 +1184,22 @@ static ApplicationController* appCtrl; attributes[i++] = NSOpenGLPFADoubleBuffer; attributes[i++] = NSOpenGLPFAColorSize; - attributes[i++] = 32; + attributes[i++] = NSOpenGLPixelFormatAttribute(32); attributes[i++] = NSOpenGLPFADepthSize; - attributes[i++] = 24; + attributes[i++] = NSOpenGLPixelFormatAttribute(24); attributes[i++] = NSOpenGLPFAStencilSize; - attributes[i++] = 8; + attributes[i++] = NSOpenGLPixelFormatAttribute(8); if (m_multisample) { attributes[i++] = NSOpenGLPFAMultisample; attributes[i++] = NSOpenGLPFASampleBuffers; - attributes[i++] = 1; + attributes[i++] = NSOpenGLPixelFormatAttribute(1); attributes[i++] = NSOpenGLPFASamples; - attributes[i++] = m_multisample; + attributes[i++] = NSOpenGLPixelFormatAttribute(m_multisample); } - attributes[i] = 0; + attributes[i] = NSOpenGLPixelFormatAttribute(0); NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; @@ -1344,6 +1422,9 @@ static ApplicationController* appCtrl; case NSFlagsChanged: ProcessKeyboardFlagsEvent(event); break; + + default: + break; } [NSApp sendEvent:event]; diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 51e0ea0c8..57a4ce7f4 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -48,6 +48,12 @@ #include #include +#include + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +// Missing type definition for 10.4 and earlier +typedef unsigned int NSUInteger; +#endif // prior to 10.5 CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG); diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index c869545ab..6753169f0 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -550,6 +550,13 @@ void SDLFB::SetVSync (bool vsync) { // Apply vsync for native backend only (where OpenGL context is set) +#if MAC_OS_X_VERSION_MIN_REQUIRED < 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); } From 5170abfeed08d20826abe453638234c4a1fc6461 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 9 Nov 2014 22:57:14 +1300 Subject: [PATCH 210/313] DACSThinker can exist with no scripts --- src/p_acs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 59a5840fd..03de8b3df 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2892,7 +2892,7 @@ void DACSThinker::Serialize (FArchive &arc) { DLevelScript *script; script = Scripts; - while (true) + while (script) { scriptcount++; @@ -2914,6 +2914,8 @@ void DACSThinker::Serialize (FArchive &arc) // We are running through this list backwards, so the next entry is the last processed DLevelScript *next = NULL; arc << scriptcount; + Scripts = NULL; + LastScript = NULL; for (int i = 0; i < scriptcount; i++) { arc << Scripts; From a5993c4e2d3d57d4c4b71ba38e983f12d67a2ca1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 11:58:47 +0200 Subject: [PATCH 211/313] Disabled usage of IOKit's HID Manager API on OS X prior to 10.5 Source code only, CMake needs to be updated too --- src/cocoa/i_joystick.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index 5a1c92e8c..889c38fed 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -33,6 +33,8 @@ #include "m_joy.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + #include "HID_Utilities_External.h" #include "d_event.h" @@ -774,3 +776,37 @@ void I_ProcessJoysticks() 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 From d64a4a17ff9a306e55d451190d96a46bccc29a38 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 9 Nov 2014 12:11:58 +0100 Subject: [PATCH 212/313] - fixed: PRINT_LOG no longer worked since replacing the console buffer. --- src/c_console.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/c_console.cpp b/src/c_console.cpp index e7c7dc30b..9dcaa0820 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -561,6 +561,11 @@ int PrintString (int printlevel, const char *outline) maybedrawnow (false, false); } } + else if (Logfile != NULL) + { + fputs (outline, Logfile); + fflush (Logfile); + } return (int)strlen (outline); } From fa1d62ffbf509a4231a58488a53676c7c2a0558e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 15:23:34 +0200 Subject: [PATCH 213/313] Fixed random junk that may appear in transparency (alpha) channel of mouse cursor image --- src/cocoa/i_backend_cocoa.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 859fcc495..e2bd70374 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1539,6 +1539,8 @@ bool I_SetCursor(FTexture* cursorpic) // 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); From a40eb3443b2363606dd6d12e45e7ee8d19faf979 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 15:34:04 +0200 Subject: [PATCH 214/313] Fixed infinite recursion when setting window style on OS X 10.6 or newer --- src/cocoa/i_backend_cocoa.mm | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index e2bd70374..4a029515e 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1000,7 +1000,11 @@ static ApplicationController* appCtrl; - (void)setStyleMask:(NSUInteger)styleMask { - [appCtrl setWindowStyleMask:styleMask]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + [super setStyleMask:styleMask]; +#else // 10.5 or earlier + [appCtrl setWindowStyleMask:styleMask]; +#endif // 10.6 or higher } @end @@ -1455,18 +1459,15 @@ static ApplicationController* appCtrl; - (void)setWindowStyleMask:(NSUInteger)styleMask { -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - [m_window setStyleMask:styleMask]; -#else // 10.5 or earlier // 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; -#endif // 10.6 or higher } From 70bb80e2bfda676cef9cdb868bb785ecfe4ceed5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 16:49:26 +0200 Subject: [PATCH 215/313] Removed OS X version check It didn't work correctly and .plist should set minimum OS version to run on --- src/cocoa/i_backend_cocoa.mm | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 4a029515e..dd376efb8 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1955,39 +1955,8 @@ int SDL_SetPalette(SDL_Surface* surface, int flags, SDL_Color* colors, int first #undef main #endif // main -static void CheckOSVersion() -{ - static const char* const PARAMETER_NAME = "kern.osrelease"; - - size_t size = 0; - - if (-1 == sysctlbyname(PARAMETER_NAME, NULL, &size, NULL, 0)) - { - return; - } - - char* version = static_cast(alloca(size)); - - if (-1 == sysctlbyname(PARAMETER_NAME, version, &size, NULL, 0)) - { - return; - } - - if (strcmp(version, "10.0") < 0) - { - CFOptionFlags responseFlags; - CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, - CFSTR("Unsupported version of OS X"), CFSTR("You need OS X 10.6 or higher running on Intel platform in order to play."), - NULL, NULL, NULL, &responseFlags); - - exit(EXIT_FAILURE); - } -} - int main(int argc, char** argv) { - CheckOSVersion(); - gettimeofday(&s_startTicks, NULL); for (int i = 0; i <= argc; ++i) From 660ebf2c6cf2796bec39242ed0ea8dc13d65421e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 16:53:25 +0200 Subject: [PATCH 216/313] Added auto-release pools to decrease memory fragmentation on older OS X like 10.5 or 10.6 --- src/cocoa/i_backend_cocoa.mm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index dd376efb8..089d07f0d 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1378,7 +1378,9 @@ static ApplicationController* appCtrl; - (void)processEvents:(NSTimer*)timer { ZD_UNUSED(timer); - + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + while (true) { NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask @@ -1435,6 +1437,8 @@ static ApplicationController* appCtrl; } [NSApp updateWindows]; + + [pool release]; } @@ -1513,6 +1517,8 @@ void I_SetMainWindowVisible(bool visible) bool I_SetCursor(FTexture* cursorpic) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) { s_cursor = [NSCursor arrowCursor]; @@ -1567,7 +1573,9 @@ bool I_SetCursor(FTexture* cursorpic) } [appCtrl invalidateCursorRects]; - + + [pool release]; + return true; } From c024b30e9d3c8913b0138774a9b5ea9b073e8969 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 17:13:03 +0200 Subject: [PATCH 217/313] Availability of game controller API is now determined on runtime --- src/cocoa/i_joystick.cpp | 50 +++++++++++----------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index 889c38fed..4d983176d 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -33,7 +33,7 @@ #include "m_joy.h" -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +#include #include "HID_Utilities_External.h" @@ -726,7 +726,19 @@ IOKitJoystickManager* s_joystickManager; void I_StartupJoysticks() { - s_joystickManager = new IOKitJoystickManager; + SInt32 majorVersion = 0; + SInt32 minorVersion = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + Gestalt(gestaltSystemVersionMajor, &majorVersion); + Gestalt(gestaltSystemVersionMinor, &minorVersion); +#pragma clang diagnostic pop + + if (majorVersion >= 10 && minorVersion >= 5) + { + s_joystickManager = new IOKitJoystickManager; + } } void I_ShutdownJoysticks() @@ -776,37 +788,3 @@ void I_ProcessJoysticks() 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 From a37459af2ca711d48775c6737b9fd31cacf9bd5b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 9 Nov 2014 18:00:36 +0200 Subject: [PATCH 218/313] Removed all code to support compilation using 10.4 SDK 10.5 is minimum version of OS X SDK to build. Deployment target can be 10.4 though This reverts significant part of d061adf --- src/cocoa/i_backend_cocoa.mm | 76 +----------------------------------- src/sdl/iwadpicker_cocoa.mm | 5 --- src/sdl/sdlvideo.cpp | 7 ---- 3 files changed, 1 insertion(+), 87 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 089d07f0d..5d4aac859 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -45,80 +45,6 @@ #include #include -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 - -// Missing definitions for 10.4 and earlier - -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 - #include // Avoid collision between DObject class and Objective-C @@ -679,7 +605,7 @@ void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) const NSPoint viewPos = IsHiDPISupported() ? [view convertPointToBacking:windowPos] - : [view convertPointFromBase:windowPos]; + : [view convertPoint:windowPos fromView:nil]; const CGFloat frameHeight = GetRealContentViewSize(window).height; diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 57a4ce7f4..29b0454de 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -50,11 +50,6 @@ #include #include -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 -// Missing type definition for 10.4 and earlier -typedef unsigned int NSUInteger; -#endif // prior to 10.5 - CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG); enum diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 6753169f0..c869545ab 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -550,13 +550,6 @@ void SDLFB::SetVSync (bool vsync) { // Apply vsync for native backend only (where OpenGL context is set) -#if MAC_OS_X_VERSION_MIN_REQUIRED < 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); } From b08fcbf6b3ce306e851227b57b314e1b157e8537 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 10 Nov 2014 18:37:14 +1300 Subject: [PATCH 219/313] Added compat profile for TNT MAP31 --- src/compatibility.cpp | 19 +++++++++++++++++++ wadsrc/static/compatibility.txt | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 7bd28e5d9..8a4341b76 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -82,6 +82,7 @@ enum CP_SETWALLYSCALE, CP_SETTHINGZ, CP_SETTAG, + CP_SETTHINGFLAGS, }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -318,6 +319,15 @@ void ParseCompatibility() sc.MustGetNumber(); CompatParams.Push(sc.Number); } + else if (sc.Compare("setthingflags")) + { + if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size(); + CompatParams.Push(CP_SETTHINGFLAGS); + sc.MustGetNumber(); + CompatParams.Push(sc.Number); + sc.MustGetNumber(); + CompatParams.Push(sc.Number); + } else { sc.UnGet(); @@ -540,6 +550,15 @@ void SetCompatibilityParams() i += 3; break; } + case CP_SETTHINGFLAGS: + { + if ((unsigned)CompatParams[i + 1] < MapThingsConverted.Size()) + { + MapThingsConverted[CompatParams[i + 1]].flags = CompatParams[i + 2]; + } + i += 3; + break; + } } } } diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 30c54c556..d2bb85fd2 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -389,3 +389,8 @@ B9DFF13207EACAC675C71D82624D0007 // XtheaterIII map01 { DisablePushWindowCheck } + +A53AE580A4AF2B5D0B0893F86914781E // TNT: Evilution map31 +{ + setthingflags 470 2016 +} \ No newline at end of file From 16e0f79fd7c31ab5a3b942c2899adaa2827e7fe3 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 11 Nov 2014 02:18:52 +1300 Subject: [PATCH 220/313] Fix alt-tabbed desync with demos - Fixed: Stop the game timer if the window looses focus --- src/d_net.cpp | 2 +- src/doomstat.h | 1 + src/g_game.cpp | 5 +++-- src/s_sound.cpp | 26 +++++++++----------------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 4fcd66bd4..02ca8c0c4 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -939,7 +939,7 @@ void NetUpdate (void) newtics = nowtime - gametime; gametime = nowtime; - if (newtics <= 0) // nothing new to update + if (newtics <= 0 || pauseext) // nothing new to update or window paused { GetPackets (); return; diff --git a/src/doomstat.h b/src/doomstat.h index 1559609f1..565d15bd6 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -109,6 +109,7 @@ enum EMenuState extern bool automapactive; // In AutoMap mode? extern EMenuState menuactive; // Menu overlayed? extern int paused; // Game Pause? +extern bool pauseext; extern bool viewactive; diff --git a/src/g_game.cpp b/src/g_game.cpp index af4a5411e..e419e09fc 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -141,6 +141,7 @@ gameaction_t gameaction; gamestate_t gamestate = GS_STARTUP; int paused; +bool pauseext; bool sendpause; // send a pause event next tic bool sendsave; // send a save event next tic bool sendturn180; // [RH] send a 180 degree turn next tic @@ -1152,13 +1153,13 @@ void G_Ticker () // If the user alt-tabbed away, paused gets set to -1. In this case, // we do not want to read more demo commands until paused is no // longer negative. - if (demoplayback && paused >= 0) + if (demoplayback) { G_ReadDemoTiccmd (cmd, i); } else { - memcpy (cmd, newcmd, sizeof(ticcmd_t)); + memcpy(cmd, newcmd, sizeof(ticcmd_t)); } // check for turbo cheats diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 75bd33d47..ccaebf610 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1783,21 +1783,13 @@ void S_SetSoundPaused (int state) { if (state) { - if (paused <= 0) + if (paused == 0) { S_ResumeSound(true); if (GSnd != NULL) { GSnd->SetInactive(SoundRenderer::INACTIVE_Active); } - if (!netgame -#ifdef _DEBUG - && !demoplayback -#endif - ) - { - paused = 0; - } } } else @@ -1811,16 +1803,16 @@ void S_SetSoundPaused (int state) SoundRenderer::INACTIVE_Complete : SoundRenderer::INACTIVE_Mute); } - if (!netgame -#ifdef _DEBUG - && !demoplayback -#endif - ) - { - paused = -1; - } } } + if (!netgame +#ifdef _DEBUG + && !demoplayback +#endif + ) + { + pauseext = !state; + } } //========================================================================== From ad33afb8c1c65053c8c8abecf91b3af50a1388dc Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 11 Nov 2014 02:19:24 +1300 Subject: [PATCH 221/313] Added text showing who paused a game in multiplayer --- src/d_main.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 7eb09c4e4..4fea35301 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -833,15 +833,23 @@ void D_Display () } } // draw pause pic - if (paused && menuactive == MENU_Off) + if ((paused || pauseext) && menuactive == MENU_Off) { FTexture *tex; int x; + FString pstring = "By "; tex = TexMan(gameinfo.PauseSign); x = (SCREENWIDTH - tex->GetScaledWidth() * CleanXfac)/2 + tex->GetScaledLeftOffset() * CleanXfac; screen->DrawTexture (tex, x, 4, DTA_CleanNoMove, true, TAG_DONE); + if (paused && multiplayer) + { + pstring += players[paused - 1].userinfo.GetName(); + screen->DrawText(SmallFont, CR_RED, + (screen->GetWidth() - SmallFont->StringWidth(pstring)*CleanXfac) / 2, + (tex->GetScaledHeight() * CleanYfac) + 4, pstring, DTA_CleanNoMove, true, TAG_DONE); + } } // [RH] Draw icon, if any From 936ee41e413ca408febf6111e4ead1026c8d3e9e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 10 Nov 2014 22:37:53 +0200 Subject: [PATCH 222/313] Removed executable permission from HID Utilities source files --- src/cocoa/HID_Config_Utilities.c | 0 src/cocoa/HID_Error_Handler.c | 0 src/cocoa/HID_Name_Lookup.c | 0 src/cocoa/HID_Queue_Utilities.c | 0 src/cocoa/HID_Utilities.c | 0 src/cocoa/HID_Utilities_External.h | 0 src/cocoa/IOHIDDevice_.c | 0 src/cocoa/IOHIDDevice_.h | 0 src/cocoa/IOHIDElement_.c | 0 src/cocoa/IOHIDElement_.h | 0 src/cocoa/IOHIDLib_.h | 0 src/cocoa/ImmrHIDUtilAddOn.c | 0 src/cocoa/ImmrHIDUtilAddOn.h | 0 13 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/cocoa/HID_Config_Utilities.c mode change 100755 => 100644 src/cocoa/HID_Error_Handler.c mode change 100755 => 100644 src/cocoa/HID_Name_Lookup.c mode change 100755 => 100644 src/cocoa/HID_Queue_Utilities.c mode change 100755 => 100644 src/cocoa/HID_Utilities.c mode change 100755 => 100644 src/cocoa/HID_Utilities_External.h mode change 100755 => 100644 src/cocoa/IOHIDDevice_.c mode change 100755 => 100644 src/cocoa/IOHIDDevice_.h mode change 100755 => 100644 src/cocoa/IOHIDElement_.c mode change 100755 => 100644 src/cocoa/IOHIDElement_.h mode change 100755 => 100644 src/cocoa/IOHIDLib_.h mode change 100755 => 100644 src/cocoa/ImmrHIDUtilAddOn.c mode change 100755 => 100644 src/cocoa/ImmrHIDUtilAddOn.h diff --git a/src/cocoa/HID_Config_Utilities.c b/src/cocoa/HID_Config_Utilities.c old mode 100755 new mode 100644 diff --git a/src/cocoa/HID_Error_Handler.c b/src/cocoa/HID_Error_Handler.c old mode 100755 new mode 100644 diff --git a/src/cocoa/HID_Name_Lookup.c b/src/cocoa/HID_Name_Lookup.c old mode 100755 new mode 100644 diff --git a/src/cocoa/HID_Queue_Utilities.c b/src/cocoa/HID_Queue_Utilities.c old mode 100755 new mode 100644 diff --git a/src/cocoa/HID_Utilities.c b/src/cocoa/HID_Utilities.c old mode 100755 new mode 100644 diff --git a/src/cocoa/HID_Utilities_External.h b/src/cocoa/HID_Utilities_External.h old mode 100755 new mode 100644 diff --git a/src/cocoa/IOHIDDevice_.c b/src/cocoa/IOHIDDevice_.c old mode 100755 new mode 100644 diff --git a/src/cocoa/IOHIDDevice_.h b/src/cocoa/IOHIDDevice_.h old mode 100755 new mode 100644 diff --git a/src/cocoa/IOHIDElement_.c b/src/cocoa/IOHIDElement_.c old mode 100755 new mode 100644 diff --git a/src/cocoa/IOHIDElement_.h b/src/cocoa/IOHIDElement_.h old mode 100755 new mode 100644 diff --git a/src/cocoa/IOHIDLib_.h b/src/cocoa/IOHIDLib_.h old mode 100755 new mode 100644 diff --git a/src/cocoa/ImmrHIDUtilAddOn.c b/src/cocoa/ImmrHIDUtilAddOn.c old mode 100755 new mode 100644 diff --git a/src/cocoa/ImmrHIDUtilAddOn.h b/src/cocoa/ImmrHIDUtilAddOn.h old mode 100755 new mode 100644 From d53e860d2841eed1146ee07d6346147e47e5707a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 10 Nov 2014 22:59:40 +0200 Subject: [PATCH 223/313] Moved supported by OS features checks to runtime No more compile time checks via preprocessor macro definitions This introduces dependency from Carbon framework, see SetSystemUIMode() function. It's available in 64-bit, and so, it's not deprecated --- src/cocoa/i_backend_cocoa.mm | 68 ++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 5d4aac859..3f9a727b3 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -74,6 +74,14 @@ // --------------------------------------------------------------------------- +#ifndef NSAppKitVersionNumber10_6 + +@interface NSWindow(SetStyleMask) +- (void)setStyleMask:(NSUInteger)styleMask; +@end + +#endif // !NSAppKitVersionNumber10_6 + #ifndef NSAppKitVersionNumber10_7 @interface NSView(HiDPIStubs) @@ -95,6 +103,7 @@ RenderBufferOptions rbOpts; +EXTERN_CVAR(Bool, fullscreen) EXTERN_CVAR(Bool, vid_hidpi) CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -266,7 +275,7 @@ void CheckNativeMouse() } // unnamed namespace -// from iokit_joystick.cpp +// see cocoa/i_joystick.cpp void I_ProcessJoysticks(); @@ -574,11 +583,9 @@ void ProcessKeyboardEvent(NSEvent* theEvent) bool IsHiDPISupported() { -#ifdef NSAppKitVersionNumber10_7 - return NSAppKitVersionNumber >= NSAppKitVersionNumber10_7; -#else // !NSAppKitVersionNumber10_7 - return false; -#endif // NSAppKitVersionNumber10_7 + // 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) @@ -589,7 +596,7 @@ NSSize GetRealContentViewSize(const NSWindow* const window) // 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 && NSNormalWindowLevel == [window level]) + return (vid_hidpi && !fullscreen) ? [view convertSizeToBacking:frameSize] : frameSize; } @@ -903,6 +910,15 @@ 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; @@ -910,27 +926,33 @@ static ApplicationController* appCtrl; - (void)setLevel:(NSInteger)level { -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - [super setLevel:level]; -#else // 10.5 or earlier - // 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] + 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); -#endif // 10.6 or higher + const SystemUIMode mode = LEVEL_FULLSCREEN == level + ? kUIModeAllHidden + : kUIModeNormal; + SetSystemUIMode(mode, 0); + } } - (void)setStyleMask:(NSUInteger)styleMask { -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - [super setStyleMask:styleMask]; -#else // 10.5 or earlier - [appCtrl setWindowStyleMask:styleMask]; -#endif // 10.6 or higher + if (s_fullscreenNewAPI) + { + [super setStyleMask:styleMask]; + } + else + { + [appCtrl setWindowStyleMask:styleMask]; + } } @end From e0eca1e0f5fc0f34a97e5a41c31e325e62202547 Mon Sep 17 00:00:00 2001 From: darealshinji Date: Tue, 11 Nov 2014 06:26:38 +0100 Subject: [PATCH 224/313] link output_sdl.so against SDL --- output_sdl/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/output_sdl/CMakeLists.txt b/output_sdl/CMakeLists.txt index b959a6873..289f6f48d 100644 --- a/output_sdl/CMakeLists.txt +++ b/output_sdl/CMakeLists.txt @@ -1,6 +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 ) 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 b54b18c8c581a05a2bf60b74bafe992f6865123c Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 12 Nov 2014 14:08:26 -0600 Subject: [PATCH 225/313] -Added: CAUSEPAIN. Actors which deal damage (even if 0) while having this flag can cause other actors to trigger pain chances. -Fixed: ALLOWPAIN should not trigger pain states if the damage is 0, but still allow for infighting to occur. -Fixed: an unneeded logic call was processing damage modification when it was 0 to begin with. --- src/actor.h | 1 + src/p_interaction.cpp | 57 ++++++++++++++++++++-------------- src/p_map.cpp | 2 +- src/thingdef/thingdef_data.cpp | 1 + 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/actor.h b/src/actor.h index 22f4cb776..52a50b04e 100644 --- a/src/actor.h +++ b/src/actor.h @@ -346,6 +346,7 @@ enum MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. + MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. // --- mobj.renderflags --- diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 88d95b25e..5ae456f53 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -970,13 +970,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } return -1; } - if ((target->flags2 & MF2_INVULNERABLE) && damage < TELEFRAG_DAMAGE && !(flags & DMG_FORCED)) + if ((target->flags2 & MF2_INVULNERABLE) && (damage < TELEFRAG_DAMAGE) && (!(flags & DMG_FORCED))) { // actor is invulnerable if (target->player == NULL) { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { - if (target->flags7 & MF7_ALLOWPAIN) + if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) { invulpain = true; //This returns -1 later. fakeDamage = damage; @@ -991,7 +991,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Players are optionally excluded from getting thrust by damage. if (static_cast(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { - if (target->flags7 & MF7_ALLOWPAIN) + if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) plrDontThrust = 1; else return -1; @@ -999,7 +999,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if ((target->flags7 & MF7_ALLOWPAIN) && (damage < TELEFRAG_DAMAGE)) + if (((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) && (damage < TELEFRAG_DAMAGE)) { //Intentionally do not jump to fakepain because the damage hasn't been dished out yet. //Once it's dished out, THEN we can disregard damage factors affecting pain chances. @@ -1059,7 +1059,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage == -1) { - if (target->flags7 & MF7_ALLOWPAIN) //Hold off ending the function before we can deal the pain chances. + if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) //Hold off ending the function before we can deal the pain chances. goto fakepain; return -1; } @@ -1089,7 +1089,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { goto dopain; } - else if (target->flags7 & MF7_ALLOWPAIN) + else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) goto fakepain; return -1; @@ -1099,7 +1099,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_NO_FACTOR)) { damage = FixedMul(damage, target->DamageFactor); - if (damage >= 0) + if (damage > 0) { damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); } @@ -1109,7 +1109,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { goto dopain; } - else if (target->flags7 & MF7_ALLOWPAIN) + else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) goto fakepain; return -1; @@ -1120,7 +1120,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } if (damage == -1) { - if (target->flags7 & MF7_ALLOWPAIN) + if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) goto fakepain; return -1; @@ -1243,12 +1243,16 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { // check the real player, not a voodoo doll here for invulnerability effects if ((damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || - (player->cheats & CF_GODMODE))) || - (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) + (player->cheats & CF_GODMODE))) || + (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him - if (player->mo->flags7 & MF7_ALLOWPAIN) - { + + if (((!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2)) && (!(player->mo->flags5 & MF5_NOPAIN))) && + (((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || (inflictor->flags7 & MF7_CAUSEPAIN))) + //Make sure no godmodes and NOPAIN flags are found first. + //Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN. + { invulpain = true; fakeDamage = damage; goto fakepain; @@ -1270,8 +1274,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { // If MF6_FORCEPAIN is set, make the player enter the pain state. if (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL && - (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS) && - (!(player->mo->flags2 & MF2_INVULNERABLE))) + (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS) + && (!(player->mo->flags2 & MF2_INVULNERABLE)) && (!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2))) { goto dopain; } @@ -1302,7 +1306,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. - if (((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA) && damage < TELEFRAG_DAMAGE)) && player->playerstate != PST_DEAD) + if ((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && (damage < TELEFRAG_DAMAGE)) && (player->playerstate != PST_DEAD)) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1335,7 +1339,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = newdam; if (damage <= 0) { - if (target->flags7 & MF7_ALLOWPAIN) + if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) goto fakepain; else return damage; @@ -1426,10 +1430,14 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } fakepain: //Needed so we can skip the rest of the above, but still obey the original rules. - if (target->flags7 & MF7_ALLOWPAIN && (fakeDamage != damage)) + + //CAUSEPAIN can always attempt to trigger the chances of pain. + //ALLOWPAIN can do the same, only if the (unfiltered aka fake) damage is greater than 0. + if ((((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage > 0)) + || (inflictor->flags7 & MF7_CAUSEPAIN)) && (fakeDamage != damage)) { - holdDamage = damage; - damage = fakeDamage; + holdDamage = damage; //Store the modified damage away after factors are taken into account. + damage = fakeDamage; //Retrieve the original damage. } if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) && @@ -1446,8 +1454,8 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig } } - if ((damage >= target->PainThreshold && pr_damagemobj() < painchance) || - (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN))) + if ((((damage >= target->PainThreshold)) && (pr_damagemobj() < painchance)) + || (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN))) { dopain: if (mod == NAME_Electric) @@ -1484,6 +1492,7 @@ dopain: } } } + //ALLOWPAIN and CAUSEPAIN can still trigger infighting, even if no pain state is worked out. target->reactiontime = 0; // we're awake now... if (source) { @@ -1526,9 +1535,9 @@ dopain: { return -1; //NOW we return -1! } - else if (target->flags7 & MF7_ALLOWPAIN) + else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) { - return holdDamage; + return holdDamage; //This is the calculated damage after all is said and done. } return damage; } diff --git a/src/p_map.cpp b/src/p_map.cpp index 44db5f4d8..066805d3f 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3695,7 +3695,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, // [GZ] If MF6_FORCEPAIN is set, we need to call P_DamageMobj even if damage is 0! // Note: The puff may not yet be spawned here so we must check the class defaults, not the actor. int newdam = damage; - if (damage || (puffDefaults != NULL && puffDefaults->flags6 & MF6_FORCEPAIN)) + if (damage || (puffDefaults != NULL && ((puffDefaults->flags6 & MF6_FORCEPAIN) || (puffDefaults->flags7 & MF7_CAUSEPAIN)))) { int dmgflags = DMG_INFLICTOR_IS_PUFF | pflag; // Allow MF5_PIERCEARMOR on a weapon as well. diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 347b2e01a..e8cb91239 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -246,6 +246,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7), + DEFINE_FLAG(MF7, CAUSEPAIN, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From 9e4a262c87765018c706db5648b89bc093ba5e83 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 12 Nov 2014 23:03:59 +0200 Subject: [PATCH 226/313] Enabled building with SDK 10.4 Game controllers support will disabled in this case --- src/cocoa/HID_Config_Utilities.c | 7 +++ src/cocoa/HID_Error_Handler.c | 7 +++ src/cocoa/HID_Name_Lookup.c | 7 +++ src/cocoa/HID_Queue_Utilities.c | 7 +++ src/cocoa/HID_Utilities.c | 7 +++ src/cocoa/IOHIDDevice_.c | 7 +++ src/cocoa/IOHIDElement_.c | 7 +++ src/cocoa/ImmrHIDUtilAddOn.c | 7 +++ src/cocoa/i_backend_cocoa.mm | 74 ++++++++++++++++++++++++++++++++ src/cocoa/i_joystick.cpp | 38 ++++++++++++++++ src/sdl/iwadpicker_cocoa.mm | 5 +++ src/sdl/sdlvideo.cpp | 7 +++ 12 files changed, 180 insertions(+) diff --git a/src/cocoa/HID_Config_Utilities.c b/src/cocoa/HID_Config_Utilities.c index 6cd1ad56a..18d4672d3 100644 --- a/src/cocoa/HID_Config_Utilities.c +++ b/src/cocoa/HID_Config_Utilities.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #define LOG_SCORING 0 #include // malloc @@ -917,3 +922,5 @@ int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDevic return (inHIDInfoPtr->actionCookie); } } // HIDGetElementConfig + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Error_Handler.c b/src/cocoa/HID_Error_Handler.c index 8aaca7f36..0802a2ca7 100644 --- a/src/cocoa/HID_Error_Handler.c +++ b/src/cocoa/HID_Error_Handler.c @@ -43,6 +43,11 @@ // 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 @@ -99,3 +104,5 @@ void HIDReportError(const char *strError) { } #endif // kVerboseErrors } // HIDReportError + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Name_Lookup.c b/src/cocoa/HID_Name_Lookup.c index 4e4b023bd..c9a1ca264 100644 --- a/src/cocoa/HID_Name_Lookup.c +++ b/src/cocoa/HID_Name_Lookup.c @@ -43,6 +43,11 @@ // 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" @@ -1201,3 +1206,5 @@ static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IO return (results); } // hu_AddDeviceElementToUsageXML #endif + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Queue_Utilities.c b/src/cocoa/HID_Queue_Utilities.c index 586609d7f..ab1d83667 100644 --- a/src/cocoa/HID_Queue_Utilities.c +++ b/src/cocoa/HID_Queue_Utilities.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #include "HID_Utilities_External.h" // ================================== @@ -352,3 +357,5 @@ unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHID return (false); // did not get event } /* HIDGetEvent */ + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Utilities.c b/src/cocoa/HID_Utilities.c index 2beb6ca02..3152cfdd9 100644 --- a/src/cocoa/HID_Utilities.c +++ b/src/cocoa/HID_Utilities.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //*************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #pragma mark - includes & imports //----------------------------------------------------- @@ -1059,3 +1064,5 @@ static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UIn return (refHIDMatchDictionary); } // hu_SetUpMatchingDictionary + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/IOHIDDevice_.c b/src/cocoa/IOHIDDevice_.c index ab35c21e0..30c01dc7c 100644 --- a/src/cocoa/IOHIDDevice_.c +++ b/src/cocoa/IOHIDDevice_.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #pragma mark - includes & imports //----------------------------------------------------- @@ -610,3 +615,5 @@ static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFString } // IOHIDDevice_SetPtrProperty //***************************************************** + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/IOHIDElement_.c b/src/cocoa/IOHIDElement_.c index f4d8be9eb..906d2926a 100644 --- a/src/cocoa/IOHIDElement_.c +++ b/src/cocoa/IOHIDElement_.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #pragma mark - includes & imports //----------------------------------------------------- @@ -500,3 +505,5 @@ void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKe } // IOHIDElement_SetLongProperty //***************************************************** + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/ImmrHIDUtilAddOn.c b/src/cocoa/ImmrHIDUtilAddOn.c index 07e6a31b1..4937d3687 100644 --- a/src/cocoa/ImmrHIDUtilAddOn.c +++ b/src/cocoa/ImmrHIDUtilAddOn.c @@ -43,6 +43,11 @@ // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #include #include @@ -99,3 +104,5 @@ bool FreeHIDObject(io_service_t inHIDObject) { return (kIOReturnSuccess == kr); } // FreeHIDObject + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 3f9a727b3..39b6642e2 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -45,6 +45,80 @@ #include #include +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + +// Missing definitions for 10.4 and earlier + +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 + #include // Avoid collision between DObject class and Objective-C diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index 4d983176d..bd2c59348 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -33,6 +33,10 @@ #include "m_joy.h" +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + #include #include "HID_Utilities_External.h" @@ -788,3 +792,37 @@ void I_ProcessJoysticks() 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/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 29b0454de..c3b167c09 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -50,6 +50,11 @@ #include #include +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +// Missing type definition for 10.4 and earlier +typedef unsigned int NSUInteger; +#endif // prior to 10.5 + CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG); enum diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index c869545ab..59587fc0b 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -550,6 +550,13 @@ void SDLFB::SetVSync (bool vsync) { // 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); } From fd354dbe9a9059ef539738f3abc0c5c9a5f47770 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Fri, 14 Nov 2014 12:18:46 +0100 Subject: [PATCH 227/313] - Added CPF_NOTURN flag for A_CustomPunch. --- src/thingdef/thingdef_codeptr.cpp | 14 +++++++++----- wadsrc/static/actors/constants.txt | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a34a2f8d6..a668e589b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1384,6 +1384,7 @@ enum CPF_DAGGER = 2, CPF_PULLIN = 4, CPF_NORANDOMPUFFZ = 8, + CPF_NOTURN = 16, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) @@ -1424,7 +1425,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); - // turn to face target if (linetarget) { if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) @@ -1435,10 +1435,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); } - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); + 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); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 8baf2e65c..d9da11b2d 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -175,6 +175,7 @@ const int CPF_USEAMMO = 1; const int CPF_DAGGER = 2; const int CPF_PULLIN = 4; const int CPF_NORANDOMPUFFZ = 8; +const int CPF_NOTURN = 16; // Flags for A_CustomMissile const int FPF_AIMATANGLE = 1; From ee977f94d73567aa3538b3a9b940b2711cf38aa3 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Fri, 14 Nov 2014 16:54:56 +0000 Subject: [PATCH 228/313] - Moved bot thinking logic into DBot. --- src/b_bot.cpp | 21 ++++++ src/b_bot.h | 19 +++-- src/b_func.cpp | 32 +++++++-- src/b_game.cpp | 144 +++++++++++++------------------------ src/g_game.cpp | 9 +-- src/g_level.cpp | 4 ++ src/g_shared/a_pickups.cpp | 9 ++- src/p_interaction.cpp | 13 ++-- src/p_mobj.cpp | 29 ++++---- src/p_setup.cpp | 1 - src/p_user.cpp | 7 +- src/version.h | 2 +- 12 files changed, 154 insertions(+), 136 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index dc6f2cdc4..6a088ffcb 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -30,6 +30,7 @@ DBot::DBot () void DBot::Clear () { + player = NULL; angle = 0; dest = NULL; prev = NULL; @@ -64,6 +65,10 @@ void DBot::Serialize (FArchive &arc) arc << savedyaw << savedpitch; } + else if (SaveVersion >= 4516) + { + arc << player; + } arc << angle << dest @@ -88,6 +93,22 @@ void DBot::Serialize (FArchive &arc) << oldy; } +void DBot::Tick () +{ + Super::Tick (); + + if (player->mo == NULL || bglobal.freeze) + { + return; + } + + BotThinkCycles.Clock(); + bglobal.m_Thinking = true; + bglobal.Think (player->mo, &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]); + bglobal.m_Thinking = false; + BotThinkCycles.Unclock(); +} + CVAR (Int, bot_next_color, 11, 0) CVAR (Bool, bot_observer, false, 0) diff --git a/src/b_bot.h b/src/b_bot.h index 1862faee5..7740e5e00 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -14,6 +14,7 @@ #include "d_ticcmd.h" #include "r_defs.h" #include "a_pickups.h" +#include "stats.h" #define FORWARDWALK 0x1900 #define FORWARDRUN 0x3200 @@ -89,20 +90,22 @@ public: void ClearPlayer (int playernum, bool keepTeam); //(B_Game.c) - void Main (int buf); + void Main (); void Init (); void End(); bool SpawnBot (const char *name, int color = NOCOLOR); - bool LoadBots (); - void ForgetBots (); void TryAddBot (BYTE **stream, int player); void RemoveAllBots (bool fromlist); - void DestroyAllBots (); + bool LoadBots (); + void ForgetBots (); //(B_Func.c) bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); + void StartTravel (); + void FinishTravel (); //(B_Think.c) + void Think (AActor *actor, ticcmd_t *cmd); void WhatToGet (AActor *actor, AActor *item); //(B_move.c) @@ -144,7 +147,6 @@ private: bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); //(B_Think.c) - void Think (AActor *actor, ticcmd_t *cmd); void ThinkForMove (AActor *actor, ticcmd_t *cmd); void Set_enemy (AActor *actor); @@ -155,16 +157,18 @@ protected: bool observer; //Consoleplayer is observer. }; -class DBot : public DObject +class DBot : public DThinker { - DECLARE_CLASS(DBot,DObject) + DECLARE_CLASS(DBot,DThinker) HAS_OBJECT_POINTERS public: DBot (); void Clear (); void Serialize (FArchive &arc); + void Tick (); + player_t *player; angle_t angle; // The wanted angle that the bot try to get every tic. // (used to get a smooth view movement) TObjPtr dest; // Move Destination. @@ -205,6 +209,7 @@ public: //Externs extern FCajunMaster bglobal; +extern cycle_t BotThinkCycles, BotSupportCycles; EXTERN_CVAR (Float, bot_flag_return_time) EXTERN_CVAR (Int, bot_next_color) diff --git a/src/b_func.cpp b/src/b_func.cpp index 0303de8c6..9ea03d45e 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -272,10 +272,12 @@ shootmissile: bool FCajunMaster::IsLeader (player_t *player) { - for (int count = 0; count < MAXPLAYERS; count++) + DBot *Bot; + TThinkerIterator it; + + while ((Bot = it.Next ()) != NULL) { - if (players[count].Bot != NULL - && players[count].Bot->mate == player->mo) + if (Bot->mate == player->mo) { return true; } @@ -402,7 +404,7 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) && bot != client->mo) { if (Check_LOS (bot, client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. - //if(P_CheckSight( bot, players[count].mo)) + //if(P_CheckSight(bot, players[count].mo)) { temp = P_AproxDistance (client->mo->x - bot->x, client->mo->y - bot->y); @@ -552,3 +554,25 @@ bool FCajunMaster::SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FChec actor->flags = savedFlags; return res; } + +void FCajunMaster::StartTravel () +{ + DBot *Bot; + TThinkerIterator it; + + while ((Bot = it.Next ()) != NULL) + { + Bot->ChangeStatNum (STAT_TRAVELLING); + } +} + +void FCajunMaster::FinishTravel () +{ + DBot *Bot; + TThinkerIterator it(STAT_TRAVELLING); + + while ((Bot = it.Next ()) != NULL) + { + Bot->ChangeStatNum (STAT_DEFAULT); + } +} diff --git a/src/b_game.cpp b/src/b_game.cpp index 853ba7f23..a05106a70 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -94,71 +94,46 @@ FCajunMaster::~FCajunMaster() ForgetBots(); } -//This function is called every tick (from g_game.c), -//send bots into thinking (+more). -void FCajunMaster::Main (int buf) +//This function is called every tick (from g_game.c). +void FCajunMaster::Main () { - int i; - BotThinkCycles.Reset(); - if (demoplayback) + if (demoplayback || gamestate != GS_LEVEL || consoleplayer != Net_Arbitrator) return; - if (gamestate != GS_LEVEL) - return; - - m_Thinking = true; - - //Think for bots. - if (botnum) + //Add new bots? + if (wanted_botnum > botnum && !freeze) { - BotThinkCycles.Clock(); - for (i = 0; i < MAXPLAYERS; i++) + if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) { - if (playeringame[i] && players[i].mo && !freeze && players[i].Bot != NULL) - Think (players[i].mo, &netcmds[i][buf]); + if (!SpawnBot (getspawned[spawn_tries])) + wanted_botnum--; + spawn_tries++; } - BotThinkCycles.Unclock(); + + t_join--; } - if (consoleplayer == Net_Arbitrator) + //Check if player should go observer. Or un observe + if (bot_observer && !observer && !netgame) { - //Add new bots? - if (wanted_botnum > botnum && !freeze) - { - if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) - { - if (!SpawnBot (getspawned[spawn_tries])) - wanted_botnum--; - spawn_tries++; - } - - t_join--; - } - - //Check if player should go observer. Or un observe - if (bot_observer && !observer && !netgame) - { - Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); - observer = true; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; - players[consoleplayer].mo->flags2 |= MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); - } - else if (!bot_observer && observer && !netgame) //Go back - { - Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); - observer = false; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; - players[consoleplayer].mo->flags2 &= ~MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); - } + Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); + observer = true; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; + players[consoleplayer].mo->flags2 |= MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); + } + else if (!bot_observer && observer && !netgame) //Go back + { + Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); + observer = false; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; + players[consoleplayer].mo->flags2 &= ~MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); } - - m_Thinking = false; } void FCajunMaster::Init () @@ -195,22 +170,18 @@ void FCajunMaster::Init () //Called on each level exit (from g_game.c). void FCajunMaster::End () { - int i; - //Arrange wanted botnum and their names, so they can be spawned next level. getspawned.Clear(); - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL) - { - if (deathmatch) - { - getspawned.Push(players[i].userinfo.GetName()); - } - } - } if (deathmatch) { + DBot *Bot; + TThinkerIterator it; + + while ((Bot = it.Next ()) != NULL) + { + getspawned.Push(Bot->player->userinfo.GetName()); + } + wanted_botnum = botnum; } } @@ -400,7 +371,7 @@ bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill) multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). players[bnum].Bot = new DBot; - GC::WriteBarrier (players[bnum].Bot); + players[bnum].Bot->player = &players[bnum]; players[bnum].Bot->skill = skill; playeringame[bnum] = true; players[bnum].mo = NULL; @@ -422,31 +393,30 @@ bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill) void FCajunMaster::RemoveAllBots (bool fromlist) { - int i, j; + DBot *Bot; + TThinkerIterator it; + int i; - for (i = 0; i < MAXPLAYERS; ++i) + while ((Bot = it.Next ()) != NULL) { - if (playeringame[i] && players[i].Bot != NULL) + // If a player is looking through this bot's eyes, make him + // look through his own eyes instead. + for (i = 0; i < MAXPLAYERS; ++i) { - // If a player is looking through this bot's eyes, make him - // look through his own eyes instead. - for (j = 0; j < MAXPLAYERS; ++j) + if (Bot->player != &players[i] && playeringame[i] && players[i].Bot == NULL) { - if (i != j && playeringame[j] && players[j].Bot == NULL) + if (players[i].camera == Bot->player->mo) { - if (players[j].camera == players[i].mo) + players[i].camera = players[i].mo; + if (i == consoleplayer) { - players[j].camera = players[j].mo; - if (j == consoleplayer) - { - StatusBar->AttachToPlayer (players + j); - } + StatusBar->AttachToPlayer (players + i); } } } - ClearPlayer (i, !fromlist); - FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, i); } + ClearPlayer (Bot->player - players, !fromlist); + FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, Bot->player - players); } if (fromlist) @@ -456,18 +426,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist) botnum = 0; } -void FCajunMaster::DestroyAllBots () -{ - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (players[i].Bot != NULL) - { - players[i].Bot->Destroy (); - players[i].Bot = NULL; - } - } -} - //------------------ //Reads data for bot from diff --git a/src/g_game.cpp b/src/g_game.cpp index af4a5411e..d56c58910 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1127,11 +1127,8 @@ void G_Ticker () // check, not just the player's x position like BOOM. DWORD rngsum = FRandom::StaticSumSeeds (); - if ((gametic % ticdup) == 0) - { - //Added by MC: For some of that bot stuff. The main bot function. - bglobal.Main (buf); - } + //Added by MC: For some of that bot stuff. The main bot function. + bglobal.Main (); for (i = 0; i < MAXPLAYERS; i++) { @@ -1394,7 +1391,6 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { - // [GRB] Give inventory specified in DECORATE actor->GiveDefaultInventory (); p->ReadyWeapon = p->PendingWeapon; @@ -1405,6 +1401,7 @@ void G_PlayerReborn (int player) { botskill_t skill = p->Bot->skill; p->Bot->Clear (); + p->Bot->player = p; p->Bot->skill = skill; } } diff --git a/src/g_level.cpp b/src/g_level.cpp index 2a2d6471a..761ea4f48 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1164,6 +1164,8 @@ void G_StartTravel () } } } + + bglobal.StartTravel (); } //========================================================================== @@ -1261,6 +1263,8 @@ void G_FinishTravel () } } } + + bglobal.FinishTravel (); } //========================================================================== diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index fc3e863a2..f5c262d8c 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1021,11 +1021,14 @@ void AInventory::Touch (AActor *toucher) P_GiveSecret(toucher, true, true, -1); } + DBot *Bot; + TThinkerIterator it; + //Added by MC: Check if item taken was the roam destination of any bot - for (int i = 0; i < MAXPLAYERS; i++) + while ((Bot = it.Next ()) != NULL) { - if (playeringame[i] && players[i].Bot != NULL && this == players[i].Bot->dest) - players[i].Bot->dest = NULL; + if (Bot->dest == this) + Bot->dest = NULL; } } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 88d95b25e..88cc89668 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -611,14 +611,17 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) if (player->Bot != NULL) player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; + DBot *Bot; + TThinkerIterator it; + //Added by MC: Discard enemies. - for (int i = 0; i < MAXPLAYERS; i++) + while ((Bot = it.Next ()) != NULL) { - if (players[i].Bot != NULL && this == players[i].Bot->enemy) + if (this == Bot->enemy) { - if (players[i].Bot->dest == players[i].Bot->enemy) - players[i].Bot->dest = NULL; - players[i].Bot->enemy = NULL; + if (Bot->dest == Bot->enemy) + Bot->dest = NULL; + Bot->enemy = NULL; } } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c7bfcea21..27bc50a83 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -84,7 +84,6 @@ static void PlayerLandedOnThing (AActor *mo, AActor *onmobj); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern cycle_t BotSupportCycles; extern int BotWTG; EXTERN_CVAR (Int, cl_rockettrails) @@ -3043,7 +3042,6 @@ void AActor::Tick () AActor *onmo; - int i; //assert (state != NULL); if (state == NULL) @@ -3233,35 +3231,36 @@ void AActor::Tick () { BotSupportCycles.Clock(); bglobal.m_Thinking = true; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].Bot == NULL) - continue; + DBot *Bot; + TThinkerIterator it; + + while ((Bot = it.Next ()) != NULL) + { if (flags3 & MF3_ISMONSTER) { if (health > 0 - && !players[i].Bot->enemy - && player ? !IsTeammate (players[i].mo) : true - && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST - && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) + && !Bot->enemy + && player ? !IsTeammate (Bot->player->mo) : true + && P_AproxDistance (Bot->player->mo->x-x, Bot->player->mo->y-y) < MAX_MONSTER_TARGET_DIST + && P_CheckSight (Bot->player->mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. - players[i].Bot->enemy = this; + Bot->enemy = this; } } else if (flags & MF_SPECIAL) { //Item pickup time //clock (BotWTG); - bglobal.WhatToGet (players[i].mo, this); + bglobal.WhatToGet (Bot->player->mo, this); //unclock (BotWTG); BotWTG++; } else if (flags & MF_MISSILE) { - if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) + if (!Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. - if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) - players[i].Bot->missile = this; + if (target != Bot->player->mo && bglobal.Check_LOS (Bot->player->mo, this, ANGLE_90)) + Bot->missile = this; } } } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 53b288798..6b5d9403f 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4175,7 +4175,6 @@ static void P_Shutdown () P_FreeLevelData (); P_FreeExtraLevelData (); ST_Clear(); - bglobal.DestroyAllBots (); } #if 0 diff --git a/src/p_user.cpp b/src/p_user.cpp index 72b92b7d1..bf656d2b9 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3066,7 +3066,6 @@ void player_t::Serialize (FArchive &arc) if (SaveVersion < 4514 && IsBot) { Bot = new DBot; - GC::WriteBarrier (Bot); arc << Bot->angle << Bot->dest @@ -3089,6 +3088,12 @@ void player_t::Serialize (FArchive &arc) << Bot->oldx << Bot->oldy; } + + if (SaveVersion < 4516 && Bot != NULL) + { + Bot->player = this; + } + if (arc.IsLoading ()) { // If the player reloaded because they pressed +use after dying, we diff --git a/src/version.h b/src/version.h index f091f416a..15d01d315 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 4515 +#define SAVEVER 4516 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From e38aee070ce3dcbc394ffc44cf52d6f0fa5fd6cb Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sat, 15 Nov 2014 08:58:29 +0000 Subject: [PATCH 229/313] - Changed TThinkerIterator loops back to MAXPLAYERS loops. - Added STAT_BOT. --- src/b_bot.cpp | 1 + src/b_func.cpp | 28 +++++++++++++--------------- src/b_game.cpp | 38 +++++++++++++++++++------------------- src/g_shared/a_pickups.cpp | 9 +++------ src/p_interaction.cpp | 13 +++++-------- src/p_mobj.cpp | 28 ++++++++++++++-------------- src/statnums.h | 1 + 7 files changed, 56 insertions(+), 62 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 6a088ffcb..d2acdcab6 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -24,6 +24,7 @@ IMPLEMENT_POINTY_CLASS(DBot) END_POINTERS DBot::DBot () +: DThinker(STAT_BOT) { Clear (); } diff --git a/src/b_func.cpp b/src/b_func.cpp index 9ea03d45e..349155a81 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -272,12 +272,10 @@ shootmissile: bool FCajunMaster::IsLeader (player_t *player) { - DBot *Bot; - TThinkerIterator it; - - while ((Bot = it.Next ()) != NULL) + for (int count = 0; count < MAXPLAYERS; count++) { - if (Bot->mate == player->mo) + if (players[count].Bot != NULL + && players[count].Bot->mate == player->mo) { return true; } @@ -557,22 +555,22 @@ bool FCajunMaster::SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FChec void FCajunMaster::StartTravel () { - DBot *Bot; - TThinkerIterator it; - - while ((Bot = it.Next ()) != NULL) + for (int i = 0; i < MAXPLAYERS; ++i) { - Bot->ChangeStatNum (STAT_TRAVELLING); + if (players[i].Bot != NULL) + { + players[i].Bot->ChangeStatNum (STAT_TRAVELLING); + } } } void FCajunMaster::FinishTravel () { - DBot *Bot; - TThinkerIterator it(STAT_TRAVELLING); - - while ((Bot = it.Next ()) != NULL) + for (int i = 0; i < MAXPLAYERS; ++i) { - Bot->ChangeStatNum (STAT_DEFAULT); + if (players[i].Bot != NULL) + { + players[i].Bot->ChangeStatNum (STAT_BOT); + } } } diff --git a/src/b_game.cpp b/src/b_game.cpp index a05106a70..44aff03fa 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -170,16 +170,15 @@ void FCajunMaster::Init () //Called on each level exit (from g_game.c). void FCajunMaster::End () { + int i; + //Arrange wanted botnum and their names, so they can be spawned next level. getspawned.Clear(); if (deathmatch) { - DBot *Bot; - TThinkerIterator it; - - while ((Bot = it.Next ()) != NULL) + for (i = 0; i < MAXPLAYERS; i++) { - getspawned.Push(Bot->player->userinfo.GetName()); + getspawned.Push(players[i].userinfo.GetName()); } wanted_botnum = botnum; @@ -393,30 +392,31 @@ bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill) void FCajunMaster::RemoveAllBots (bool fromlist) { - DBot *Bot; - TThinkerIterator it; - int i; + int i, j; - while ((Bot = it.Next ()) != NULL) + for (i = 0; i < MAXPLAYERS; ++i) { - // If a player is looking through this bot's eyes, make him - // look through his own eyes instead. - for (i = 0; i < MAXPLAYERS; ++i) + if (players[i].Bot != NULL) { - if (Bot->player != &players[i] && playeringame[i] && players[i].Bot == NULL) + // If a player is looking through this bot's eyes, make him + // look through his own eyes instead. + for (j = 0; j < MAXPLAYERS; ++j) { - if (players[i].camera == Bot->player->mo) + if (i != j && playeringame[j] && players[j].Bot == NULL) { - players[i].camera = players[i].mo; - if (i == consoleplayer) + if (players[j].camera == players[i].mo) { - StatusBar->AttachToPlayer (players + i); + players[j].camera = players[j].mo; + if (j == consoleplayer) + { + StatusBar->AttachToPlayer (players + j); + } } } } + ClearPlayer (i, !fromlist); + FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, i); } - ClearPlayer (Bot->player - players, !fromlist); - FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, Bot->player - players); } if (fromlist) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index f5c262d8c..1aab998a9 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1021,14 +1021,11 @@ void AInventory::Touch (AActor *toucher) P_GiveSecret(toucher, true, true, -1); } - DBot *Bot; - TThinkerIterator it; - //Added by MC: Check if item taken was the roam destination of any bot - while ((Bot = it.Next ()) != NULL) + for (int i = 0; i < MAXPLAYERS; i++) { - if (Bot->dest == this) - Bot->dest = NULL; + if (players[i].Bot != NULL && this == players[i].Bot->dest) + players[i].Bot->dest = NULL; } } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 88cc89668..88d95b25e 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -611,17 +611,14 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) if (player->Bot != NULL) player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; - DBot *Bot; - TThinkerIterator it; - //Added by MC: Discard enemies. - while ((Bot = it.Next ()) != NULL) + for (int i = 0; i < MAXPLAYERS; i++) { - if (this == Bot->enemy) + if (players[i].Bot != NULL && this == players[i].Bot->enemy) { - if (Bot->dest == Bot->enemy) - Bot->dest = NULL; - Bot->enemy = NULL; + if (players[i].Bot->dest == players[i].Bot->enemy) + players[i].Bot->dest = NULL; + players[i].Bot->enemy = NULL; } } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 27bc50a83..4ef075720 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3042,6 +3042,7 @@ void AActor::Tick () AActor *onmo; + int i; //assert (state != NULL); if (state == NULL) @@ -3231,36 +3232,35 @@ void AActor::Tick () { BotSupportCycles.Clock(); bglobal.m_Thinking = true; - - DBot *Bot; - TThinkerIterator it; - - while ((Bot = it.Next ()) != NULL) + for (i = 0; i < MAXPLAYERS; i++) { + if (!playeringame[i] || players[i].Bot == NULL) + continue; + if (flags3 & MF3_ISMONSTER) { if (health > 0 - && !Bot->enemy - && player ? !IsTeammate (Bot->player->mo) : true - && P_AproxDistance (Bot->player->mo->x-x, Bot->player->mo->y-y) < MAX_MONSTER_TARGET_DIST - && P_CheckSight (Bot->player->mo, this, SF_SEEPASTBLOCKEVERYTHING)) + && !players[i].Bot->enemy + && player ? !IsTeammate (players[i].mo) : true + && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST + && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. - Bot->enemy = this; + players[i].Bot->enemy = this; } } else if (flags & MF_SPECIAL) { //Item pickup time //clock (BotWTG); - bglobal.WhatToGet (Bot->player->mo, this); + bglobal.WhatToGet (players[i].mo, this); //unclock (BotWTG); BotWTG++; } else if (flags & MF_MISSILE) { - if (!Bot->missile && (flags3 & MF3_WARNBOT)) + if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. - if (target != Bot->player->mo && bglobal.Check_LOS (Bot->player->mo, this, ANGLE_90)) - Bot->missile = this; + if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) + players[i].Bot->missile = this; } } } diff --git a/src/statnums.h b/src/statnums.h index 344a328c8..25f644d1d 100644 --- a/src/statnums.h +++ b/src/statnums.h @@ -63,6 +63,7 @@ enum STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement STAT_ACTORMOVER, // actor movers STAT_SCRIPTS, // The ACS thinker. This is to ensure that it can't tick before all actors called PostBeginPlay + STAT_BOT, // Bot thinker }; #endif \ No newline at end of file From 4b5d7361cd9389f24506e562b563fcc3f5298783 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Nov 2014 11:47:05 +0200 Subject: [PATCH 230/313] Fixed exiting when window is closed by clicking close button This happened on 10.6 and later when fullscreen mode was used before windowed --- src/cocoa/i_backend_cocoa.mm | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 39b6642e2..3d24fe8b5 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1163,6 +1163,8 @@ static bool s_fullscreenNewAPI; // Hide window as nothing will be rendered at this point [m_window orderOut:nil]; + + I_ShutdownJoysticks(); } @@ -1187,10 +1189,6 @@ static bool s_fullscreenNewAPI; [window makeFirstResponder:self]; [window setAcceptsMouseMovedEvents:YES]; - NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton]; - [closeButton setAction:@selector(closeWindow:)]; - [closeButton setTarget:self]; - return window; } @@ -1297,6 +1295,10 @@ static bool s_fullscreenNewAPI; [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 @@ -1496,14 +1498,6 @@ static bool s_fullscreenNewAPI; m_window = tempWindow; } - -- (void)closeWindow:(id)sender -{ - I_ShutdownJoysticks(); - - [NSApp terminate:sender]; -} - @end From b63bd56da8bb27ad32a36eb1ecc522f60b2677ac Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Nov 2014 13:32:37 +0200 Subject: [PATCH 231/313] Removed .xib file and, therefore, dependency from ibtool Menu is now created in code --- src/CMakeLists.txt | 15 +- src/cocoa/i_backend_cocoa.mm | 108 ++++- src/cocoa/zdoom.xib | 874 ----------------------------------- 3 files changed, 108 insertions(+), 889 deletions(-) delete mode 100644 src/cocoa/zdoom.xib diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef252fc3a..af323f5b3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -594,20 +594,7 @@ set( PLAT_COCOA_SOURCES if( APPLE ) if( OSX_COCOA_BACKEND ) - find_program( IBTOOL ibtool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) - if( ${IBTOOL} STREQUAL "IBTOOL-NOTFOUND" ) - message( SEND_ERROR "ibtool can not be found to compile xib files." ) - endif( ${IBTOOL} STREQUAL "IBTOOL-NOTFOUND" ) - - set( NIB_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/zdoom.nib" ) - add_custom_command( OUTPUT "${NIB_FILE}" - COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text - --compile "${NIB_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/zdoom.xib" - COMMENT "Compiling zdoom.xib" ) - - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_COCOA_SOURCES} "${NIB_FILE}" "${FMOD_LIBRARY}" ) - - set_source_files_properties( "${NIB_FILE}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_COCOA_SOURCES} "${FMOD_LIBRARY}" ) else( OSX_COCOA_BACKEND ) set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} "${FMOD_LIBRARY}" ) set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/SDLMain.m ) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 3d24fe8b5..05ee3a06e 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1975,6 +1975,107 @@ int SDL_SetPalette(SDL_Surface* surface, int flags, SDL_Color* colors, int first } // extern "C" + +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]; + + 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]; +} + +} // unnamed namespace + + #ifdef main #undef main #endif // main @@ -2006,7 +2107,12 @@ int main(int argc, char** argv) NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; - [NSBundle loadNibNamed:@"zdoom" owner:NSApp]; + + // The following line isn't mandatory + // but it enables to run without application bundle + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + CreateMenu(); appCtrl = [ApplicationController new]; [NSApp setDelegate:appCtrl]; diff --git a/src/cocoa/zdoom.xib b/src/cocoa/zdoom.xib deleted file mode 100644 index 56099dc4b..000000000 --- a/src/cocoa/zdoom.xib +++ /dev/null @@ -1,874 +0,0 @@ - - - - 1060 - 11C74 - 851 - 1138.23 - 567.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 851 - - - YES - - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - YES - - NSApplication - - - FirstResponder - - - NSApplication - - - NSFontManager - - - Main Menu - - YES - - - ZDoom - - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - ZDoom - - YES - - - About ZDoom - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Preferencesā€¦ - , - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Services - - 2147483647 - - - submenuAction: - - Services - - YES - - _NSServicesMenu - - - - - YES - YES - - - 2147483647 - - - - - - Hide ZDoom - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Quit ZDoom - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - Edit - - 2147483647 - - - submenuAction: - - Edit - - YES - - - Undo - z - 1048576 - 2147483647 - - - - - - Redo - Z - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Cut - x - 1048576 - 2147483647 - - - - - - Copy - c - 1048576 - 2147483647 - - - - - - Paste - v - 1048576 - 2147483647 - - - - - - Delete - - 2147483647 - - - - - - Select All - a - 1048576 - 2147483647 - - - - - - - - - Window - - 2147483647 - - - submenuAction: - - Window - - YES - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Bring All to Front - - 2147483647 - - - - - _NSWindowsMenu - - - - _NSMainMenu - - - - - YES - - - performMiniaturize: - - - - 37 - - - - arrangeInFront: - - - - 39 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - performZoom: - - - - 240 - - - - hide: - - - - 367 - - - - hideOtherApplications: - - - - 368 - - - - terminate: - - - - 369 - - - - unhideAllApplications: - - - - 370 - - - - cut: - - - - 738 - - - - paste: - - - - 739 - - - - redo: - - - - 742 - - - - undo: - - - - 746 - - - - copy: - - - - 752 - - - - delete: - - - - 753 - - - - selectAll: - - - - 755 - - - - - YES - - 0 - - YES - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - YES - - - - - - - - 19 - - - YES - - - - - - 56 - - - YES - - - - - - 57 - - - YES - - - - - - - - - - - - - - - - 58 - - - - - 134 - - - - - 150 - - - - - 136 - - - - - 144 - - - - - 129 - - - - - 143 - - - - - 236 - - - - - 131 - - - YES - - - - - - 149 - - - - - 145 - - - - - 130 - - - - - 24 - - - YES - - - - - - - - - 92 - - - - - 5 - - - - - 239 - - - - - 23 - - - - - 371 - - - - - 681 - - - YES - - - - - - 682 - - - YES - - - - - - - - - - - - - 683 - - - - - 684 - - - - - 685 - - - - - 686 - - - - - 687 - - - - - 688 - - - - - 690 - - - - - 691 - - - - - - - YES - - YES - -3.IBPluginDependency - 129.IBPluginDependency - 129.ImportedFromIB2 - 130.IBPluginDependency - 130.ImportedFromIB2 - 130.editorWindowContentRectSynchronizationRect - 131.IBPluginDependency - 131.ImportedFromIB2 - 134.IBPluginDependency - 134.ImportedFromIB2 - 136.IBPluginDependency - 136.ImportedFromIB2 - 143.IBPluginDependency - 143.ImportedFromIB2 - 144.IBPluginDependency - 144.ImportedFromIB2 - 145.IBPluginDependency - 145.ImportedFromIB2 - 149.IBPluginDependency - 149.ImportedFromIB2 - 150.IBPluginDependency - 150.ImportedFromIB2 - 19.IBPluginDependency - 19.ImportedFromIB2 - 23.IBPluginDependency - 23.ImportedFromIB2 - 236.IBPluginDependency - 236.ImportedFromIB2 - 239.IBPluginDependency - 239.ImportedFromIB2 - 24.IBEditorWindowLastContentRect - 24.IBPluginDependency - 24.ImportedFromIB2 - 24.editorWindowContentRectSynchronizationRect - 29.IBEditorWindowLastContentRect - 29.IBPluginDependency - 29.ImportedFromIB2 - 29.WindowOrigin - 29.editorWindowContentRectSynchronizationRect - 5.IBPluginDependency - 5.ImportedFromIB2 - 56.IBPluginDependency - 56.ImportedFromIB2 - 57.IBEditorWindowLastContentRect - 57.IBPluginDependency - 57.ImportedFromIB2 - 57.editorWindowContentRectSynchronizationRect - 58.IBPluginDependency - 58.ImportedFromIB2 - 681.IBPluginDependency - 682.IBEditorWindowLastContentRect - 682.IBPluginDependency - 683.IBPluginDependency - 684.IBPluginDependency - 685.IBPluginDependency - 686.IBPluginDependency - 687.IBPluginDependency - 688.IBPluginDependency - 690.IBPluginDependency - 691.IBPluginDependency - 92.IBPluginDependency - 92.ImportedFromIB2 - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{436, 809}, {64, 6}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{651, 262}, {194, 73}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{525, 802}, {197, 73}} - {{514, 335}, {220, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {74, 862} - {{11, 977}, {478, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{487, 217}, {195, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{23, 794}, {245, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - {{607, 182}, {151, 153}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - YES - - - YES - - - - - YES - - - YES - - - - 842 - - - - YES - - NSObject - - IBFrameworkSource - Print.framework/Headers/PDEPluginInterface.h - - - - - 0 - IBCocoaFramework - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - ZDoom.xcodeproj - 3 - - YES - - YES - NSMenuCheckmark - NSMenuMixedState - - - YES - {9, 8} - {7, 2} - - - - From 21e7beb21b6c5ed475fd26e603c3bdb445a7cc95 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 16 Nov 2014 02:39:16 +1300 Subject: [PATCH 232/313] Fixed missing NULL pointer checks --- src/p_interaction.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 5ae456f53..0068f091a 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -991,7 +991,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Players are optionally excluded from getting thrust by damage. if (static_cast(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) plrDontThrust = 1; else return -1; @@ -999,7 +999,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) && (damage < TELEFRAG_DAMAGE)) + if (((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (damage < TELEFRAG_DAMAGE)) { //Intentionally do not jump to fakepain because the damage hasn't been dished out yet. //Once it's dished out, THEN we can disregard damage factors affecting pain chances. @@ -1089,7 +1089,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { goto dopain; } - else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) goto fakepain; return -1; @@ -1109,7 +1109,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { goto dopain; } - else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) goto fakepain; return -1; @@ -1120,7 +1120,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } if (damage == -1) { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) goto fakepain; return -1; @@ -1434,7 +1434,7 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig //CAUSEPAIN can always attempt to trigger the chances of pain. //ALLOWPAIN can do the same, only if the (unfiltered aka fake) damage is greater than 0. if ((((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage > 0)) - || (inflictor->flags7 & MF7_CAUSEPAIN)) && (fakeDamage != damage)) + || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (fakeDamage != damage)) { holdDamage = damage; //Store the modified damage away after factors are taken into account. damage = fakeDamage; //Retrieve the original damage. @@ -1535,7 +1535,7 @@ dopain: { return -1; //NOW we return -1! } - else if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) { return holdDamage; //This is the calculated damage after all is said and done. } From 2d5cbe1d8b166a0ed8e929a5a647e957c5f9eacb Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 16 Nov 2014 02:44:17 +1300 Subject: [PATCH 233/313] Missed a save --- src/p_interaction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 0068f091a..7c88eb516 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -976,7 +976,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) { invulpain = true; //This returns -1 later. fakeDamage = damage; @@ -1249,7 +1249,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { // player is invulnerable, so don't hurt him if (((!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2)) && (!(player->mo->flags5 & MF5_NOPAIN))) && - (((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || (inflictor->flags7 & MF7_CAUSEPAIN))) + (((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) //Make sure no godmodes and NOPAIN flags are found first. //Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN. { @@ -1339,7 +1339,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = newdam; if (damage <= 0) { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) goto fakepain; else return damage; From 92143f90fa7bfac8be7a6e0c145161da4f4e4d03 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 16 Nov 2014 02:59:37 +1300 Subject: [PATCH 234/313] Yet another pointer --- src/p_interaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 7c88eb516..4ee86d82c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1370,7 +1370,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { - if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && (!(inflictor->flags3 & MF7_FOILBUDDHA) && !(flags & DMG_FOILBUDDHA))) + if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && ((inflictor == NULL || !(inflictor->flags3 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA))) { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; } From cc8d8928ae416c846a9f077b0c115518dcf9ee3e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Nov 2014 17:07:27 +0200 Subject: [PATCH 235/313] Removed explicit set of activation policy for application Bundle is used anyway but this method requires 10.6 or higher --- src/cocoa/i_backend_cocoa.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 05ee3a06e..b30ab1ff0 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -2108,10 +2108,6 @@ int main(int argc, char** argv) [NSApplication sharedApplication]; - // The following line isn't mandatory - // but it enables to run without application bundle - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - CreateMenu(); appCtrl = [ApplicationController new]; From 398b36064b44084a0dbab99d143e2a4ab38e546e Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Nov 2014 17:27:23 +0200 Subject: [PATCH 236/313] Added usage of OpenGL client storage extension This improves performance significantly of 10.4 --- src/cocoa/i_backend_cocoa.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index b30ab1ff0..22ed23254 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1375,6 +1375,7 @@ static bool s_fullscreenNewAPI; 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); From 65ce06f274a2ee5ed19cf010aec5d7527666440a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Nov 2014 19:11:01 +0200 Subject: [PATCH 237/313] Added ability to run without application bundle You just need executable, zdoom.pk3 and IWAD --- src/cocoa/i_backend_cocoa.mm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 22ed23254..05aacd6c8 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -119,6 +119,21 @@ enum #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 + +#endif // prior to 10.6 + #include // Avoid collision between DObject class and Objective-C @@ -2109,6 +2124,13 @@ int main(int argc, char** argv) [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(); appCtrl = [ApplicationController new]; From 9ccb839ae2c34399417b2dd2ab7c29dc0aff3859 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 16 Nov 2014 16:23:15 +1300 Subject: [PATCH 238/313] Fix slope inconsistency for 64bit - Fixed: Slope logic would change between 32bit and 64bit due to float math. --- src/p_map.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 066805d3f..b406dff7b 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -736,11 +736,9 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) else { // Find the point on the line closest to the actor's center, and use // that to calculate openings - float dx = (float)ld->dx; - float dy = (float)ld->dy; - fixed_t r = (fixed_t)(((float)(tm.x - ld->v1->x) * dx + - (float)(tm.y - ld->v1->y) * dy) / - (dx*dx + dy*dy) * 16777216.f); + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); + SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + fixed_t r = (fixed_t)(r_num / r_den); /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, ld->frontsector->floorplane.b, From bcdf22b910b69ca66a203a8f96fe54fc8df5cca0 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 16 Nov 2014 09:30:57 +0200 Subject: [PATCH 239/313] Fixed potential crash in word expansion API 10.5.8 PPC crashed 100%, some versions of 10.6 i386 were affected too --- src/sdl/iwadpicker_cocoa.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index c3b167c09..d1364fa81 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -428,7 +428,7 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) [arguments addObject:currentParameter]; } - wordexp_t expansion; + wordexp_t expansion = {}; if (0 == wordexp([parameters UTF8String], &expansion, 0)) { From 93a732f0cab3d95d23ec99712b13ffbf8bf67ee9 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 16 Nov 2014 10:25:25 +0200 Subject: [PATCH 240/313] Fixed empty application menu on 10.5 and earlier --- src/cocoa/i_backend_cocoa.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 05aacd6c8..81c487cc7 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -2021,6 +2021,11 @@ NSMenuItem* CreateApplicationMenu() NSMenuItem* menuItem = [NSMenuItem new]; [menuItem setSubmenu:menu]; + if ([NSApp respondsToSelector:@selector(setAppleMenu:)]) + { + [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; + } + return menuItem; } From fd85e116e54a91553e243004d208cf2411031957 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 16 Nov 2014 10:29:03 +0200 Subject: [PATCH 241/313] Fixed another potential crash in word expansion API Calling wordfree() may lead to a crash when wordexp_t instance is not zero-initialized This happened usually on older OS X like 10.5 or 10.6 --- src/sound/music_midi_timidity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 4acda49b9..38ba8557e 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -433,7 +433,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity () } int forkres; - wordexp_t words; + wordexp_t words = {}; switch (wordexp (CommandLine.GetChars(), &words, 0)) { From 4b2af7074e038ae442e8cb8a56858ecd90cfadd2 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sun, 16 Nov 2014 11:29:16 +0100 Subject: [PATCH 242/313] - Improve the 64 bit incompatibility patch. - Reorder the operands. First the numerator, then the denominator :) . - Add a comment regarding the motivations for this change. --- src/p_map.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index b406dff7b..c0e5c3baa 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -736,8 +736,18 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) else { // Find the point on the line closest to the actor's center, and use // that to calculate openings - SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); + // [EP] Use 64 bit integers in order to keep the exact result of the + // multiplication, because in the worst case, which is by the map limit + // (32767 units, which is 2147418112 in fixed_t notation), the result + // would occupy 62 bits (if I consider also the addition with another + // and possible 62 bit value, it's 63 bits). + // This privilege could not be available if the starting data would be + // 64 bit long. + // With this, the division is exact as the 32 bit float counterpart, + // though I don't know why I had to discard the first 24 bits from the + // divisor. SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); fixed_t r = (fixed_t)(r_num / r_den); /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, From 1bda54f3cd8fc97c6a1624216dc2f9d8a21a31d4 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 16 Nov 2014 13:15:56 +0200 Subject: [PATCH 243/313] Fixed a few endian issues in ACS system KDiZD Intro, a.k.a. Title Map, is now played correctly; camera on Z1M1 is functional --- src/p_acs.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 03de8b3df..0a8eef879 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1873,7 +1873,7 @@ FBehavior::FBehavior (int lumpnum, FileReader * fr, int len) funcm->HasReturnValue = funcf->HasReturnValue; funcm->ImportNum = funcf->ImportNum; funcm->LocalCount = funcf->LocalCount; - funcm->Address = funcf->Address; + funcm->Address = LittleLong(funcf->Address); } } @@ -2058,7 +2058,7 @@ FBehavior::FBehavior (int lumpnum, FileReader * fr, int len) const char *const parse = (char *)&chunk[2]; DWORD i; - for (i = 0; i < chunk[1]; ) + for (i = 0; i < LittleLong(chunk[1]); ) { if (parse[i]) { @@ -2351,7 +2351,7 @@ void FBehavior::LoadScriptsDirectory () scripts.b = FindChunk (MAKE_ID('S','F','L','G')); if (scripts.dw != NULL) { - max = scripts.dw[1] / 4; + max = LittleLong(scripts.dw[1]) / 4; scripts.dw += 2; for (i = max; i > 0; --i, scripts.w += 2) { @@ -2367,7 +2367,7 @@ void FBehavior::LoadScriptsDirectory () scripts.b = FindChunk (MAKE_ID('S','V','C','T')); if (scripts.dw != NULL) { - max = scripts.dw[1] / 4; + max = LittleLong(scripts.dw[1]) / 4; scripts.dw += 2; for (i = max; i > 0; --i, scripts.w += 2) { @@ -2681,7 +2681,7 @@ BYTE *FBehavior::FindChunk (DWORD id) const { return chunk; } - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; } return NULL; } @@ -2689,14 +2689,14 @@ BYTE *FBehavior::FindChunk (DWORD id) const BYTE *FBehavior::NextChunk (BYTE *chunk) const { DWORD id = *(DWORD *)chunk; - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; while (chunk != NULL && chunk < Data + DataSize) { if (((DWORD *)chunk)[0] == id) { return chunk; } - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; } return NULL; } From 64e34d883b74db7a3868cd81ba9556dac4ce9138 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 17 Nov 2014 14:54:46 +0200 Subject: [PATCH 244/313] Fixed build on UNIX systems other than OS X --- src/CMakeLists.txt | 9 ++++++--- src/sdl/i_timer.cpp | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index af323f5b3..fb1eab35a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -593,15 +593,18 @@ set( PLAT_COCOA_SOURCES cocoa/zdoom.icns ) if( APPLE ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} "${FMOD_LIBRARY}" ) + if( OSX_COCOA_BACKEND ) - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_COCOA_SOURCES} "${FMOD_LIBRARY}" ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) else( OSX_COCOA_BACKEND ) - set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} "${FMOD_LIBRARY}" ) - set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} sdl/SDLMain.m ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} sdl/SDLMain.m ) 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 ) diff --git a/src/sdl/i_timer.cpp b/src/sdl/i_timer.cpp index 1b73edd5b..e3f9906b6 100644 --- a/src/sdl/i_timer.cpp +++ b/src/sdl/i_timer.cpp @@ -2,6 +2,7 @@ // Moved from sdl/i_system.cpp #include +#include #include #include From 601852d22439bc753b1cedd5269c1b899aaa7c37 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 17 Nov 2014 21:55:24 -0500 Subject: [PATCH 245/313] - Fixed: fixrtext isn't needed with Win64 builds. --- src/CMakeLists.txt | 12 ++++++------ tools/CMakeLists.txt | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e887775fe..2484b3ad9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -357,19 +357,19 @@ if( NOT NO_ASM ) set( ASM_FLAGS -f win32 -DWIN32 -i${CMAKE_CURRENT_SOURCE_DIR}/ ) endif( X64 ) endif( UNIX ) - if( WIN32 ) + if( WIN32 AND NOT X64 ) set( FIXRTEXT fixrtext ) - else( WIN32 ) + else( WIN32 AND NOT X64 ) set( FIXRTEXT "" ) - endif( WIN32 ) + endif( WIN32 AND NOT X64 ) message( STATUS "Selected assembler: ${ASSEMBLER}" ) MACRO( ADD_ASM_FILE indir infile ) set( ASM_OUTPUT_${infile} "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/${indir}/${infile}${ASM_OUTPUT_EXTENSION}" ) - if( WIN32 ) + if( WIN32 AND NOT X64 ) set( FIXRTEXT_${infile} COMMAND ${FIXRTEXT} "${ASM_OUTPUT_${infile}}" ) - else( WIN32 ) + else( WIN32 AND NOT X64 ) set( FIXRTEXT_${infile} COMMAND "" ) - endif( WIN32 ) + endif( WIN32 AND NOT X64 ) add_custom_command( OUTPUT ${ASM_OUTPUT_${infile}} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/${indir} COMMAND ${ASSEMBLER} ${ASM_FLAGS} -o"${ASM_OUTPUT_${infile}}" "${CMAKE_CURRENT_SOURCE_DIR}/${indir}/${infile}${ASM_SOURCE_EXTENSION}" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 08a1c4f4b..4f9e9ae22 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required( VERSION 2.4 ) add_subdirectory( lemon ) add_subdirectory( re2c ) -if( WIN32 ) +if( WIN32 AND NOT CMAKE_SIZEOF_VOID_P MATCHES "8" ) add_subdirectory( fixrtext ) -endif( WIN32 ) +endif( WIN32 AND NOT CMAKE_SIZEOF_VOID_P MATCHES "8" ) add_subdirectory( updaterevision ) add_subdirectory( zipdir ) From 99b2cfa14715f6b470ac8531f6b396f88d24d413 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 20 Nov 2014 18:57:40 +1300 Subject: [PATCH 246/313] Added APROP_DamageMultiply - Used with Set/GetActorProperty, adds a generic multiplier for damage a source deals. --- src/actor.h | 1 + src/p_acs.cpp | 6 ++++++ src/p_interaction.cpp | 10 ++++++++-- src/p_mobj.cpp | 13 +++++++++++-- src/version.h | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/actor.h b/src/actor.h index 52a50b04e..f46ee7148 100644 --- a/src/actor.h +++ b/src/actor.h @@ -971,6 +971,7 @@ public: FNameNoInit DamageType; FNameNoInit DamageTypeReceived; fixed_t DamageFactor; + fixed_t DamageMultiply; FNameNoInit PainType; FNameNoInit DeathType; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 03de8b3df..cc2e0f847 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3673,6 +3673,7 @@ enum APROP_AttackZOffset = 40, APROP_StencilColor = 41, APROP_Friction = 42, + APROP_DamageMultiplier=43, }; // These are needed for ACS's APROP_RenderStyle @@ -3862,6 +3863,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->DamageFactor = value; break; + case APROP_DamageMultiplier: + actor->DamageMultiply = value; + break; + case APROP_MasterTID: AActor *other; other = SingleActorFromTID (value, NULL); @@ -3933,6 +3938,7 @@ int DLevelScript::GetActorProperty (int tid, int property, const SDWORD *stack, case APROP_Speed: return actor->Speed; case APROP_Damage: return actor->Damage; // Should this call GetMissileDamage() instead? case APROP_DamageFactor:return actor->DamageFactor; + case APROP_DamageMultiplier: return actor->DamageMultiply; case APROP_Alpha: return actor->alpha; case APROP_RenderStyle: for (int style = STYLE_None; style < STYLE_Count; ++style) { // Check for a legacy render style that matches. diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 4ee86d82c..efed105f8 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1065,10 +1065,16 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } // Handle active damage modifiers (e.g. PowerDamage) - if (source != NULL && source->Inventory != NULL) + if (source != NULL) { int olddam = damage; - source->Inventory->ModifyDamage(olddam, mod, damage, false); + + damage = FixedMul(damage, source->DamageMultiply); + if (source->Inventory != NULL) + { + source->Inventory->ModifyDamage(olddam, mod, damage, false); + } + if (olddam != damage && damage <= 0) { // Still allow FORCEPAIN if (MustForcePain(target, inflictor)) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c7bfcea21..39531c6ef 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -313,8 +313,16 @@ void AActor::Serialize (FArchive &arc) } arc << lastpush << lastbump << PainThreshold - << DamageFactor - << WeaveIndexXY << WeaveIndexZ + << DamageFactor; + if (SaveVersion >= 4516) + { + arc << DamageMultiply; + } + else + { + DamageMultiply = FRACUNIT; + } + arc << WeaveIndexXY << WeaveIndexZ << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner << PoisonDamage << PoisonDuration << PoisonPeriod; if (SaveVersion >= 3235) @@ -3868,6 +3876,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t actor->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 if (G_SkillProperty(SKILLP_FastMonsters)) actor->Speed = actor->GetClass()->Meta.GetMetaFixed(AMETA_FastSpeed, actor->Speed); + actor->DamageMultiply = FRACUNIT; // set subsector and/or block links diff --git a/src/version.h b/src/version.h index f091f416a..15d01d315 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 4515 +#define SAVEVER 4516 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From e303833e5fb2db4c0c84fc8499ad2ec1ecf44f0d Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 20 Nov 2014 22:12:16 +1300 Subject: [PATCH 247/313] Inventory damage needs multiplying first --- src/p_interaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index efed105f8..fb947f309 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1069,11 +1069,11 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { int olddam = damage; - damage = FixedMul(damage, source->DamageMultiply); if (source->Inventory != NULL) { source->Inventory->ModifyDamage(olddam, mod, damage, false); } + damage = FixedMul(damage, source->DamageMultiply); if (olddam != damage && damage <= 0) { // Still allow FORCEPAIN From 3437f4fcaba244a290a6abe4f8dcccc8c35a9862 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 21 Nov 2014 17:49:57 +1300 Subject: [PATCH 248/313] Check ACS module size in saved games --- src/p_acs.cpp | 8 ++++++++ src/p_acs.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 03de8b3df..be4dea612 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1581,20 +1581,28 @@ void FBehavior::StaticSerializeModuleStates (FArchive &arc) for (modnum = 0; modnum < StaticModules.Size(); ++modnum) { FBehavior *module = StaticModules[modnum]; + int ModSize = module->GetDataSize(); if (arc.IsStoring()) { arc.WriteString (module->ModuleName); + if (SaveVersion >= 4516) arc << ModSize; } else { char *modname = NULL; arc << modname; + if (SaveVersion >= 4516) arc << ModSize; if (stricmp (modname, module->ModuleName) != 0) { delete[] modname; I_Error ("Level was saved with a different set of ACS modules."); } + else if (ModSize != module->GetDataSize()) + { + delete[] modname; + I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); + } delete[] modname; } module->SerializeVars (arc); diff --git a/src/p_acs.h b/src/p_acs.h index 02544e367..88016f0db 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -308,6 +308,7 @@ public: int GetScriptIndex (const ScriptPtr *ptr) const { ptrdiff_t index = ptr - Scripts; return index >= NumScripts ? -1 : (int)index; } ScriptPtr *GetScriptPtr(int index) const { return index >= 0 && index < NumScripts ? &Scripts[index] : NULL; } int GetLumpNum() const { return LumpNum; } + int GetDataSize() const { return DataSize; } const char *GetModuleName() const { return ModuleName; } ACSProfileInfo *GetFunctionProfileData(int index) { return index >= 0 && index < NumFunctions ? &FunctionProfileData[index] : NULL; } ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); } From c494063eb926cb73811b41457cea7fe5fcb917b9 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 21 Nov 2014 17:53:33 +1300 Subject: [PATCH 249/313] Forgot version bump --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index f091f416a..15d01d315 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 4515 +#define SAVEVER 4516 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From e9075334a3cb515c88f1bb4822cac53affde46fd Mon Sep 17 00:00:00 2001 From: khokh2001 Date: Sun, 23 Nov 2014 00:36:22 +0900 Subject: [PATCH 250/313] new opl3 emulator --- src/CMakeLists.txt | 1 + src/oplsynth/mlopl_io.cpp | 4 +- src/oplsynth/nukedopl3.cpp | 1145 ++++++++++++++++++++++++++++++++++++ src/oplsynth/nukedopl3.h | 118 ++++ src/oplsynth/opl.h | 1 + wadsrc/static/menudef.txt | 1 + zdoom.vcproj | 8 + 7 files changed, 1276 insertions(+), 2 deletions(-) create mode 100644 src/oplsynth/nukedopl3.cpp create mode 100644 src/oplsynth/nukedopl3.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2484b3ad9..605e32f23 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -992,6 +992,7 @@ add_executable( zdoom WIN32 oplsynth/opl_mus_player.cpp oplsynth/dosbox/opl.cpp oplsynth/OPL3.cpp + oplsynth/nukedopl3.cpp resourcefiles/ancientzip.cpp resourcefiles/file_7z.cpp resourcefiles/file_grp.cpp diff --git a/src/oplsynth/mlopl_io.cpp b/src/oplsynth/mlopl_io.cpp index b9d06629f..691463470 100644 --- a/src/oplsynth/mlopl_io.cpp +++ b/src/oplsynth/mlopl_io.cpp @@ -323,7 +323,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) { assert(numchips >= 1 && numchips <= countof(chips)); uint i; - IsOPL3 = (opl_core == 1 || opl_core == 2); + IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3); memset(chips, 0, sizeof(chips)); if (IsOPL3) @@ -332,7 +332,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : JavaOPLCreate(stereo)) : YM3812Create(stereo); + OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); if (chip == NULL) { break; diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp new file mode 100644 index 000000000..95d03ae8d --- /dev/null +++ b/src/oplsynth/nukedopl3.cpp @@ -0,0 +1,1145 @@ +/* +* Copyright (C) 2013-2014 Nuke.YKT +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team: + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. +*/ + +//version 1.4.2 + +/* Changelog: + v1.1: + Vibrato's sign fix + v1.2: + Operator key fix + Corrected 4-operator mode + Corrected rhythm mode + Some small fixes + v1.2.1: + Small envelope generator fix + Removed EX_Get function(not used) + v1.3: + Complete rewrite + (Not released) + v1.4: + New envelope and waveform generator + Some small fixes. + (Not released) + v1.4.1: + Envelope generator rate calculation fix + (Not released) + v1.4.2: + Version for ZDoom. +*/ + + +/* Verified: + Noise generator. + Waveform generator. + Envelope generator increase table. + Tremolo. +*/ + +/* TODO: + Verify: + kslrom[15] value(is it 128?). + Sustain level = 15. + Vibrato, Phase generator. + Rhythm part. + Envelope generator state switching(decay->sustain when egt = 1 and decay->release). + Feedback. + Register write. + 4-operator. +*/ + +#include +#include +#include "nukedopl3.h" + +// Channel types + +enum { + ch_4op2, + ch_2op, + ch_4op, + ch_drum +}; + +// Envelope generator states + +enum { + eg_off, + eg_attack, + eg_decay, + eg_sustain, + eg_release +}; + +// Envelope key types + +enum { + egk_norm = 1, + egk_drum = 2 +}; + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; + +static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; + +// +// LFO vibrato +// + +static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; +static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[3][4][8] = { + { { 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, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static const Bit8u op_offset[18] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0b, 0x09, 0x0c, 0x0a, 0x0d, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15 }; + + + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(slot *slott); + +// +// Phase generator +// + +void PG_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + Bit16u fnum = chan->f_number; + if (slt->vibrato) { + Bit8u fnum_high = chan->f_number >> (7 + vib_table[opl->vib_pos] + (!opl->dvb)); + fnum += fnum_high * vibsgn_table[opl->vib_pos]; + } + slt->PG_pos += (((fnum << chan->block) >> 1) * mt[slt->mult]) >> 1; +} + +// +// Envelope generator +// + +Bit16s envelope_calcexp(Bit32u level) { + return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); +} + +Bit16s envelope_calcsin0(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + } + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin1(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin2(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin3(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = 0x1000; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin4(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if ((phase & 0x300) == 0x100 && (phase & 0xff)) { + phase--; + neg = ~0; + } + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin5(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin6(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + } + return envelope_calcexp(envelope << 3) ^ neg; +} + +Bit16s envelope_calcsin7(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +envelope_sinfunc envelope_sin[8] = { + envelope_calcsin0, + envelope_calcsin1, + envelope_calcsin2, + envelope_calcsin3, + envelope_calcsin4, + envelope_calcsin5, + envelope_calcsin6, + envelope_calcsin7 +}; + +void envelope_gen_off(slot *slott); +void envelope_gen_change(slot *slott); +void envelope_gen_attack(slot *slott); +void envelope_gen_decay(slot *slott); +void envelope_gen_sustain(slot *slott); +void envelope_gen_release(slot *slott); + +envelope_genfunc envelope_gen[6] = { + envelope_gen_off, + envelope_gen_attack, + envelope_gen_decay, + envelope_gen_sustain, + envelope_gen_release, + envelope_gen_change +}; + +enum envelope_gen_num { + envelope_gen_num_off = 0, + envelope_gen_num_attack = 1, + envelope_gen_num_decay = 2, + envelope_gen_num_sustain = 3, + envelope_gen_num_release = 4, + envelope_gen_num_change = 5 +}; + +void envelope_gen_off(slot *slott) { + slott->EG_out = 0x1ff; +} + +void envelope_gen_change(slot *slott) { + slott->eg_gen = slott->eg_gennext; +} + +void envelope_gen_attack(slot *slott) { + slott->EG_out += ((~slott->EG_out) *slott->eg_inc) >> 3; + if (slott->EG_out < 0x00) { + slott->EG_out = 0x00; + } + if (slott->EG_out == 0x00) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_decay; + } +} + +void envelope_gen_decay(slot *slott) { + slott->EG_out += slott->eg_inc; + if (slott->EG_out >= slott->EG_sl << 4) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_sustain; + } +} + +void envelope_gen_sustain(slot *slott) { + if (!slott->EG_type) { + envelope_gen_release(slott); + } +} + +void envelope_gen_release(slot *slott) { + slott->EG_out += slott->eg_inc; + if (slott->EG_out >= 0x1ff) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_off; + } +} + +Bit8u EG_CalcRate(chip *opl, Bit8u op, Bit8u rate) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + if (rate == 0x00) { + return 0x00; + } + Bit8u rof = slt->ksr ? chan->ksv : (chan->ksv >> 2); + Bit8u rat = (rate << 2) + rof; + if (rat > 0x3c) { + rat = 0x3c; + } + return rat; +} + +void envelope_calc(chip *opl, Bit8u op) { + slot *slott = &opl->OPs[op]; + Bit16u timer = opl->timer; + Bit8u rate_h, rate_l; + Bit8u rate; + Bit8u reg_rate = 0;; + switch (slott->eg_gen) { + case envelope_gen_num_attack: + reg_rate = slott->EG_ar; + break; + case envelope_gen_num_decay: + reg_rate = slott->EG_dr; + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + reg_rate = slott->EG_rr; + break; + } + rate = EG_CalcRate(opl, op, reg_rate); + rate_h = rate >> 2; + rate_l = rate & 3; + Bit8u inc = 0; + if (slott->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { + inc = 8; + } + else if (eg_incsh[rate_h] > 0) { + if ((timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((timer) >> eg_incsh[rate_h]) & 0x07]; + } + } + else { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][timer & 0x07] << (-eg_incsh[rate_h]); + } + slott->eg_inc = inc; + envelope_gen[slott->eg_gen](slott); +} + +void EG_UpdateKSL(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + Bit8u fnum_high = (chan->f_number >> 6) & 0x0f; + Bit16s ksl = (kslrom[fnum_high] << 1) - ((chan->block ^ 0x07) << 5) - 0x20; + if (ksl < 0x00) { + ksl = 0x00; + } + slt->EG_ksl = ksl >> kslshift[slt->ksl]; +} + +void EG_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + envelope_calc(opl, op); + slt->EG_mout = slt->EG_out + slt->EG_ksl + (slt->EG_tl << 2); + if (slt->tremolo) { + slt->EG_mout += opl->trem_val; + } + if (slt->EG_mout > 0x1ff) { + slt->EG_mout = 0x1ff; + } +} + +void EG_KeyOn(chip *opl, Bit8u op, Bit8u type) { + slot *slt = &opl->OPs[op]; + if (!slt->key) { + slt->EG_state = eg_attack; + slt->eg_gen = envelope_gen_num_change; + slt->eg_gennext = envelope_gen_num_attack; + slt->PG_pos = 0; + } + slt->key |= type; +} + +void EG_KeyOff(chip *opl, Bit8u op, Bit8u type) { + slot *slt = &opl->OPs[op]; + if (slt->key) { + slt->key &= (~type); + if (slt->key == 0x00) { + slt->EG_state = eg_release; + slt->eg_gen = envelope_gen_num_change; + slt->eg_gennext = envelope_gen_num_release; + } + } +} + +// +// Noise Generator +// + +void N_Generate(chip *opl) { + if (opl->noise & 1) { + opl->noise ^= 0x800302; + } + opl->noise >>= 1; +} + +// +// Operator(Slot) +// + +void OP_Update20(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->tremolo = (opl->opl_memory[0x20 + slt->offset] >> 7); + slt->vibrato = (opl->opl_memory[0x20 + slt->offset] >> 6) & 0x01; + slt->EG_type = (opl->opl_memory[0x20 + slt->offset] >> 5) & 0x01; + slt->ksr = (opl->opl_memory[0x20 + slt->offset] >> 4) & 0x01; + slt->mult = (opl->opl_memory[0x20 + slt->offset]) & 0x0f; +} + +void OP_Update40(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_tl = (opl->opl_memory[0x40 + slt->offset]) & 0x3f; + slt->ksl = (opl->opl_memory[0x40 + slt->offset] >> 6) & 0x03; + EG_UpdateKSL(opl, op); +} + +void OP_Update60(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_dr = (opl->opl_memory[0x60 + slt->offset]) & 0x0f; + slt->EG_ar = (opl->opl_memory[0x60 + slt->offset] >> 4) & 0x0f; +} + +void OP_Update80(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_rr = (opl->opl_memory[0x80 + slt->offset]) & 0x0f; + slt->EG_sl = (opl->opl_memory[0x80 + slt->offset] >> 4) & 0x0f; + if (slt->EG_sl == 0x0f) { + slt->EG_sl = 0x1f; + } +} + +void OP_UpdateE0(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->waveform = opl->opl_memory[0xe0 + slt->offset] & 0x07; + if (!opl->newm) { + slt->waveform &= 0x03; + } +} + +void OP_GeneratePhase(chip *opl, Bit8u op, Bit16u phase) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform](phase, slt->EG_out); +} + +void OP_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform]((Bit16u)((slt->PG_pos >> 9) + (*slt->mod)), slt->EG_mout); +} + +void OP_GenerateZM(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform]((Bit16u)(slt->PG_pos >> 9), slt->EG_mout); +} + +void OP_CalcFB(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + slt->prevout[1] = slt->prevout[0]; + slt->prevout[0] = slt->out; + if (chan->feedback) { + slt->fbmod = (slt->prevout[0] + slt->prevout[1]) >> chan->feedback; + } else { + slt->fbmod = 0; + } +} + +// +// Channel +// + +void CH_UpdateRhythm(chip *opl) { + opl->rhythm = (opl->opl_memory[0xbd] & 0x3f); + if (opl->rhythm & 0x20) { + for (Bit8u i = 6; i < 9; i++) { + opl->Channels[i].chtype = ch_drum; + } + //HH + if (opl->rhythm & 0x01) { + EG_KeyOn(opl, 14, egk_drum); + } else { + EG_KeyOff(opl, 14, egk_drum); + } + //TC + if (opl->rhythm & 0x02) { + EG_KeyOn(opl, 17, egk_drum); + } else { + EG_KeyOff(opl, 17, egk_drum); + } + //TOM + if (opl->rhythm & 0x04) { + EG_KeyOn(opl, 16, egk_drum); + } else { + EG_KeyOff(opl, 16, egk_drum); + } + //SD + if (opl->rhythm & 0x08) { + EG_KeyOn(opl, 15, egk_drum); + } else { + EG_KeyOff(opl, 15, egk_drum); + } + //BD + if (opl->rhythm & 0x10) { + EG_KeyOn(opl, 12, egk_drum); + EG_KeyOn(opl, 13, egk_drum); + } else { + EG_KeyOff(opl, 12, egk_drum); + EG_KeyOff(opl, 13, egk_drum); + } + } else { + for (Bit8u i = 6; i < 9; i++) { + opl->Channels[i].chtype = ch_2op; + } + } +} + +void CH_UpdateAB0(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm && chan->chtype == ch_4op2) { + return; + } + Bit16u f_number = (opl->opl_memory[0xa0 + chan->offset]) | (((opl->opl_memory[0xb0 + chan->offset]) & 0x03) << 8); + Bit8u block = ((opl->opl_memory[0xb0 + chan->offset]) >> 2) & 0x07; + Bit8u ksv = block * 2 | ((f_number >> (9 - opl->nts)) & 0x01); + chan->f_number = f_number; + chan->block = block; + chan->ksv = ksv; + EG_UpdateKSL(opl, ch * 2); + EG_UpdateKSL(opl, ch * 2 + 1); + OP_Update60(opl, ch * 2); + OP_Update60(opl, ch * 2 + 1); + OP_Update80(opl, ch * 2); + OP_Update80(opl, ch * 2 + 1); + if (opl->newm && chan->chtype == ch_4op) { + chan = &opl->Channels[ch + 3]; + chan->f_number = f_number; + chan->block = block; + chan->ksv = ksv; + EG_UpdateKSL(opl, (ch + 3) * 2); + EG_UpdateKSL(opl, (ch + 3) * 2 + 1); + OP_Update60(opl, (ch + 3) * 2); + OP_Update60(opl, (ch + 3) * 2 + 1); + OP_Update80(opl, (ch + 3) * 2); + OP_Update80(opl, (ch + 3) * 2 + 1); + } +} + +void CH_SetupAlg(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (chan->alg & 0x08) { + return; + } + if (chan->alg & 0x04) { + switch (chan->alg & 0x03) { + case 0: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 1: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; + opl->OPs[ch * 2].mod = &opl->zm; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 2: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 3: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->zm; + break; + } + } else { + switch (chan->alg & 0x01) { + case 0: + opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 1: + opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; + opl->OPs[ch * 2 + 1].mod = &opl->zm; + break; + } + } +} + +void CH_UpdateC0(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + Bit8u fb = (opl->opl_memory[0xc0 + chan->offset] & 0x0e) >> 1; + chan->feedback = fb ? (9 - fb) : 0; + chan->con = opl->opl_memory[0xc0 + chan->offset] & 0x01; + chan->alg = chan->con; + if (opl->newm) { + if (chan->chtype == ch_4op) { + channel *chan1 = &opl->Channels[ch + 3]; + chan1->alg = 0x04 | (chan->con << 1) | (chan1->con); + chan->alg = 0x08; + CH_SetupAlg(opl, ch + 3); + } else if (chan->chtype == ch_4op2) { + channel *chan1 = &opl->Channels[ch - 3]; + chan->alg = 0x04 | (chan1->con << 1) | (chan->con); + chan1->alg = 0x08; + CH_SetupAlg(opl, ch); + } else { + CH_SetupAlg(opl, ch); + } + } else { + CH_SetupAlg(opl, ch); + } + if (opl->newm) { + chan->cha = ((opl->opl_memory[0xc0 + chan->offset] >> 4) & 0x01) ? ~0 : 0; + chan->chb = ((opl->opl_memory[0xc0 + chan->offset] >> 5) & 0x01) ? ~0 : 0; + chan->chc = ((opl->opl_memory[0xc0 + chan->offset] >> 6) & 0x01) ? ~0 : 0; + chan->chd = ((opl->opl_memory[0xc0 + chan->offset] >> 7) & 0x01) ? ~0 : 0; + } else { + opl->Channels[ch].cha = opl->Channels[ch].chb = ~0; + opl->Channels[ch].chc = opl->Channels[ch].chd = 0; + } +} + +void CH_Set2OP(chip *opl) { + for (Bit8u i = 0; i < 18; i++) { + opl->Channels[i].chtype = ch_2op; + CH_UpdateC0(opl, i); + } +} + +void CH_Set4OP(chip *opl) { + for (Bit8u i = 0; i < 3; i++) { + if ((opl->opl_memory[0x104] >> i) & 0x01) { + opl->Channels[i].chtype = ch_4op; + opl->Channels[i + 3].chtype = ch_4op2; + CH_UpdateC0(opl, i); + CH_UpdateC0(opl, i + 3); + } + if ((opl->opl_memory[0x104] >> (i + 3)) & 0x01) { + opl->Channels[i + 9].chtype = ch_4op; + opl->Channels[i + 3 + 9].chtype = ch_4op2; + CH_UpdateC0(opl, i + 9); + CH_UpdateC0(opl, i + 3 + 9); + } + } +} + +void CH_GenerateRhythm(chip *opl) { + if (opl->rhythm & 0x20) { + channel *chan6 = &opl->Channels[6]; + channel *chan7 = &opl->Channels[7]; + channel *chan8 = &opl->Channels[8]; + slot *slt12 = &opl->OPs[12]; + slot *slt13 = &opl->OPs[13]; + slot *slt14 = &opl->OPs[14]; + slot *slt15 = &opl->OPs[15]; + slot *slt16 = &opl->OPs[16]; + slot *slt17 = &opl->OPs[17]; + //BD + OP_Generate(opl, 12); + OP_Generate(opl, 13); + chan6->out = slt13->out * 2; + Bit16u P14 = (slt14->PG_pos >> 9) & 0x3ff; + Bit16u P17 = (slt17->PG_pos >> 9) & 0x3ff; + Bit16u phase = 0; + // HH TC Phase bit + Bit16u PB = ((P14 & 0x08) | (((P14 >> 5) ^ P14) & 0x04) | (((P17 >> 2) ^ P17) & 0x08)) ? 0x01 : 0x00; + //HH + phase = (PB << 9) | (0x34 << ((PB ^ (opl->noise & 0x01) << 1))); + OP_GeneratePhase(opl, 14, phase); + //SD + phase = (0x100 << ((P14 >> 8) & 0x01)) ^ ((opl->noise & 0x01) << 8); + OP_GeneratePhase(opl, 15, phase); + //TT + OP_GenerateZM(opl, 16); + //TC + phase = 0x100 | (PB << 9); + OP_GeneratePhase(opl, 17, phase); + chan7->out = (slt14->out + slt15->out) * 2; + chan8->out = (slt16->out + slt17->out) * 2; + } +} + +void CH_Generate(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (chan->chtype == ch_drum) { + return; + } + if (chan->alg & 0x08) { + chan->out = 0; + return; + } else if (chan->alg & 0x04) { + OP_Generate(opl, (ch - 3) * 2); + OP_Generate(opl, (ch - 3) * 2 + 1); + OP_Generate(opl, ch * 2); + OP_Generate(opl, ch * 2 + 1); + switch (chan->alg & 0x03) { + case 0: + chan->out = opl->OPs[ch * 2 + 1].out; + break; + case 1: + chan->out = opl->OPs[(ch - 3) * 2 + 1].out + opl->OPs[ch * 2 + 1].out; + break; + case 2: + chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2 + 1].out; + break; + case 3: + chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + break; + } + } + else { + OP_Generate(opl, ch * 2); + OP_Generate(opl, ch * 2 + 1); + switch (chan->alg & 0x01) { + case 0: + chan->out = opl->OPs[ch * 2 + 1].out; + break; + case 1: + chan->out = opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + break; + } + } +} + +void CH_Enable(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm) { + if (chan->chtype == ch_4op) { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + EG_KeyOn(opl, (ch + 3) * 2, egk_norm); + EG_KeyOn(opl, (ch + 3) * 2 + 1, egk_norm); + } + else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + } + } + else { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + } +} + +void CH_Disable(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm) { + if (chan->chtype == ch_4op) { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + EG_KeyOff(opl, (ch + 3) * 2, egk_norm); + EG_KeyOff(opl, (ch + 3) * 2 + 1, egk_norm); + } + else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + } + } + else { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + } +} + +Bit16s limshort(Bit32s a) { + if (a > 32767) { + a = 32767; + } + else if (a < -32768) { + a = -32768; + } + return (Bit16s)a; +} + +void NukedOPL3::Reset() { + for (Bit8u i = 0; i < 36; i++) { + opl3.OPs[i].PG_pos = 0; + opl3.OPs[i].PG_inc = 0; + opl3.OPs[i].EG_out = 0x1ff; + opl3.OPs[i].EG_mout = 0x1ff; + opl3.OPs[i].eg_inc = 0; + opl3.OPs[i].eg_gen = 0; + opl3.OPs[i].eg_gennext = 0; + opl3.OPs[i].EG_ksl = 0; + opl3.OPs[i].EG_ar = 0; + opl3.OPs[i].EG_dr = 0; + opl3.OPs[i].EG_sl = 0; + opl3.OPs[i].EG_rr = 0; + opl3.OPs[i].EG_state = eg_off; + opl3.OPs[i].EG_type = 0; + opl3.OPs[i].out = 0; + opl3.OPs[i].prevout[0] = 0; + opl3.OPs[i].prevout[1] = 0; + opl3.OPs[i].fbmod = 0; + opl3.OPs[i].offset = op_offset[i % 18] + ((i > 17) << 8); + opl3.OPs[i].mult = 0; + opl3.OPs[i].vibrato = 0; + opl3.OPs[i].tremolo = 0; + opl3.OPs[i].ksr = 0; + opl3.OPs[i].EG_tl = 0; + opl3.OPs[i].ksl = 0; + opl3.OPs[i].key = 0; + opl3.OPs[i].waveform = 0; + } + for (Bit8u i = 0; i < 9; i++) { + opl3.Channels[i].con = 0; + opl3.Channels[i + 9].con = 0; + opl3.Channels[i].chtype = ch_2op; + opl3.Channels[i + 9].chtype = ch_2op; + opl3.Channels[i].alg = 0; + opl3.Channels[i + 9].alg = 0; + opl3.Channels[i].offset = i; + opl3.Channels[i + 9].offset = 0x100 + i; + opl3.Channels[i].feedback = 0; + opl3.Channels[i + 9].feedback = 0; + opl3.Channels[i].out = 0; + opl3.Channels[i + 9].out = 0; + opl3.Channels[i].cha = ~0; + opl3.Channels[i + 9].cha = ~0; + opl3.Channels[i].chb = ~0; + opl3.Channels[i + 9].chb = ~0; + opl3.Channels[i].chc = 0; + opl3.Channels[i + 9].chc = 0; + opl3.Channels[i].chd = 0; + opl3.Channels[i + 9].chd = 0; + opl3.Channels[i].out = 0; + opl3.Channels[i + 9].out = 0; + opl3.Channels[i].f_number = 0; + opl3.Channels[i + 9].f_number = 0; + opl3.Channels[i].block = 0; + opl3.Channels[i + 9].block = 0; + opl3.Channels[i].ksv = 0; + opl3.Channels[i + 9].ksv = 0; + opl3.Channels[i].panl = (float)CENTER_PANNING_POWER; + opl3.Channels[i + 9].panl = (float)CENTER_PANNING_POWER; + opl3.Channels[i].panr = (float)CENTER_PANNING_POWER; + opl3.Channels[i + 9].panr = (float)CENTER_PANNING_POWER; + } + memset(opl3.opl_memory, 0, 0x200); + opl3.newm = 0; + opl3.nts = 0; + opl3.rhythm = 0; + opl3.dvb = 0; + opl3.dam = 0; + opl3.noise = 0x306600; + opl3.vib_pos = 0; + opl3.timer = 0; + opl3.trem_inc = 0; + opl3.trem_tval = 0; + opl3.trem_dir = 0; + opl3.trem_val = 0; + opl3.zm = 0; + CH_Set2OP(&opl3); +} + +void NukedOPL3::WriteReg(int reg, int v) { + v &= 0xff; + reg &= 0x1ff; + Bit8u highbank = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + opl3.opl_memory[reg & 0x1ff] = v; + switch (regm & 0xf0) { + case 0x00: + if (highbank) { + switch (regm & 0x0f) { + case 0x04: + CH_Set2OP(&opl3); + CH_Set4OP(&opl3); + break; + case 0x05: + opl3.newm = v & 0x01; + break; + } + } + else { + switch (regm & 0x0f) { + case 0x08: + opl3.nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update20(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update40(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update60(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update80(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) { + OP_UpdateE0(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) { + CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + } + break; + case 0xb0: + if (regm == 0xbd && !highbank) { + opl3.dam = v >> 7; + opl3.dvb = (v >> 6) & 0x01; + CH_UpdateRhythm(&opl3); + } + else if ((regm & 0x0f) < 9) { + CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + if (v & 0x20) { + CH_Enable(&opl3, 9 * highbank + (regm & 0x0f)); + } + else { + CH_Disable(&opl3, 9 * highbank + (regm & 0x0f)); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) { + CH_UpdateC0(&opl3, 9 * highbank + (regm & 0x0f)); + } + break; + } +} + +void NukedOPL3::Update(float* sndptr, int numsamples) { + Bit32s outa, outb; + Bit8u ii = 0; + for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { + outa = 0; + outb = 0; + for (ii = 0; ii < 36; ii++) { + OP_CalcFB(&opl3, ii); + } + CH_GenerateRhythm(&opl3); + for (ii = 0; ii < 18; ii++) { + CH_Generate(&opl3, ii); + if (FullPan) { + outa += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panl); + outb += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panr); + } + else { + outa += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].cha); + outb += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].chb); + } + } + for (ii = 0; ii < 36; ii++) { + EG_Generate(&opl3, ii); + PG_Generate(&opl3, ii); + } + N_Generate(&opl3); + opl3.trem_inc++; + if (!(opl3.trem_inc & 0x3f)) { + if (!opl3.trem_dir) { + if (opl3.trem_tval == 105) { + opl3.trem_tval--; + opl3.trem_dir = 1; + } + else { + opl3.trem_tval++; + } + } + else { + if (opl3.trem_tval == 0) { + opl3.trem_tval++; + opl3.trem_dir = 0; + } + else { + opl3.trem_tval--; + } + } + opl3.trem_val = (opl3.trem_tval >> 2) >> ((!opl3.dam) << 1); + } + opl3.timer++; + opl3.vib_pos = (opl3.timer >> 10) & 0x07; + *sndptr++ += (float)(outa / 10240.0); + *sndptr++ += (float)(outb / 10240.0); + } +} + +void NukedOPL3::SetPanning(int c, float left, float right) { + if (FullPan) { + opl3.Channels[c].panl = left; + opl3.Channels[c].panr = right; + } +} + +NukedOPL3::NukedOPL3(bool stereo) { + FullPan = stereo; + Reset(); +} + +OPLEmul *NukedOPL3Create(bool stereo) { + return new NukedOPL3(stereo); +} \ No newline at end of file diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h new file mode 100644 index 000000000..51b78935b --- /dev/null +++ b/src/oplsynth/nukedopl3.h @@ -0,0 +1,118 @@ +/* +* Copyright (C) 2013-2014 Nuke.YKT +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team: + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. +*/ + +//version 1.4.2 + +#include "opl.h" +#include "muslib.h" + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef DWORD Bit32u; +typedef SDWORD Bit32s; +typedef WORD Bit16u; +typedef SWORD Bit16s; +typedef BYTE Bit8u; +typedef SBYTE Bit8s; + +struct channel { + Bit8u con; + Bit8u chtype; + Bit8u alg; + Bit16u offset; + Bit8u feedback; + Bit16u cha, chb, chc, chd; + Bit16s out; + Bit16u f_number; + Bit8u block; + Bit8u ksv; + float panl; + float panr; +}; + +struct slot { + Bit32u PG_pos; + Bit32u PG_inc; + Bit16s EG_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_gennext; + Bit16u EG_mout; + Bit8u EG_ksl; + Bit8u EG_ar; + Bit8u EG_dr; + Bit8u EG_sl; + Bit8u EG_rr; + Bit8u EG_state; + Bit8u EG_type; + Bit16s out; + Bit16s *mod; + Bit16s prevout[2]; + Bit16s fbmod; + Bit16u offset; + Bit8u mult; + Bit8u vibrato; + Bit8u tremolo; + Bit8u ksr; + Bit8u EG_tl; + Bit8u ksl; + Bit8u key; + Bit8u waveform; +}; + + +struct chip { + Bit8u opl_memory[0x200]; + Bit8u newm; + Bit8u nts; + Bit8u rhythm; + Bit8u dvb; + Bit8u dam; + Bit32u noise; + Bit16u vib_pos; + Bit16u timer; + Bit8u trem_inc; + Bit8u trem_tval; + Bit8u trem_dir; + Bit8u trem_val; + channel Channels[18]; + slot OPs[36]; + Bit16s zm; +}; + +class NukedOPL3 : public OPLEmul { +private: + chip opl3; + bool FullPan; +public: + void Reset(); + void Update(float* sndptr, int numsamples); + void WriteReg(int reg, int v); + void SetPanning(int c, float left, float right); + + NukedOPL3(bool stereo); +}; \ No newline at end of file diff --git a/src/oplsynth/opl.h b/src/oplsynth/opl.h index 661258a26..2cbb19c32 100644 --- a/src/oplsynth/opl.h +++ b/src/oplsynth/opl.h @@ -20,6 +20,7 @@ public: OPLEmul *YM3812Create(bool stereo); OPLEmul *DBOPLCreate(bool stereo); OPLEmul *JavaOPLCreate(bool stereo); +OPLEmul *NukedOPL3Create(bool stereo); #define OPL_SAMPLE_RATE 49716.0 #define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 408e534a8..abc654252 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1478,6 +1478,7 @@ OptionValue OplCores 0, "MAME OPL2" 1, "DOSBox OPL3" 2, "Java OPL3" + 3, "Nuked OPL3" } OptionMenu AdvSoundOptions diff --git a/zdoom.vcproj b/zdoom.vcproj index cabc6c84f..21aaccb8e 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2632,6 +2632,14 @@ RelativePath=".\src\oplsynth\opl_mus_player.h" > + + + + From 88f4305e7e985e8bfaa954c7d09bc85a15efc26e Mon Sep 17 00:00:00 2001 From: khokh2001 Date: Sun, 23 Nov 2014 00:39:26 +0900 Subject: [PATCH 251/313] new opl3 emulator --- src/oplsynth/nukedopl3.cpp | 2 +- src/oplsynth/nukedopl3.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp index 95d03ae8d..ee95741f5 100644 --- a/src/oplsynth/nukedopl3.cpp +++ b/src/oplsynth/nukedopl3.cpp @@ -1142,4 +1142,4 @@ NukedOPL3::NukedOPL3(bool stereo) { OPLEmul *NukedOPL3Create(bool stereo) { return new NukedOPL3(stereo); -} \ No newline at end of file +} diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index 51b78935b..7a032cb5a 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -115,4 +115,4 @@ public: void SetPanning(int c, float left, float right); NukedOPL3(bool stereo); -}; \ No newline at end of file +}; From 5ed70d97a6ca00891cfa281734d1e370ea2aec8d Mon Sep 17 00:00:00 2001 From: khokh2001 Date: Sun, 23 Nov 2014 02:18:40 +0900 Subject: [PATCH 252/313] New OPL3 emulator. --- src/oplsynth/nukedopl3.cpp | 4 +++- src/oplsynth/nukedopl3.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp index ee95741f5..90673d855 100644 --- a/src/oplsynth/nukedopl3.cpp +++ b/src/oplsynth/nukedopl3.cpp @@ -19,10 +19,12 @@ /* Nuked Yamaha YMF262(aka OPL3) emulator. Thanks: - MAME Development Team: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPL2 ROMs. */ //version 1.4.2 diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index 7a032cb5a..15f264c1a 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -19,10 +19,12 @@ /* Nuked Yamaha YMF262(aka OPL3) emulator. Thanks: - MAME Development Team: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPL2 ROMs. */ //version 1.4.2 From e9b24a10a1f00bfc429876aa14b68a663d3b4d3a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 23 Nov 2014 12:47:04 +0200 Subject: [PATCH 253/313] OS version detection without deprecated API --- src/cocoa/i_backend_cocoa.mm | 25 +++++++++++++++++++++ src/cocoa/i_osversion.h | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 src/cocoa/i_osversion.h diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 81c487cc7..70aeaeacb 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -153,6 +153,7 @@ typedef NSInteger NSApplicationActivationPolicy; #include "v_video.h" #include "version.h" #include "i_rbopts.h" +#include "i_osversion.h" #undef Class @@ -2094,9 +2095,33 @@ 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; +} + } // unnamed namespace +const DarwinVersion darwinVersion = GetDarwinVersion(); + + #ifdef main #undef main #endif // main diff --git a/src/cocoa/i_osversion.h b/src/cocoa/i_osversion.h new file mode 100755 index 000000000..5e6ac6d20 --- /dev/null +++ b/src/cocoa/i_osversion.h @@ -0,0 +1,43 @@ +/* + ** 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; From b4ff34dae51a45744432ce74955b8981fb463257 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 23 Nov 2014 12:48:05 +0200 Subject: [PATCH 254/313] Use recently added OS version detection to check for HID Manager API availability --- src/cocoa/i_joystick.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index bd2c59348..56db8f815 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -37,13 +37,12 @@ #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 -#include - #include "HID_Utilities_External.h" #include "d_event.h" #include "doomdef.h" #include "templates.h" +#include "i_osversion.h" namespace @@ -730,16 +729,9 @@ IOKitJoystickManager* s_joystickManager; void I_StartupJoysticks() { - SInt32 majorVersion = 0; - SInt32 minorVersion = 0; + // HID Manager API is available on 10.5 (Darwin 9.x) or newer -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - Gestalt(gestaltSystemVersionMajor, &majorVersion); - Gestalt(gestaltSystemVersionMinor, &minorVersion); -#pragma clang diagnostic pop - - if (majorVersion >= 10 && minorVersion >= 5) + if (darwinVersion.major >= 9) { s_joystickManager = new IOKitJoystickManager; } From 877bfcd3280c3feceebd39838bde80d09cd6c43a Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 23 Nov 2014 22:18:41 +0200 Subject: [PATCH 255/313] Cleaned up support for older OS X SDKs --- src/cocoa/i_backend_cocoa.mm | 75 +++++++++++++++++------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index 70aeaeacb..bd3dc3115 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -45,9 +45,39 @@ #include #include -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +#include -// Missing definitions for 10.4 and earlier +// 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 "dikeys.h" +#include "doomdef.h" +#include "doomstat.h" +#include "s_sound.h" +#include "textures.h" +#include "v_video.h" +#include "version.h" +#include "i_rbopts.h" +#include "i_osversion.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; @@ -132,47 +162,13 @@ typedef NSInteger NSApplicationActivationPolicy; - (BOOL)setActivationPolicy:(NSApplicationActivationPolicy)activationPolicy; @end -#endif // prior to 10.6 - -#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 "dikeys.h" -#include "doomdef.h" -#include "doomstat.h" -#include "s_sound.h" -#include "textures.h" -#include "v_video.h" -#include "version.h" -#include "i_rbopts.h" -#include "i_osversion.h" - -#undef Class - - -#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) - - -// --------------------------------------------------------------------------- - - -#ifndef NSAppKitVersionNumber10_6 - @interface NSWindow(SetStyleMask) - (void)setStyleMask:(NSUInteger)styleMask; @end -#endif // !NSAppKitVersionNumber10_6 +#endif // prior to 10.6 -#ifndef NSAppKitVersionNumber10_7 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 @interface NSView(HiDPIStubs) - (NSPoint)convertPointToBacking:(NSPoint)aPoint; @@ -186,11 +182,12 @@ typedef NSInteger NSApplicationActivationPolicy; - (NSRect)convertRectToBacking:(NSRect)aRect; @end -#endif // !NSAppKitVersionNumber10_7 +#endif // prior to 10.7 // --------------------------------------------------------------------------- + RenderBufferOptions rbOpts; EXTERN_CVAR(Bool, fullscreen) From eacb9aa10570c505e938d1b547a7d860941b9b44 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 24 Nov 2014 19:18:07 -0500 Subject: [PATCH 256/313] - Fixed more non-OSX issues. --- src/sdl/i_gui.cpp | 52 ++++++++++++++++++++++++++++++++++++++-- src/sdl/i_system.cpp | 56 -------------------------------------------- 2 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index 37fc750cb..bccd81972 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -9,10 +9,58 @@ #include "v_palette.h" #include "textures.h" +SDL_Surface *cursorSurface = NULL; +SDL_Rect cursorBlit = {0, 0, 32, 32}; -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 +#include +#undef GC +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; +} +#endif bool I_SetCursor(FTexture *cursorpic) { diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index d65dbc6c8..41fdef323 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -71,13 +71,6 @@ #include "m_fixed.h" #include "g_level.h" -#ifdef USE_XCURSOR -// Xlib has its own GC, so don't let it interfere. -#define GC XGC -#include -#undef GC -#endif - #ifdef __APPLE__ #include #endif // __APPLE__ @@ -95,11 +88,6 @@ extern bool GtkAvailable; #elif defined(__APPLE__) int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad); #endif -#ifdef USE_XCURSOR -bool UseXCursor; -SDL_Cursor *X11Cursor; -SDL_Cursor *FirstCursor; -#endif DWORD LanguageIDs[4]; @@ -755,47 +743,3 @@ unsigned int I_MakeRNGSeed() } return seed; } - -#ifdef USE_XCURSOR -// 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; -} -#endif - -SDL_Surface *cursorSurface = NULL; -SDL_Rect cursorBlit = {0, 0, 32, 32}; From 7e579a0a2a962669dcfa2cb71607b18ea0d90a87 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 24 Nov 2014 18:30:17 -0600 Subject: [PATCH 257/313] - Fixed: Godmode didn't stop ALLOW/CAUSE/FORCEPAIN because the code execution always ended prematurely. - Optimized checks for fake pain and forced pain. Fake pain now calls a subfunction so the code is cleaner. --- src/p_interaction.cpp | 69 +++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 4ee86d82c..7d5a2a74d 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -925,6 +925,11 @@ static inline bool MustForcePain(AActor *target, AActor *inflictor) (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS)); } +static inline bool isFakePain(AActor *target, AActor *inflictor) +{ + return ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))); +} + // Returns the amount of damage actually inflicted upon the target, or -1 if // the damage was cancelled. @@ -940,6 +945,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, bool justhit = false; bool plrDontThrust = false; bool invulpain = false; + bool fakedPain = false; + bool forcedPain = false; int fakeDamage = 0; int holdDamage = 0; @@ -948,6 +955,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } + //Rather than unnecessarily call the function over and over again, let's be a little more efficient. + fakedPain = (isFakePain(target, inflictor)); + forcedPain = (MustForcePain(target, inflictor)); + // Spectral targets only take damage from spectral projectiles. if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) { @@ -976,7 +987,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) { invulpain = true; //This returns -1 later. fakeDamage = damage; @@ -991,7 +1002,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Players are optionally excluded from getting thrust by damage. if (static_cast(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) plrDontThrust = 1; else return -1; @@ -999,7 +1010,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (damage < TELEFRAG_DAMAGE)) + if ((fakedPain) && (damage < TELEFRAG_DAMAGE)) { //Intentionally do not jump to fakepain because the damage hasn't been dished out yet. //Once it's dished out, THEN we can disregard damage factors affecting pain chances. @@ -1057,9 +1068,9 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage > 0) damage = inflictor->DoSpecialDamage (target, damage, mod); - if (damage == -1) + if ((damage == -1) && (target->player == NULL)) //This isn't meant for the player. { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) //Hold off ending the function before we can deal the pain chances. + if (fakedPain) //Hold off ending the function before we can deal the pain chances. goto fakepain; return -1; } @@ -1069,12 +1080,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { int olddam = damage; source->Inventory->ModifyDamage(olddam, mod, damage, false); - if (olddam != damage && damage <= 0) + if (((source->flags7 & MF7_CAUSEPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0)) { // Still allow FORCEPAIN - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } + else if (fakedPain) + goto fakepain; + return -1; } } @@ -1083,13 +1095,11 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); - if (((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0)) + if ((olddam != damage && damage <= 0) && target->player == NULL) { // Still allow FORCEPAIN and make sure we're still passing along fake damage to hit enemies for their pain states. - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) goto fakepain; return -1; @@ -1103,13 +1113,11 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); } - if (damage <= 0) + if (damage <= 0 && target->player == NULL) { // Still allow FORCEPAIN - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) goto fakepain; return -1; @@ -1118,9 +1126,9 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage > 0) damage = target->TakeSpecialDamage (inflictor, source, damage, mod); } - if (damage == -1) + if (damage == -1 && target->player == NULL) //Make sure it's not a player, the pain has yet to be processed with cheats. { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) goto fakepain; return -1; @@ -1247,17 +1255,18 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him - - if (((!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2)) && (!(player->mo->flags5 & MF5_NOPAIN))) && - (((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) //Make sure no godmodes and NOPAIN flags are found first. //Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN. - { + if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN)) + return -1; + else if ((((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) + { invulpain = true; fakeDamage = damage; goto fakepain; } - return -1; + else + return -1; } if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL) @@ -1306,6 +1315,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. + // [MC]Buddha2 absorbs telefrag damage, and anything else thrown their way. if ((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && (damage < TELEFRAG_DAMAGE)) && (player->playerstate != PST_DEAD)) { // If this is a voodoo doll we need to handle the real player as well. @@ -1339,7 +1349,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = newdam; if (damage <= 0) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) goto fakepain; else return damage; @@ -1370,6 +1380,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { + //[MC]Buddha flag for monsters. if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && ((inflictor == NULL || !(inflictor->flags3 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA))) { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; @@ -1434,7 +1445,7 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig //CAUSEPAIN can always attempt to trigger the chances of pain. //ALLOWPAIN can do the same, only if the (unfiltered aka fake) damage is greater than 0. if ((((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage > 0)) - || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (fakeDamage != damage)) + || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) { holdDamage = damage; //Store the modified damage away after factors are taken into account. damage = fakeDamage; //Retrieve the original damage. @@ -1535,7 +1546,7 @@ dopain: { return -1; //NOW we return -1! } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) { return holdDamage; //This is the calculated damage after all is said and done. } From 0123279a1f3dd844bd7fc6e34537c8b225a1b97a Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 24 Nov 2014 22:22:29 -0500 Subject: [PATCH 258/313] - Work around an i386 specific optimizer bug in Apple's GCC 4.2 (GCC 4.0 has worse bugs). - Fixed: zdoom-info.plist used some non-existant/wrong variables. --- src/cocoa/zdoom-info.plist | 4 ++-- src/g_shared/a_armor.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cocoa/zdoom-info.plist b/src/cocoa/zdoom-info.plist index 8371b0555..2a1911cdf 100644 --- a/src/cocoa/zdoom-info.plist +++ b/src/cocoa/zdoom-info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - ${EXECUTABLE_NAME} + ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleIconFile zdoom.icns CFBundleIdentifier @@ -23,7 +23,7 @@ LSApplicationCategoryType public.app-category.action-games LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} + 10.4 CFBundleDocumentTypes diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index a745197c2..0f343e523 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -516,7 +516,14 @@ void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) // with the dragon skin bracers. if (damage < 10000) { +#if __APPLE__ && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1 + // -O1 optimizer bug work around. Only needed for + // GCC 4.2.1 on OS X for 10.4/10.5 tools compatibility. + volatile fixed_t tmp = 300; + Slots[i] -= Scale (damage, SlotsIncrement[i], tmp); +#else Slots[i] -= Scale (damage, SlotsIncrement[i], 300); +#endif if (Slots[i] < 2*FRACUNIT) { Slots[i] = 0; From 46eb5ce2658a62be579cc81669fae01019b77561 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 24 Nov 2014 23:26:38 -0500 Subject: [PATCH 259/313] - Move cursor variables again due to shared code with Cocoa and SDL backends. --- src/sdl/i_gui.cpp | 4 ++-- src/sdl/sdlvideo.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp index bccd81972..b40fb7517 100644 --- a/src/sdl/i_gui.cpp +++ b/src/sdl/i_gui.cpp @@ -9,8 +9,8 @@ #include "v_palette.h" #include "textures.h" -SDL_Surface *cursorSurface = NULL; -SDL_Rect cursorBlit = {0, 0, 32, 32}; +extern SDL_Surface *cursorSurface; +extern SDL_Rect cursorBlit; #ifdef USE_XCURSOR // Xlib has its own GC, so don't let it interfere. diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 59587fc0b..e4222633d 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -81,10 +81,11 @@ struct MiniModeInfo // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern IVideo *Video; -extern SDL_Surface *cursorSurface; -extern SDL_Rect cursorBlit; 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) From 6665ac583728033c6cfa09eb9ec4dc1c8544ac7e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 25 Nov 2014 16:49:27 +0100 Subject: [PATCH 260/313] - fixed: With P_ExplodeMissile now working properly it is no longer valid to terminate a looping sound in a missile's death state because it now gets called after the death sound has been started. --- wadsrc/static/actors/strife/crusader.txt | 2 +- wadsrc/static/actors/strife/strifebishop.txt | 2 +- wadsrc/static/actors/strife/strifeweapons.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wadsrc/static/actors/strife/crusader.txt b/wadsrc/static/actors/strife/crusader.txt index 7f7a0d0c5..eb1ceec6d 100644 --- a/wadsrc/static/actors/strife/crusader.txt +++ b/wadsrc/static/actors/strife/crusader.txt @@ -99,7 +99,7 @@ ACTOR CrusaderMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 5 Bright A_StopSoundEx("Voice") + SMIS A 5 Bright SMIS B 5 Bright SMIS C 4 Bright SMIS DEFG 2 Bright diff --git a/wadsrc/static/actors/strife/strifebishop.txt b/wadsrc/static/actors/strife/strifebishop.txt index dcedf7f58..6f38247fb 100644 --- a/wadsrc/static/actors/strife/strifebishop.txt +++ b/wadsrc/static/actors/strife/strifebishop.txt @@ -85,7 +85,7 @@ ACTOR BishopMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 0 Bright A_StopSoundEx("Voice") + SMIS A 0 Bright // State left for savegame compatibility SMIS A 5 Bright A_Explode(64,64,1,1) SMIS B 5 Bright SMIS C 4 Bright diff --git a/wadsrc/static/actors/strife/strifeweapons.txt b/wadsrc/static/actors/strife/strifeweapons.txt index deea01a52..3b256e226 100644 --- a/wadsrc/static/actors/strife/strifeweapons.txt +++ b/wadsrc/static/actors/strife/strifeweapons.txt @@ -390,7 +390,7 @@ ACTOR MiniMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 0 Bright A_StopSoundEx("Voice") + SMIS A 0 Bright // State left for savegame compatibility SMIS A 5 Bright A_Explode(64,64,1,1) SMIS B 5 Bright SMIS C 4 Bright From 29ecbac96378dc1e5b43d640e12d5f27819b2620 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 25 Nov 2014 17:00:17 +0100 Subject: [PATCH 261/313] - fixed: The demo buffer was allocated with conflicting methods, because M_ReadFile used new whereas the rest of the demo code assumed malloc. Added a new M_ReadFileMalloc function to handle this case without rewriting other things. --- src/g_game.cpp | 2 +- src/m_misc.cpp | 26 ++++++++++++++++++++++++++ src/m_misc.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 734500e55..beec9435a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2636,7 +2636,7 @@ void G_DoPlayDemo (void) { FixPathSeperator (defdemoname); DefaultExtension (defdemoname, ".lmp"); - M_ReadFile (defdemoname, &demobuffer); + M_ReadFileMalloc (defdemoname, &demobuffer); } demo_p = demobuffer; diff --git a/src/m_misc.cpp b/src/m_misc.cpp index c550c4593..7f4fa482d 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -137,6 +137,32 @@ int M_ReadFile (char const *name, BYTE **buffer) return length; } +// +// M_ReadFile (same as above but use malloc instead of new to allocate the buffer.) +// +int M_ReadFileMalloc (char const *name, BYTE **buffer) +{ + int handle, count, length; + struct stat fileinfo; + BYTE *buf; + + handle = open (name, O_RDONLY | O_BINARY, 0666); + if (handle == -1) + I_Error ("Couldn't read file %s", name); + if (fstat (handle,&fileinfo) == -1) + I_Error ("Couldn't read file %s", name); + length = fileinfo.st_size; + buf = (BYTE*)M_Malloc(length); + count = read (handle, buf, length); + close (handle); + + if (count < length) + I_Error ("Couldn't read file %s", name); + + *buffer = buf; + return length; +} + //--------------------------------------------------------------------------- // // PROC M_FindResponseFile diff --git a/src/m_misc.h b/src/m_misc.h index 90844221f..9599306de 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -33,6 +33,7 @@ extern FGameConfigFile *GameConfig; bool M_WriteFile (char const *name, void *source, int length); int M_ReadFile (char const *name, BYTE **buffer); +int M_ReadFileMalloc (char const *name, BYTE **buffer); void M_FindResponseFile (void); // [RH] M_ScreenShot now accepts a filename parameter. From cc4e66f976ab93c6d88d8721b3011238a551fe26 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 25 Nov 2014 16:44:12 +0100 Subject: [PATCH 262/313] - Fix the division by 0, improve comments. - Avoid doing the division if the result would be outside the ]0,1<<24[ range: -- if the numerator is nonpositive, ie <=0, truncate the result to 0, -- if the numerator is greater or equal than the denominator, the result will be outside the allowed range, hence truncate the result to 1<<24. -- otherwise, the result will be inside the range. Knowing that the denominator is greater than the numerator, if the numerator has the last 24 bits non zero, the denominator can't be less than 1<<24, hence the denominator won't be truncated to 0. - Add comment details to help who doesn't know math. Big deal! --- src/p_map.cpp | 56 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index c0e5c3baa..b3718ce71 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -737,18 +737,50 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) { // Find the point on the line closest to the actor's center, and use // that to calculate openings // [EP] Use 64 bit integers in order to keep the exact result of the - // multiplication, because in the worst case, which is by the map limit - // (32767 units, which is 2147418112 in fixed_t notation), the result - // would occupy 62 bits (if I consider also the addition with another - // and possible 62 bit value, it's 63 bits). - // This privilege could not be available if the starting data would be - // 64 bit long. - // With this, the division is exact as the 32 bit float counterpart, - // though I don't know why I had to discard the first 24 bits from the - // divisor. - SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + (SQWORD(tm.y - ld->v1->y)*ld->dy)); - SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); - fixed_t r = (fixed_t)(r_num / r_den); + // multiplication, because in the case the vertexes have both the + // distance coordinates equal to the map limit (32767 units, which is + // 2147418112 in fixed_t notation), the product result would occupy + // 62 bits and the sum of two products would occupy 63 bits + // in the worst case. If instead the vertexes are very close (1 in + // fixed_t notation, which is 1.52587890625e-05 in float notation), the + // product and the sum can be 1 in the worst case, which is very tiny. + SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + // The denominator is always positive. Use this to avoid useless + // calculations. + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy); + fixed_t r = 0; + if (r_num <= 0) { + // [EP] The numerator is less or equal to zero, hence the closest + // point on the line is the first vertex. Truncate the result to 0. + r = 0; + } else if (r_num >= r_den) { + // [EP] The division is greater or equal to 1, hence the closest + // point on the line is the second vertex. Truncate the result to + // 1 << 24. + r = (1 << 24); + } else { + // [EP] Deal with the limited bits. The original formula is: + // r = (r_num << 24) / r_den, + // but r_num might be big enough to make the shift overflow. + // Since the numerator can't be saved in a 128bit integer, + // the denominator must be right shifted. If the denominator is + // less than (1 << 24), there would be a division by zero. + // Thanks to the fact that in this code path the denominator is less + // than the numerator, it's possible to avoid this bad situation by + // just checking the last 24 bits of the numerator. + if ((r_num >> (63-24)) != 0) { + // [EP] In fact, if the numerator is greater than + // (1 << (63-24)), the denominator must be greater than + // (1 << (63-24)), hence the denominator won't be zero after + // the right shift by 24 places. + r = (r_num)/(r_den >> 24); + } else { + // [EP] Having the last 24 bits all zero allows right shifting + // the numerator by 24 bits. + r = (r_num << 24)/r_den; + } + } /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, ld->frontsector->floorplane.b, From 8fbed78c21b2b8dce13fa3fbc1b434d34c7d71e8 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 25 Nov 2014 19:15:25 +0100 Subject: [PATCH 263/313] - Add new function for the new slope calculations. Fixed also two MSVC warnings. --- src/p_map.cpp | 110 +++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index b3718ce71..604015700 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -74,6 +74,69 @@ TArray spechit; // Temporary holder for thing_sectorlist threads msecnode_t* sector_list = NULL; // phares 3/16/98 +//========================================================================== +// +// GetCoefficientClosestPointInLine24 +// +// Formula: (dotProduct(ldv1 - tm, ld) << 24) / dotProduct(ld, ld) +// with: ldv1 = (ld->v1->x, ld->v1->y), tm = (tm.x, tm.y) +// and ld = (ld->dx, ld->dy) +// Returns truncated to range [0, 1 << 24]. +// +//========================================================================== + +static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm) +{ + // [EP] Use 64 bit integers in order to keep the exact result of the + // multiplication, because in the case the vertexes have both the + // distance coordinates equal to the map limit (32767 units, which is + // 2147418112 in fixed_t notation), the product result would occupy + // 62 bits and the sum of two products would occupy 63 bits + // in the worst case. If instead the vertexes are very close (1 in + // fixed_t notation, which is 1.52587890625e-05 in float notation), the + // product and the sum can be 1 in the worst case, which is very tiny. + + SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + + // The denominator is always positive. Use this to avoid useless + // calculations. + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy); + + if (r_num <= 0) { + // [EP] The numerator is less or equal to zero, hence the closest + // point on the line is the first vertex. Truncate the result to 0. + return 0; + } + + if (r_num >= r_den) { + // [EP] The division is greater or equal to 1, hence the closest + // point on the line is the second vertex. Truncate the result to + // 1 << 24. + return (1 << 24); + } + + // [EP] Deal with the limited bits. The original formula is: + // r = (r_num << 24) / r_den, + // but r_num might be big enough to make the shift overflow. + // Since the numerator can't be saved in a 128bit integer, + // the denominator must be right shifted. If the denominator is + // less than (1 << 24), there would be a division by zero. + // Thanks to the fact that in this code path the denominator is greater + // than the numerator, it's possible to avoid this bad situation by + // just checking the last 24 bits of the numerator. + if ((r_num >> (63-24)) != 0) { + // [EP] In fact, if the numerator is greater than + // (1 << (63-24)), the denominator must be greater than + // (1 << (63-24)), hence the denominator won't be zero after + // the right shift by 24 places. + return (fixed_t)(r_num/(r_den >> 24)); + } + // [EP] Having the last 24 bits all zero allows right shifting + // the numerator by 24 bits. + return (fixed_t)((r_num << 24)/r_den); +} + //========================================================================== // // PIT_FindFloorCeiling @@ -736,51 +799,8 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) else { // Find the point on the line closest to the actor's center, and use // that to calculate openings - // [EP] Use 64 bit integers in order to keep the exact result of the - // multiplication, because in the case the vertexes have both the - // distance coordinates equal to the map limit (32767 units, which is - // 2147418112 in fixed_t notation), the product result would occupy - // 62 bits and the sum of two products would occupy 63 bits - // in the worst case. If instead the vertexes are very close (1 in - // fixed_t notation, which is 1.52587890625e-05 in float notation), the - // product and the sum can be 1 in the worst case, which is very tiny. - SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + - (SQWORD(tm.y - ld->v1->y)*ld->dy)); - // The denominator is always positive. Use this to avoid useless - // calculations. - SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy); - fixed_t r = 0; - if (r_num <= 0) { - // [EP] The numerator is less or equal to zero, hence the closest - // point on the line is the first vertex. Truncate the result to 0. - r = 0; - } else if (r_num >= r_den) { - // [EP] The division is greater or equal to 1, hence the closest - // point on the line is the second vertex. Truncate the result to - // 1 << 24. - r = (1 << 24); - } else { - // [EP] Deal with the limited bits. The original formula is: - // r = (r_num << 24) / r_den, - // but r_num might be big enough to make the shift overflow. - // Since the numerator can't be saved in a 128bit integer, - // the denominator must be right shifted. If the denominator is - // less than (1 << 24), there would be a division by zero. - // Thanks to the fact that in this code path the denominator is less - // than the numerator, it's possible to avoid this bad situation by - // just checking the last 24 bits of the numerator. - if ((r_num >> (63-24)) != 0) { - // [EP] In fact, if the numerator is greater than - // (1 << (63-24)), the denominator must be greater than - // (1 << (63-24)), hence the denominator won't be zero after - // the right shift by 24 places. - r = (r_num)/(r_den >> 24); - } else { - // [EP] Having the last 24 bits all zero allows right shifting - // the numerator by 24 bits. - r = (r_num << 24)/r_den; - } - } + fixed_t r = GetCoefficientClosestPointInLine24(ld, tm); + /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, ld->frontsector->floorplane.b, From 629f3c1a8acd25affcd201ec36d9192d1c66565d Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 25 Nov 2014 19:22:56 +0100 Subject: [PATCH 264/313] - Oops comment typo. --- 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 604015700..3097ae824 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -132,8 +132,8 @@ static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm // the right shift by 24 places. return (fixed_t)(r_num/(r_den >> 24)); } - // [EP] Having the last 24 bits all zero allows right shifting - // the numerator by 24 bits. + // [EP] Having the last 24 bits all zero allows left shifting + // the numerator by 24 bits without overflow. return (fixed_t)((r_num << 24)/r_den); } From 08570ec48ead6dfd77a9f58dc3897ff045a64a34 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 25 Nov 2014 13:24:35 -0600 Subject: [PATCH 265/313] - Added flags for A_Fade functions: - FTF_REMOVE: Removes the actor when the alpha hits a certain level. - - A_FadeIn - 1.0 - - A_FadeOut - 0.0 - - A_FadeTo - Alpha target level reached - FTF_CLAMP: Automatically fixes the alpha so it won't leave the range [0.0, 1.0]. --- src/thingdef/thingdef_codeptr.cpp | 39 ++++++++++++++++++++++++------ wadsrc/static/actors/actor.txt | 6 ++--- wadsrc/static/actors/constants.txt | 7 ++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a668e589b..5629edd9e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2354,18 +2354,33 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) // 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; + reduce = FRACUNIT / 10; } self->RenderStyle.Flags &= ~STYLEF_Alpha1; self->alpha += reduce; - // Should this clamp alpha to 1.0? + + if (self->alpha >= (FRACUNIT * 1)) + { + if (flags & FTF_CLAMP) + self->alpha = (FRACUNIT * 1); + if (flags & FTF_REMOVE) + self->Destroy(); + } } //=========================================================================== @@ -2379,7 +2394,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) { ACTION_PARAM_START(2); ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_BOOL(remove, 1); + ACTION_PARAM_INT(flags, 1); if (reduce == 0) { @@ -2387,9 +2402,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) } self->RenderStyle.Flags &= ~STYLEF_Alpha1; self->alpha -= reduce; - if (self->alpha <= 0 && remove) + if (self->alpha <= 0) { - self->Destroy(); + if (flags & FTF_CLAMP) + self->alpha = 0; + if (flags & FTF_REMOVE) + self->Destroy(); } } @@ -2406,7 +2424,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) ACTION_PARAM_START(3); ACTION_PARAM_FIXED(target, 0); ACTION_PARAM_FIXED(amount, 1); - ACTION_PARAM_BOOL(remove, 2); + ACTION_PARAM_INT(flags, 2); self->RenderStyle.Flags &= ~STYLEF_Alpha1; @@ -2428,7 +2446,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) self->alpha = target; } } - if (self->alpha == target && remove) + 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(); } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 468b0f7b5..c8e689dae 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -222,9 +222,9 @@ ACTOR Actor native //: Thinker action native A_Log(string whattoprint); action native A_LogInt(int whattoprint); action native A_SetTranslucent(float alpha, int style = 0); - action native A_FadeIn(float reduce = 0.1); - action native A_FadeOut(float reduce = 0.1, bool remove = true); - action native A_FadeTo(float target, float amount = 0.1, bool remove = false); + 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_SetMass(int mass); action native A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index d9da11b2d..22914fbfc 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -405,6 +405,13 @@ enum RMVF_EVERYTHING = 1 << 3, }; +// Flags for A_Fade* +enum +{ + FTF_REMOVE = 1 << 0, + FTF_CLAMP = 1 << 1, +}; + // This is only here to provide one global variable for testing. native int testglobalvar; From e1fdcdfb93c532e5f32d53a02ccf36773bb23784 Mon Sep 17 00:00:00 2001 From: khokh2001 Date: Fri, 28 Nov 2014 04:07:33 +0900 Subject: [PATCH 266/313] opl3 emulator update --- src/oplsynth/nukedopl3.cpp | 1222 ++++++++++++++++-------------------- src/oplsynth/nukedopl3.h | 222 +++++-- 2 files changed, 695 insertions(+), 749 deletions(-) diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp index 90673d855..a520f68e1 100644 --- a/src/oplsynth/nukedopl3.cpp +++ b/src/oplsynth/nukedopl3.cpp @@ -23,11 +23,11 @@ Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. - OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): OPL2 ROMs. */ -//version 1.4.2 +//version 1.5 /* Changelog: v1.1: @@ -52,6 +52,8 @@ (Not released) v1.4.2: Version for ZDoom. + v1.5: + Optimizations */ @@ -78,152 +80,17 @@ #include #include "nukedopl3.h" -// Channel types - -enum { - ch_4op2, - ch_2op, - ch_4op, - ch_drum -}; - -// Envelope generator states - -enum { - eg_off, - eg_attack, - eg_decay, - eg_sustain, - eg_release -}; - -// Envelope key types - -enum { - egk_norm = 1, - egk_drum = 2 -}; - -// -// logsin table -// - -static const Bit16u logsinrom[256] = { - 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, - 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, - 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, - 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, - 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, - 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, - 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, - 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, - 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, - 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, - 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, - 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, - 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, - 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, - 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, - 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 -}; - -// -// exp table -// - -static const Bit16u exprom[256] = { - 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, - 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, - 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, - 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, - 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, - 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, - 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, - 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, - 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, - 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, - 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, - 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, - 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, - 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, - 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, - 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa -}; - -// -// freq mult table multiplied by 2 -// -// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 -// - -static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; - -// -// ksl table -// - -static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; - -static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; - -// -// LFO vibrato -// - -static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; -static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; - -// -// envelope generator constants -// - -static const Bit8u eg_incstep[3][4][8] = { - { { 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, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } -}; - -// -// address decoding -// - -static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - -static const Bit8u op_offset[18] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0b, 0x09, 0x0c, 0x0a, 0x0d, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15 }; - - - -static const Bit8u eg_incdesc[16] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 -}; - -static const Bit8s eg_incsh[16] = { - 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 -}; - -typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); -typedef void(*envelope_genfunc)(slot *slott); - -// -// Phase generator -// - -void PG_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - Bit16u fnum = chan->f_number; - if (slt->vibrato) { - Bit8u fnum_high = chan->f_number >> (7 + vib_table[opl->vib_pos] + (!opl->dvb)); - fnum += fnum_high * vibsgn_table[opl->vib_pos]; - } - slt->PG_pos += (((fnum << chan->block) >> 1) * mt[slt->mult]) >> 1; -} - // // Envelope generator // +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl_slot *slott); + Bit16s envelope_calcexp(Bit32u level) { + if (level > 0x1fff) { + level = 0x1fff; + } return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); } @@ -352,12 +219,12 @@ envelope_sinfunc envelope_sin[8] = { envelope_calcsin7 }; -void envelope_gen_off(slot *slott); -void envelope_gen_change(slot *slott); -void envelope_gen_attack(slot *slott); -void envelope_gen_decay(slot *slott); -void envelope_gen_sustain(slot *slott); -void envelope_gen_release(slot *slott); +void envelope_gen_off(opl_slot *slott); +void envelope_gen_change(opl_slot *slott); +void envelope_gen_attack(opl_slot *slott); +void envelope_gen_decay(opl_slot *slott); +void envelope_gen_sustain(opl_slot *slott); +void envelope_gen_release(opl_slot *slott); envelope_genfunc envelope_gen[6] = { envelope_gen_off, @@ -377,222 +244,215 @@ enum envelope_gen_num { envelope_gen_num_change = 5 }; -void envelope_gen_off(slot *slott) { - slott->EG_out = 0x1ff; -} - -void envelope_gen_change(slot *slott) { - slott->eg_gen = slott->eg_gennext; -} - -void envelope_gen_attack(slot *slott) { - slott->EG_out += ((~slott->EG_out) *slott->eg_inc) >> 3; - if (slott->EG_out < 0x00) { - slott->EG_out = 0x00; - } - if (slott->EG_out == 0x00) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_decay; - } -} - -void envelope_gen_decay(slot *slott) { - slott->EG_out += slott->eg_inc; - if (slott->EG_out >= slott->EG_sl << 4) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_sustain; - } -} - -void envelope_gen_sustain(slot *slott) { - if (!slott->EG_type) { - envelope_gen_release(slott); - } -} - -void envelope_gen_release(slot *slott) { - slott->EG_out += slott->eg_inc; - if (slott->EG_out >= 0x1ff) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_off; - } -} - -Bit8u EG_CalcRate(chip *opl, Bit8u op, Bit8u rate) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - if (rate == 0x00) { +Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) { + if (reg_rate == 0x00) { return 0x00; } - Bit8u rof = slt->ksr ? chan->ksv : (chan->ksv >> 2); - Bit8u rat = (rate << 2) + rof; - if (rat > 0x3c) { - rat = 0x3c; + Bit8u rate = (reg_rate << 2) + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); + if (rate > 0x3c) { + rate = 0x3c; } - return rat; + return rate; } -void envelope_calc(chip *opl, Bit8u op) { - slot *slott = &opl->OPs[op]; - Bit16u timer = opl->timer; - Bit8u rate_h, rate_l; - Bit8u rate; - Bit8u reg_rate = 0;; - switch (slott->eg_gen) { +void envelope_update_ksl(opl_slot *slot) { + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 1) - ((slot->channel->block ^ 0x07) << 5) - 0x20; + if (ksl < 0) { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +void envelope_update_rate(opl_slot *slot) { + switch (slot->eg_gen) { + case envelope_gen_num_off: + slot->eg_rate = 0; + break; case envelope_gen_num_attack: - reg_rate = slott->EG_ar; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_ar); break; case envelope_gen_num_decay: - reg_rate = slott->EG_dr; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_dr); break; case envelope_gen_num_sustain: case envelope_gen_num_release: - reg_rate = slott->EG_rr; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_rr); break; } - rate = EG_CalcRate(opl, op, reg_rate); - rate_h = rate >> 2; - rate_l = rate & 3; +} + +void envelope_gen_off(opl_slot *slot) { + slot->eg_rout = 0x1ff; +} + +void envelope_gen_change(opl_slot *slot) { + slot->eg_gen = slot->eg_gennext; + envelope_update_rate(slot); +} + +void envelope_gen_attack(opl_slot *slot) { + slot->eg_rout += ((~slot->eg_rout) *slot->eg_inc) >> 3; + if (slot->eg_rout < 0x00) { + slot->eg_rout = 0x00; + } + if (!slot->eg_rout) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_decay; + } +} + +void envelope_gen_decay(opl_slot *slot) { + slot->eg_rout += slot->eg_inc; + if (slot->eg_rout >= slot->reg_sl << 4) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_sustain; + } +} + +void envelope_gen_sustain(opl_slot *slot) { + if (!slot->reg_type) { + envelope_gen_release(slot); + } +} + +void envelope_gen_release(opl_slot *slot) { + slot->eg_rout += slot->eg_inc; + if (slot->eg_rout >= 0x1ff) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_off; + } +} + +void envelope_calc(opl_slot *slot) { + Bit8u rate_h, rate_l; + rate_h = slot->eg_rate >> 2; + rate_l = slot->eg_rate & 3; Bit8u inc = 0; - if (slott->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { + if (slot->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { inc = 8; } else if (eg_incsh[rate_h] > 0) { - if ((timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((timer) >> eg_incsh[rate_h]) & 0x07]; + if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((slot->chip->timer) >> eg_incsh[rate_h]) & 0x07]; } } else { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l][timer & 0x07] << (-eg_incsh[rate_h]); + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); } - slott->eg_inc = inc; - envelope_gen[slott->eg_gen](slott); + slot->eg_inc = inc; + envelope_gen[slot->eg_gen](slot); + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; } -void EG_UpdateKSL(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - Bit8u fnum_high = (chan->f_number >> 6) & 0x0f; - Bit16s ksl = (kslrom[fnum_high] << 1) - ((chan->block ^ 0x07) << 5) - 0x20; - if (ksl < 0x00) { - ksl = 0x00; +void eg_keyon(opl_slot *slot, Bit8u type) { + if (!slot->key) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_attack; + slot->pg_phase = 0x00; } - slt->EG_ksl = ksl >> kslshift[slt->ksl]; + slot->key |= type; } -void EG_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - envelope_calc(opl, op); - slt->EG_mout = slt->EG_out + slt->EG_ksl + (slt->EG_tl << 2); - if (slt->tremolo) { - slt->EG_mout += opl->trem_val; - } - if (slt->EG_mout > 0x1ff) { - slt->EG_mout = 0x1ff; - } -} - -void EG_KeyOn(chip *opl, Bit8u op, Bit8u type) { - slot *slt = &opl->OPs[op]; - if (!slt->key) { - slt->EG_state = eg_attack; - slt->eg_gen = envelope_gen_num_change; - slt->eg_gennext = envelope_gen_num_attack; - slt->PG_pos = 0; - } - slt->key |= type; -} - -void EG_KeyOff(chip *opl, Bit8u op, Bit8u type) { - slot *slt = &opl->OPs[op]; - if (slt->key) { - slt->key &= (~type); - if (slt->key == 0x00) { - slt->EG_state = eg_release; - slt->eg_gen = envelope_gen_num_change; - slt->eg_gennext = envelope_gen_num_release; +void eg_keyoff(opl_slot *slot, Bit8u type) { + if (slot->key) { + slot->key &= (~type); + if (!slot->key) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_release; } } } +// +// Phase Generator +// + +void pg_generate(opl_slot *slot) { + Bit16u f_num = slot->channel->f_num; + if (slot->reg_vib) { + Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10)&0x07] + (0x01 - slot->chip->dvb)); + f_num += f_num_high * vibsgn_table[(slot->chip->timer >> 10) & 0x07]; + } + slot->pg_phase += (((f_num << slot->channel->block) >> 1) * mt[slot->reg_mult]) >> 1; +} + // // Noise Generator // -void N_Generate(chip *opl) { - if (opl->noise & 1) { - opl->noise ^= 0x800302; +void n_generate(opl_chip *chip) { + if (chip->noise & 0x01) { + chip->noise ^= 0x800302; } - opl->noise >>= 1; + chip->noise >>= 1; } // -// Operator(Slot) +// Slot // -void OP_Update20(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->tremolo = (opl->opl_memory[0x20 + slt->offset] >> 7); - slt->vibrato = (opl->opl_memory[0x20 + slt->offset] >> 6) & 0x01; - slt->EG_type = (opl->opl_memory[0x20 + slt->offset] >> 5) & 0x01; - slt->ksr = (opl->opl_memory[0x20 + slt->offset] >> 4) & 0x01; - slt->mult = (opl->opl_memory[0x20 + slt->offset]) & 0x0f; +void slot_write20(opl_slot *slot,Bit8u data) { + if ((data >> 7) & 0x01) { + slot->trem = &slot->chip->tremval; + } + else { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; + envelope_update_rate(slot); } -void OP_Update40(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_tl = (opl->opl_memory[0x40 + slt->offset]) & 0x3f; - slt->ksl = (opl->opl_memory[0x40 + slt->offset] >> 6) & 0x03; - EG_UpdateKSL(opl, op); +void slot_write40(opl_slot *slot, Bit8u data) { + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + envelope_update_ksl(slot); } -void OP_Update60(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_dr = (opl->opl_memory[0x60 + slt->offset]) & 0x0f; - slt->EG_ar = (opl->opl_memory[0x60 + slt->offset] >> 4) & 0x0f; +void slot_write60(opl_slot *slot, Bit8u data) { + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; + envelope_update_rate(slot); } -void OP_Update80(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_rr = (opl->opl_memory[0x80 + slt->offset]) & 0x0f; - slt->EG_sl = (opl->opl_memory[0x80 + slt->offset] >> 4) & 0x0f; - if (slt->EG_sl == 0x0f) { - slt->EG_sl = 0x1f; +void slot_write80(opl_slot *slot, Bit8u data) { + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; + envelope_update_rate(slot); +} + +void slot_writee0(opl_slot *slot, Bit8u data) { + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) { + slot->reg_wf &= 0x03; } } -void OP_UpdateE0(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->waveform = opl->opl_memory[0xe0 + slt->offset] & 0x07; - if (!opl->newm) { - slt->waveform &= 0x03; +void slot_generatephase(opl_slot *slot, Bit16u phase) { + slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out); +} + +void slot_generate(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9) + (*slot->mod), slot->eg_out); +} + +void slot_generatezm(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9), slot->eg_out); +} + +void slot_calgfb(opl_slot *slot) { + slot->prout[1] = slot->prout[0]; + slot->prout[0] = slot->out; + if (slot->channel->fb != 0x00) { + slot->fbmod = (slot->prout[0] + slot->prout[1]) >> (0x09 - slot->channel->fb); } -} - -void OP_GeneratePhase(chip *opl, Bit8u op, Bit16u phase) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform](phase, slt->EG_out); -} - -void OP_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform]((Bit16u)((slt->PG_pos >> 9) + (*slt->mod)), slt->EG_mout); -} - -void OP_GenerateZM(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform]((Bit16u)(slt->PG_pos >> 9), slt->EG_mout); -} - -void OP_CalcFB(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - slt->prevout[1] = slt->prevout[0]; - slt->prevout[0] = slt->out; - if (chan->feedback) { - slt->fbmod = (slt->prevout[0] + slt->prevout[1]) >> chan->feedback; - } else { - slt->fbmod = 0; + else { + slot->fbmod = 0; } } @@ -600,301 +460,319 @@ void OP_CalcFB(chip *opl, Bit8u op) { // Channel // -void CH_UpdateRhythm(chip *opl) { - opl->rhythm = (opl->opl_memory[0xbd] & 0x3f); - if (opl->rhythm & 0x20) { - for (Bit8u i = 6; i < 9; i++) { - opl->Channels[i].chtype = ch_drum; +void chan_setupalg(opl_channel *channel); + +void chan_updaterhythm(opl_chip *chip, Bit8u data) { + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) { + chip->channel[6].out[0] = &chip->slot[13].out; + chip->channel[6].out[1] = &chip->slot[13].out; + chip->channel[6].out[2] = &chip->zeromod; + chip->channel[6].out[3] = &chip->zeromod; + chip->channel[7].out[0] = &chip->slot[14].out; + chip->channel[7].out[1] = &chip->slot[14].out; + chip->channel[7].out[2] = &chip->slot[15].out; + chip->channel[7].out[3] = &chip->slot[15].out; + chip->channel[8].out[0] = &chip->slot[16].out; + chip->channel[8].out[1] = &chip->slot[16].out; + chip->channel[8].out[2] = &chip->slot[17].out; + chip->channel[8].out[3] = &chip->slot[17].out; + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_drum; } - //HH - if (opl->rhythm & 0x01) { - EG_KeyOn(opl, 14, egk_drum); - } else { - EG_KeyOff(opl, 14, egk_drum); + //hh + if (chip->rhy & 0x01) { + eg_keyon(&chip->slot[14], egk_drum); } - //TC - if (opl->rhythm & 0x02) { - EG_KeyOn(opl, 17, egk_drum); - } else { - EG_KeyOff(opl, 17, egk_drum); + else { + eg_keyoff(&chip->slot[14], egk_drum); } - //TOM - if (opl->rhythm & 0x04) { - EG_KeyOn(opl, 16, egk_drum); - } else { - EG_KeyOff(opl, 16, egk_drum); + //tc + if (chip->rhy & 0x02) { + eg_keyon(&chip->slot[17], egk_drum); } - //SD - if (opl->rhythm & 0x08) { - EG_KeyOn(opl, 15, egk_drum); - } else { - EG_KeyOff(opl, 15, egk_drum); + else { + eg_keyoff(&chip->slot[17], egk_drum); } - //BD - if (opl->rhythm & 0x10) { - EG_KeyOn(opl, 12, egk_drum); - EG_KeyOn(opl, 13, egk_drum); - } else { - EG_KeyOff(opl, 12, egk_drum); - EG_KeyOff(opl, 13, egk_drum); + //tom + if (chip->rhy & 0x04) { + eg_keyon(&chip->slot[16], egk_drum); } - } else { - for (Bit8u i = 6; i < 9; i++) { - opl->Channels[i].chtype = ch_2op; + else { + eg_keyoff(&chip->slot[16], egk_drum); + } + //sd + if (chip->rhy & 0x08) { + eg_keyon(&chip->slot[15], egk_drum); + } + else { + eg_keyoff(&chip->slot[15], egk_drum); + } + //bd + if (chip->rhy & 0x10) { + eg_keyon(&chip->slot[12], egk_drum); + eg_keyon(&chip->slot[13], egk_drum); + } + else { + eg_keyoff(&chip->slot[12], egk_drum); + eg_keyoff(&chip->slot[13], egk_drum); + } + } + else { + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_2op; + chan_setupalg(&chip->channel[chnum]); } } } -void CH_UpdateAB0(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm && chan->chtype == ch_4op2) { +void chan_writea0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { return; } - Bit16u f_number = (opl->opl_memory[0xa0 + chan->offset]) | (((opl->opl_memory[0xb0 + chan->offset]) & 0x03) << 8); - Bit8u block = ((opl->opl_memory[0xb0 + chan->offset]) >> 2) & 0x07; - Bit8u ksv = block * 2 | ((f_number >> (9 - opl->nts)) & 0x01); - chan->f_number = f_number; - chan->block = block; - chan->ksv = ksv; - EG_UpdateKSL(opl, ch * 2); - EG_UpdateKSL(opl, ch * 2 + 1); - OP_Update60(opl, ch * 2); - OP_Update60(opl, ch * 2 + 1); - OP_Update80(opl, ch * 2); - OP_Update80(opl, ch * 2 + 1); - if (opl->newm && chan->chtype == ch_4op) { - chan = &opl->Channels[ch + 3]; - chan->f_number = f_number; - chan->block = block; - chan->ksv = ksv; - EG_UpdateKSL(opl, (ch + 3) * 2); - EG_UpdateKSL(opl, (ch + 3) * 2 + 1); - OP_Update60(opl, (ch + 3) * 2); - OP_Update60(opl, (ch + 3) * 2 + 1); - OP_Update80(opl, (ch + 3) * 2); - OP_Update80(opl, (ch + 3) * 2 + 1); + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); } } -void CH_SetupAlg(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (chan->alg & 0x08) { +void chan_writeb0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { return; } - if (chan->alg & 0x04) { - switch (chan->alg & 0x03) { - case 0: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 1: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; - opl->OPs[ch * 2].mod = &opl->zm; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 2: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 3: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->zm; - break; - } - } else { - switch (chan->alg & 0x01) { - case 0: - opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 1: - opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; - opl->OPs[ch * 2 + 1].mod = &opl->zm; - break; - } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); } } -void CH_UpdateC0(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - Bit8u fb = (opl->opl_memory[0xc0 + chan->offset] & 0x0e) >> 1; - chan->feedback = fb ? (9 - fb) : 0; - chan->con = opl->opl_memory[0xc0 + chan->offset] & 0x01; - chan->alg = chan->con; - if (opl->newm) { - if (chan->chtype == ch_4op) { - channel *chan1 = &opl->Channels[ch + 3]; - chan1->alg = 0x04 | (chan->con << 1) | (chan1->con); - chan->alg = 0x08; - CH_SetupAlg(opl, ch + 3); - } else if (chan->chtype == ch_4op2) { - channel *chan1 = &opl->Channels[ch - 3]; - chan->alg = 0x04 | (chan1->con << 1) | (chan->con); - chan1->alg = 0x08; - CH_SetupAlg(opl, ch); - } else { - CH_SetupAlg(opl, ch); - } - } else { - CH_SetupAlg(opl, ch); - } - if (opl->newm) { - chan->cha = ((opl->opl_memory[0xc0 + chan->offset] >> 4) & 0x01) ? ~0 : 0; - chan->chb = ((opl->opl_memory[0xc0 + chan->offset] >> 5) & 0x01) ? ~0 : 0; - chan->chc = ((opl->opl_memory[0xc0 + chan->offset] >> 6) & 0x01) ? ~0 : 0; - chan->chd = ((opl->opl_memory[0xc0 + chan->offset] >> 7) & 0x01) ? ~0 : 0; - } else { - opl->Channels[ch].cha = opl->Channels[ch].chb = ~0; - opl->Channels[ch].chc = opl->Channels[ch].chd = 0; - } -} - -void CH_Set2OP(chip *opl) { - for (Bit8u i = 0; i < 18; i++) { - opl->Channels[i].chtype = ch_2op; - CH_UpdateC0(opl, i); - } -} - -void CH_Set4OP(chip *opl) { - for (Bit8u i = 0; i < 3; i++) { - if ((opl->opl_memory[0x104] >> i) & 0x01) { - opl->Channels[i].chtype = ch_4op; - opl->Channels[i + 3].chtype = ch_4op2; - CH_UpdateC0(opl, i); - CH_UpdateC0(opl, i + 3); - } - if ((opl->opl_memory[0x104] >> (i + 3)) & 0x01) { - opl->Channels[i + 9].chtype = ch_4op; - opl->Channels[i + 3 + 9].chtype = ch_4op2; - CH_UpdateC0(opl, i + 9); - CH_UpdateC0(opl, i + 3 + 9); - } - } -} - -void CH_GenerateRhythm(chip *opl) { - if (opl->rhythm & 0x20) { - channel *chan6 = &opl->Channels[6]; - channel *chan7 = &opl->Channels[7]; - channel *chan8 = &opl->Channels[8]; - slot *slt12 = &opl->OPs[12]; - slot *slt13 = &opl->OPs[13]; - slot *slt14 = &opl->OPs[14]; - slot *slt15 = &opl->OPs[15]; - slot *slt16 = &opl->OPs[16]; - slot *slt17 = &opl->OPs[17]; - //BD - OP_Generate(opl, 12); - OP_Generate(opl, 13); - chan6->out = slt13->out * 2; - Bit16u P14 = (slt14->PG_pos >> 9) & 0x3ff; - Bit16u P17 = (slt17->PG_pos >> 9) & 0x3ff; - Bit16u phase = 0; - // HH TC Phase bit - Bit16u PB = ((P14 & 0x08) | (((P14 >> 5) ^ P14) & 0x04) | (((P17 >> 2) ^ P17) & 0x08)) ? 0x01 : 0x00; - //HH - phase = (PB << 9) | (0x34 << ((PB ^ (opl->noise & 0x01) << 1))); - OP_GeneratePhase(opl, 14, phase); - //SD - phase = (0x100 << ((P14 >> 8) & 0x01)) ^ ((opl->noise & 0x01) << 8); - OP_GeneratePhase(opl, 15, phase); - //TT - OP_GenerateZM(opl, 16); - //TC - phase = 0x100 | (PB << 9); - OP_GeneratePhase(opl, 17, phase); - chan7->out = (slt14->out + slt15->out) * 2; - chan8->out = (slt16->out + slt17->out) * 2; - } -} - -void CH_Generate(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (chan->chtype == ch_drum) { +void chan_setupalg(opl_channel *channel) { + if (channel->chtype == ch_drum) { return; } - if (chan->alg & 0x08) { - chan->out = 0; + if (channel->alg & 0x08) { return; - } else if (chan->alg & 0x04) { - OP_Generate(opl, (ch - 3) * 2); - OP_Generate(opl, (ch - 3) * 2 + 1); - OP_Generate(opl, ch * 2); - OP_Generate(opl, ch * 2 + 1); - switch (chan->alg & 0x03) { - case 0: - chan->out = opl->OPs[ch * 2 + 1].out; + } + if (channel->alg & 0x04) { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 1: - chan->out = opl->OPs[(ch - 3) * 2 + 1].out + opl->OPs[ch * 2 + 1].out; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 2: - chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 3: - chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; break; } } else { - OP_Generate(opl, ch * 2); - OP_Generate(opl, ch * 2 + 1); - switch (chan->alg & 0x01) { - case 0: - chan->out = opl->OPs[ch * 2 + 1].out; + switch (channel->alg & 0x01) { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 1: - chan->out = opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; } } } -void CH_Enable(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm) { - if (chan->chtype == ch_4op) { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); - EG_KeyOn(opl, (ch + 3) * 2, egk_norm); - EG_KeyOn(opl, (ch + 3) * 2 + 1, egk_norm); +void chan_writec0(opl_channel *channel, Bit8u data) { + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + chan_setupalg(channel->pair); } - else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); + else if (channel->chtype == ch_4op2) { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + chan_setupalg(channel); + } + else { + chan_setupalg(channel); } } else { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); + chan_setupalg(channel); + } + if (channel->chip->newm) { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else { + channel->cha = channel->chb = ~0; } } -void CH_Disable(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm) { - if (chan->chtype == ch_4op) { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); - EG_KeyOff(opl, (ch + 3) * 2, egk_norm); - EG_KeyOff(opl, (ch + 3) * 2 + 1, egk_norm); +void chan_generaterhythm(opl_chip *chip) { + if (chip->rhy & 0x20) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[0]); + slot_generate(channel6->slots[1]); + Bit16u phase14 = channel7->slots[0]->pg_phase & 0x3ff; + Bit16u phase17 = channel8->slots[1]->pg_phase & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //hh + phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1))); + slot_generatephase(channel7->slots[0], phase); + //sd + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + slot_generatephase(channel7->slots[1], phase); + //tt + slot_generatezm(channel8->slots[0]); + //tc + phase = 0x100 | (phasebit << 9); + slot_generatephase(channel8->slots[1], phase); + } +} + +void chan_generate(opl_channel *channel) { + if (channel->chtype == ch_drum) { + return; + } + if (channel->alg & 0x08) { + return; + } + if (channel->alg & 0x04) { + slot_generate(channel->pair->slots[0]); + slot_generate(channel->pair->slots[1]); + slot_generate(channel->slots[0]); + slot_generate(channel->slots[1]); + } + else { + slot_generate(channel->slots[0]); + slot_generate(channel->slots[1]); + } +} + +void chan_enable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + eg_keyon(channel->pair->slots[0], egk_norm); + eg_keyon(channel->pair->slots[1], egk_norm); } - else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); } } else { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + } +} + +void chan_disable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + eg_keyoff(channel->pair->slots[0], egk_norm); + eg_keyoff(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } + } + else { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } +} + +void chan_set4op(opl_chip *chip, Bit8u data) { + for (Bit8u bit = 0; bit < 6; bit++) { + Bit8u chnum = bit; + if (bit >= 3) { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } } } @@ -909,99 +787,52 @@ Bit16s limshort(Bit32s a) { } void NukedOPL3::Reset() { - for (Bit8u i = 0; i < 36; i++) { - opl3.OPs[i].PG_pos = 0; - opl3.OPs[i].PG_inc = 0; - opl3.OPs[i].EG_out = 0x1ff; - opl3.OPs[i].EG_mout = 0x1ff; - opl3.OPs[i].eg_inc = 0; - opl3.OPs[i].eg_gen = 0; - opl3.OPs[i].eg_gennext = 0; - opl3.OPs[i].EG_ksl = 0; - opl3.OPs[i].EG_ar = 0; - opl3.OPs[i].EG_dr = 0; - opl3.OPs[i].EG_sl = 0; - opl3.OPs[i].EG_rr = 0; - opl3.OPs[i].EG_state = eg_off; - opl3.OPs[i].EG_type = 0; - opl3.OPs[i].out = 0; - opl3.OPs[i].prevout[0] = 0; - opl3.OPs[i].prevout[1] = 0; - opl3.OPs[i].fbmod = 0; - opl3.OPs[i].offset = op_offset[i % 18] + ((i > 17) << 8); - opl3.OPs[i].mult = 0; - opl3.OPs[i].vibrato = 0; - opl3.OPs[i].tremolo = 0; - opl3.OPs[i].ksr = 0; - opl3.OPs[i].EG_tl = 0; - opl3.OPs[i].ksl = 0; - opl3.OPs[i].key = 0; - opl3.OPs[i].waveform = 0; + memset(&opl3, 0, sizeof(opl_chip)); + for (Bit8u slotnum = 0; slotnum < 36; slotnum++) { + opl3.slot[slotnum].channel = &opl3.channel[slotnum / 2]; + opl3.slot[slotnum].chip = &opl3; + opl3.slot[slotnum].mod = &opl3.zeromod; + opl3.slot[slotnum].eg_rout = 0x1ff; + opl3.slot[slotnum].eg_out = 0x1ff; + opl3.slot[slotnum].eg_gen = envelope_gen_num_off; + opl3.slot[slotnum].eg_gennext = envelope_gen_num_off; + opl3.slot[slotnum].trem = (Bit8u*)&opl3.zeromod; } - for (Bit8u i = 0; i < 9; i++) { - opl3.Channels[i].con = 0; - opl3.Channels[i + 9].con = 0; - opl3.Channels[i].chtype = ch_2op; - opl3.Channels[i + 9].chtype = ch_2op; - opl3.Channels[i].alg = 0; - opl3.Channels[i + 9].alg = 0; - opl3.Channels[i].offset = i; - opl3.Channels[i + 9].offset = 0x100 + i; - opl3.Channels[i].feedback = 0; - opl3.Channels[i + 9].feedback = 0; - opl3.Channels[i].out = 0; - opl3.Channels[i + 9].out = 0; - opl3.Channels[i].cha = ~0; - opl3.Channels[i + 9].cha = ~0; - opl3.Channels[i].chb = ~0; - opl3.Channels[i + 9].chb = ~0; - opl3.Channels[i].chc = 0; - opl3.Channels[i + 9].chc = 0; - opl3.Channels[i].chd = 0; - opl3.Channels[i + 9].chd = 0; - opl3.Channels[i].out = 0; - opl3.Channels[i + 9].out = 0; - opl3.Channels[i].f_number = 0; - opl3.Channels[i + 9].f_number = 0; - opl3.Channels[i].block = 0; - opl3.Channels[i + 9].block = 0; - opl3.Channels[i].ksv = 0; - opl3.Channels[i + 9].ksv = 0; - opl3.Channels[i].panl = (float)CENTER_PANNING_POWER; - opl3.Channels[i + 9].panl = (float)CENTER_PANNING_POWER; - opl3.Channels[i].panr = (float)CENTER_PANNING_POWER; - opl3.Channels[i + 9].panr = (float)CENTER_PANNING_POWER; + for (Bit8u channum = 0; channum < 18; channum++) { + opl3.channel[channum].slots[0] = &opl3.slot[2 * channum]; + opl3.channel[channum].slots[1] = &opl3.slot[2 * channum + 1]; + if ((channum % 9) < 3) { + opl3.channel[channum].pair = &opl3.channel[channum + 3]; + } + else if ((channum % 9) < 6) { + opl3.channel[channum].pair = &opl3.channel[channum - 3]; + } + opl3.channel[channum].chip = &opl3; + opl3.channel[channum].out[0] = &opl3.zeromod; + opl3.channel[channum].out[1] = &opl3.zeromod; + opl3.channel[channum].out[2] = &opl3.zeromod; + opl3.channel[channum].out[3] = &opl3.zeromod; + opl3.channel[channum].chtype = ch_2op; + opl3.channel[channum].cha = ~0; + opl3.channel[channum].chb = ~0; + opl3.channel[channum].fcha = 1.0; + opl3.channel[channum].fchb = 1.0; + chan_setupalg(&opl3.channel[channum]); } - memset(opl3.opl_memory, 0, 0x200); - opl3.newm = 0; - opl3.nts = 0; - opl3.rhythm = 0; - opl3.dvb = 0; - opl3.dam = 0; opl3.noise = 0x306600; - opl3.vib_pos = 0; - opl3.timer = 0; - opl3.trem_inc = 0; - opl3.trem_tval = 0; - opl3.trem_dir = 0; - opl3.trem_val = 0; - opl3.zm = 0; - CH_Set2OP(&opl3); } void NukedOPL3::WriteReg(int reg, int v) { v &= 0xff; reg &= 0x1ff; - Bit8u highbank = (reg >> 8) & 0x01; + Bit8u high = (reg >> 8) & 0x01; Bit8u regm = reg & 0xff; - opl3.opl_memory[reg & 0x1ff] = v; switch (regm & 0xf0) { case 0x00: - if (highbank) { + if (high) { switch (regm & 0x0f) { case 0x04: - CH_Set2OP(&opl3); - CH_Set4OP(&opl3); + chan_set4op(&opl3, v); break; case 0x05: opl3.newm = v & 0x01; @@ -1019,57 +850,57 @@ void NukedOPL3::WriteReg(int reg, int v) { case 0x20: case 0x30: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update20(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write20(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x40: case 0x50: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update40(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write40(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x60: case 0x70: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update60(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write60(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x80: case 0x90: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update80(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write80(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);; } break; case 0xe0: case 0xf0: if (ad_slot[regm & 0x1f] >= 0) { - OP_UpdateE0(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_writee0(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0xa0: if ((regm & 0x0f) < 9) { - CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writea0(&opl3.channel[9 * high + (regm & 0x0f)], v); } break; case 0xb0: - if (regm == 0xbd && !highbank) { + if (regm == 0xbd && !high) { opl3.dam = v >> 7; opl3.dvb = (v >> 6) & 0x01; - CH_UpdateRhythm(&opl3); + chan_updaterhythm(&opl3, v); } else if ((regm & 0x0f) < 9) { - CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writeb0(&opl3.channel[9 * high + (regm & 0x0f)], v); if (v & 0x20) { - CH_Enable(&opl3, 9 * highbank + (regm & 0x0f)); + chan_enable(&opl3.channel[9 * high + (regm & 0x0f)]); } else { - CH_Disable(&opl3, 9 * highbank + (regm & 0x0f)); + chan_disable(&opl3.channel[9 * high + (regm & 0x0f)]); } } break; case 0xc0: if ((regm & 0x0f) < 9) { - CH_UpdateC0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writec0(&opl3.channel[9 * high + (regm & 0x0f)], v); } break; } @@ -1077,54 +908,55 @@ void NukedOPL3::WriteReg(int reg, int v) { void NukedOPL3::Update(float* sndptr, int numsamples) { Bit32s outa, outb; - Bit8u ii = 0; for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { outa = 0; outb = 0; - for (ii = 0; ii < 36; ii++) { - OP_CalcFB(&opl3, ii); + for (Bit8u ii = 0; ii < 36; ii++) { + slot_calgfb(&opl3.slot[ii]); } - CH_GenerateRhythm(&opl3); - for (ii = 0; ii < 18; ii++) { - CH_Generate(&opl3, ii); + chan_generaterhythm(&opl3); + for (Bit8u ii = 0; ii < 18; ii++) { + chan_generate(&opl3.channel[ii]); + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *opl3.channel[ii].out[jj]; + } if (FullPan) { - outa += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panl); - outb += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panr); + outa += (Bit16s)(accm * opl3.channel[ii].fcha); + outb += (Bit16s)(accm * opl3.channel[ii].fchb); } else { - outa += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].cha); - outb += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].chb); + outa += (Bit16s)(accm & opl3.channel[ii].cha); + outb += (Bit16s)(accm & opl3.channel[ii].chb); } } - for (ii = 0; ii < 36; ii++) { - EG_Generate(&opl3, ii); - PG_Generate(&opl3, ii); - } - N_Generate(&opl3); - opl3.trem_inc++; - if (!(opl3.trem_inc & 0x3f)) { - if (!opl3.trem_dir) { - if (opl3.trem_tval == 105) { - opl3.trem_tval--; - opl3.trem_dir = 1; - } - else { - opl3.trem_tval++; - } - } - else { - if (opl3.trem_tval == 0) { - opl3.trem_tval++; - opl3.trem_dir = 0; - } - else { - opl3.trem_tval--; - } - } - opl3.trem_val = (opl3.trem_tval >> 2) >> ((!opl3.dam) << 1); + for (Bit8u ii = 0; ii < 36; ii++) { + envelope_calc(&opl3.slot[ii]); + pg_generate(&opl3.slot[ii]); } + n_generate(&opl3); opl3.timer++; - opl3.vib_pos = (opl3.timer >> 10) & 0x07; + if (!(opl3.timer & 0x3f)) { + if (!opl3.tremdir) { + if (opl3.tremtval == 105) { + opl3.tremtval--; + opl3.tremdir = 1; + } + else { + opl3.tremtval++; + } + } + else { + if (opl3.tremtval == 0) { + opl3.tremtval++; + opl3.tremdir = 0; + } + else { + opl3.tremtval--; + } + } + opl3.tremval = (opl3.tremtval >> 2) >> ((1 - opl3.dam) << 1); + } *sndptr++ += (float)(outa / 10240.0); *sndptr++ += (float)(outb / 10240.0); } @@ -1132,8 +964,8 @@ void NukedOPL3::Update(float* sndptr, int numsamples) { void NukedOPL3::SetPanning(int c, float left, float right) { if (FullPan) { - opl3.Channels[c].panl = left; - opl3.Channels[c].panr = right; + opl3.channel[c].fcha = left; + opl3.channel[c].fchb = right; } } @@ -1144,4 +976,4 @@ NukedOPL3::NukedOPL3(bool stereo) { OPLEmul *NukedOPL3Create(bool stereo) { return new NukedOPL3(stereo); -} +} \ No newline at end of file diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index 15f264c1a..ccf37fe14 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -5,12 +5,12 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. -* +* * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. -* +* * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -23,11 +23,11 @@ Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. - OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): OPL2 ROMs. */ -//version 1.4.2 +//version 1.5 #include "opl.h" #include "muslib.h" @@ -41,74 +41,188 @@ typedef SWORD Bit16s; typedef BYTE Bit8u; typedef SBYTE Bit8s; -struct channel { - Bit8u con; - Bit8u chtype; - Bit8u alg; - Bit16u offset; - Bit8u feedback; - Bit16u cha, chb, chc, chd; - Bit16s out; - Bit16u f_number; - Bit8u block; - Bit8u ksv; - float panl; - float panr; +// Channel types + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 }; -struct slot { - Bit32u PG_pos; - Bit32u PG_inc; - Bit16s EG_out; +// Envelope key types + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; + +static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; + +// +// LFO vibrato +// + +static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; +static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[3][4][8] = { + { { 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, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } +}; + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + +struct opl_chip; +struct opl_slot; +struct opl_channel; + +struct opl_slot { + opl_channel *channel; + opl_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout[2]; + Bit16s eg_rout; + Bit16s eg_out; Bit8u eg_inc; Bit8u eg_gen; Bit8u eg_gennext; - Bit16u EG_mout; - Bit8u EG_ksl; - Bit8u EG_ar; - Bit8u EG_dr; - Bit8u EG_sl; - Bit8u EG_rr; - Bit8u EG_state; - Bit8u EG_type; - Bit16s out; - Bit16s *mod; - Bit16s prevout[2]; - Bit16s fbmod; - Bit16u offset; - Bit8u mult; - Bit8u vibrato; - Bit8u tremolo; - Bit8u ksr; - Bit8u EG_tl; - Bit8u ksl; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; Bit8u key; - Bit8u waveform; + Bit32u pg_phase; }; +struct opl_channel { + opl_slot *slots[2]; + opl_channel *pair; + opl_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + float fcha, fchb; +}; -struct chip { - Bit8u opl_memory[0x200]; +struct opl_chip { + opl_channel channel[18]; + opl_slot slot[36]; + Bit16u timer; Bit8u newm; Bit8u nts; - Bit8u rhythm; Bit8u dvb; Bit8u dam; + Bit8u rhy; + Bit8u vibpos; + Bit8u tremval; + Bit8u tremtval; + Bit8u tremdir; Bit32u noise; - Bit16u vib_pos; - Bit16u timer; - Bit8u trem_inc; - Bit8u trem_tval; - Bit8u trem_dir; - Bit8u trem_val; - channel Channels[18]; - slot OPs[36]; - Bit16s zm; + Bit16s zeromod; }; + class NukedOPL3 : public OPLEmul { private: - chip opl3; + opl_chip opl3; bool FullPan; public: void Reset(); From 94f08aa593b180c22318e88d1ae925f91139f2b1 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 27 Nov 2014 15:12:33 -0600 Subject: [PATCH 267/313] - Added: Boolean to specify if A_Raise functions should perform CopyFriendliness based upon who raised it. By default, this is false. --- src/p_lnspec.cpp | 4 ++-- src/p_local.h | 2 +- src/p_things.cpp | 8 +++++++- src/thingdef/thingdef_codeptr.cpp | 28 ++++++++++++++++++++++------ wadsrc/static/actors/actor.txt | 6 +++--- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 0d74b4d42..67215cf41 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1510,7 +1510,7 @@ FUNC(LS_Thing_Raise) if (arg0==0) { - ok = P_Thing_Raise (it); + ok = P_Thing_Raise (it,NULL); } else { @@ -1518,7 +1518,7 @@ FUNC(LS_Thing_Raise) while ( (target = iterator.Next ()) ) { - ok |= P_Thing_Raise(target); + ok |= P_Thing_Raise(target,NULL); } } return ok; diff --git a/src/p_local.h b/src/p_local.h index dec80ecaa..c0a754e25 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -171,7 +171,7 @@ bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); void P_RemoveThing(AActor * actor); -bool P_Thing_Raise(AActor *thing); +bool P_Thing_Raise(AActor *thing, AActor *raiser); bool P_Thing_CanRaise(AActor *thing); const PClass *P_GetSpawnableType(int spawnnum); diff --git a/src/p_things.cpp b/src/p_things.cpp index 0189848e4..63173564c 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -412,7 +412,7 @@ void P_RemoveThing(AActor * actor) } -bool P_Thing_Raise(AActor *thing) +bool P_Thing_Raise(AActor *thing, AActor *raiser) { FState * RaiseState = thing->GetRaiseState(); if (RaiseState == NULL) @@ -445,6 +445,12 @@ bool P_Thing_Raise(AActor *thing) thing->Revive(); + if (raiser != NULL) + { + // Let's copy the friendliness of the one who raised it. + thing->CopyFriendliness(raiser, false); + } + thing->SetState (RaiseState); return true; } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 5629edd9e..a620f0080 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3761,11 +3761,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) // A_RaiseMaster // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + if (self->master != NULL) { - P_Thing_Raise(self->master); + if (copy) + P_Thing_Raise(self->master, self); + else + P_Thing_Raise(self->master, NULL); } } @@ -3774,8 +3780,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) // A_RaiseChildren // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); TThinkerIterator it; AActor *mo; @@ -3783,7 +3791,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) { if (mo->master == self) { - P_Thing_Raise(mo); + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); } } } @@ -3793,8 +3804,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) // A_RaiseSiblings // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); TThinkerIterator it; AActor *mo; @@ -3804,7 +3817,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) { if (mo->master == self->master && mo != self) { - P_Thing_Raise(mo); + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); } } } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index c8e689dae..2f97c2a71 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -241,9 +241,9 @@ ACTOR Actor native //: Thinker 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(); - action native A_RaiseChildren(); - action native A_RaiseSiblings(); + action native A_RaiseMaster(bool copy = 0); + action native A_RaiseChildren(bool copy = 0); + action native A_RaiseSiblings(bool copy = 0); action native A_CheckFloor(state label); action native A_CheckCeiling(state label); action native A_PlayerSkinCheck(state label); From a418f564e98eccfbff70f29156ee61ac7d72304a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 28 Nov 2014 16:34:42 +1300 Subject: [PATCH 268/313] Fixed message duplication in logs - Any printed hud messages would have duplicate entries in logs. --- src/c_console.cpp | 14 -------------- src/p_acs.cpp | 7 ------- 2 files changed, 21 deletions(-) diff --git a/src/c_console.cpp b/src/c_console.cpp index c6fe58749..e3e3d98fe 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1554,13 +1554,6 @@ void C_MidPrint (FFont *font, const char *msg) AddToConsole (-1, bar1); AddToConsole (-1, msg); AddToConsole (-1, bar3); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (msg, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0, (EColorRange)PrintColors[PRINTLEVELS], con_midtime), MAKE_ID('C','N','T','R')); @@ -1578,13 +1571,6 @@ void C_MidPrintBold (FFont *font, const char *msg) AddToConsole (-1, bar2); AddToConsole (-1, msg); AddToConsole (-1, bar3); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (msg, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0, (EColorRange)PrintColors[PRINTLEVELS+1], con_midtime), MAKE_ID('C','N','T','R')); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2ff70c9e3..866087688 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -7725,13 +7725,6 @@ scriptwait: AddToConsole (-1, consolecolor); AddToConsole (-1, work); AddToConsole (-1, bar); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (work, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } } } } From e7da849f77765fc0ddf8db9ba4453b0f6e553f74 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Sat, 29 Nov 2014 17:03:58 +0000 Subject: [PATCH 269/313] - Moved bot specific functions into DBot. --- src/b_bot.cpp | 4 +- src/b_bot.h | 71 ++++++------ src/b_func.cpp | 200 +++++++++++++++++----------------- src/b_move.cpp | 123 +++++++++++---------- src/b_think.cpp | 284 +++++++++++++++++++++++------------------------- src/p_mobj.cpp | 4 +- 6 files changed, 335 insertions(+), 351 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index d2acdcab6..05cac045b 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -66,7 +66,7 @@ void DBot::Serialize (FArchive &arc) arc << savedyaw << savedpitch; } - else if (SaveVersion >= 4516) + else { arc << player; } @@ -105,7 +105,7 @@ void DBot::Tick () BotThinkCycles.Clock(); bglobal.m_Thinking = true; - bglobal.Think (player->mo, &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]); + Think (); bglobal.m_Thinking = false; BotThinkCycles.Unclock(); } diff --git a/src/b_bot.h b/src/b_bot.h index 7740e5e00..69a2f7774 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -89,7 +89,7 @@ public: void ClearPlayer (int playernum, bool keepTeam); - //(B_Game.c) + //(b_game.cpp) void Main (); void Init (); void End(); @@ -99,23 +99,16 @@ public: bool LoadBots (); void ForgetBots (); - //(B_Func.c) - bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); + //(b_func.cpp) void StartTravel (); void FinishTravel (); + bool IsLeader (player_t *player); + void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); + fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd); + bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); - //(B_Think.c) - void Think (AActor *actor, ticcmd_t *cmd); - void WhatToGet (AActor *actor, AActor *item); - - //(B_move.c) - void Roam (AActor *actor, ticcmd_t *cmd); - bool Move (AActor *actor, ticcmd_t *cmd); - bool TryWalk (AActor *actor, ticcmd_t *cmd); - void NewChaseDir (AActor *actor, ticcmd_t *cmd); + //(b_move.cpp) bool CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cmd); - void TurnToAng (AActor *actor); - void Pitch (AActor *actor, AActor *target); bool IsDangerous (sector_t *sec); TArray getspawned; //Array of bots (their names) which should be spawned when starting a game. @@ -132,24 +125,9 @@ public: bool m_Thinking; private: - //(B_Game.c) + //(b_game.cpp) bool DoAddBot (BYTE *info, botskill_t skill); - //(B_Func.c) - bool Reachable (AActor *actor, AActor *target); - void Dofire (AActor *actor, ticcmd_t *cmd); - bool IsLeader (player_t *player); - AActor *Choose_Mate (AActor *bot); - AActor *Find_enemy (AActor *bot); - void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); - fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd); - angle_t FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd); - bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); - - //(B_Think.c) - void ThinkForMove (AActor *actor, ticcmd_t *cmd); - void Set_enemy (AActor *actor); - protected: bool ctf; int loaded_bots; @@ -168,13 +146,17 @@ public: void Serialize (FArchive &arc); void Tick (); + //(b_think.cpp) + void WhatToGet (AActor *item); + + //(b_func.cpp) + bool Check_LOS (AActor *to, angle_t vangle); + player_t *player; angle_t angle; // The wanted angle that the bot try to get every tic. // (used to get a smooth view movement) TObjPtr dest; // Move Destination. TObjPtr prev; // Previous move destination. - - TObjPtr enemy; // The dead meat. TObjPtr missile; // A threatening missile that needs to be avoided. TObjPtr mate; // Friend (used for grouping in teamplay or coop). @@ -204,6 +186,27 @@ public: fixed_t oldx; fixed_t oldy; + +private: + //(B_think.cpp) + void Think (); + void ThinkForMove (ticcmd_t *cmd); + void Set_enemy (); + + //(B_func.cpp) + bool Reachable (AActor *target); + void Dofire (ticcmd_t *cmd); + AActor *Choose_Mate (); + AActor *Find_enemy (); + angle_t FireRox (AActor *enemy, ticcmd_t *cmd); + + //(b_move.cpp) + void Roam (ticcmd_t *cmd); + bool Move (ticcmd_t *cmd); + bool TryWalk (ticcmd_t *cmd); + void NewChaseDir (ticcmd_t *cmd); + void TurnToAng (); + void Pitch (AActor *target); }; @@ -220,7 +223,3 @@ EXTERN_CVAR (Bool, bot_watersplash) EXTERN_CVAR (Bool, bot_chat) #endif // __B_BOT_H__ - - - - diff --git a/src/b_func.cpp b/src/b_func.cpp index 349155a81..7165d2cc1 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -24,24 +24,23 @@ static FRandom pr_botdofire ("BotDoFire"); -//Checks TRUE reachability from -//one looker to another. First mobj (looker) is looker. -bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) +//Checks TRUE reachability from bot to a looker. +bool DBot::Reachable (AActor *rtarget) { - if (looker == rtarget) + if (player->mo == rtarget) return false; if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget->x, rtarget->y) - rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y)) - < looker->height) //Where rtarget is, looker can't be. + < player->mo->height) //Where rtarget is, player->mo can't be. return false; - sector_t *last_s = looker->Sector; - fixed_t last_z = last_s->floorplane.ZatPoint (looker->x, looker->y); - fixed_t estimated_dist = P_AproxDistance (looker->x - rtarget->x, looker->y - rtarget->y); + sector_t *last_s = player->mo->Sector; + fixed_t last_z = last_s->floorplane.ZatPoint (player->mo->x, player->mo->y); + fixed_t estimated_dist = P_AproxDistance (player->mo->x - rtarget->x, player->mo->y - rtarget->y); bool reachable = true; - FPathTraverse it(looker->x+looker->velx, looker->y+looker->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); + FPathTraverse it(player->mo->x+player->mo->velx, player->mo->y+player->mo->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); intercept_t *in; while ((in = it.Next())) { @@ -55,8 +54,8 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) frac = in->frac - FixedDiv (4*FRACUNIT, MAX_TRAVERSE_DIST); dist = FixedMul (frac, MAX_TRAVERSE_DIST); - hitx = it.Trace().x + FixedMul (looker->velx, frac); - hity = it.Trace().y + FixedMul (looker->vely, frac); + hitx = it.Trace().x + FixedMul (player->mo->velx, frac); + hity = it.Trace().y + FixedMul (player->mo->vely, frac); if (in->isaline) { @@ -76,7 +75,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) if (!bglobal.IsDangerous (s) && //Any nukage/lava? (floorheight <= (last_z+MAXMOVEHEIGHT) && ((ceilingheight == floorheight && line->special) - || (ceilingheight - floorheight) >= looker->height))) //Does it fit? + || (ceilingheight - floorheight) >= player->mo->height))) //Does it fit? { last_z = floorheight; last_s = s; @@ -95,7 +94,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) } thing = in->d.thing; - if (thing == looker) //Can't reach self in this case. + if (thing == player->mo) //Can't reach self in this case. continue; if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT))) { @@ -115,16 +114,16 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) //if these conditions are true, the function returns true. //GOOD TO KNOW is that the player's view angle //in doom is 90 degrees infront. -bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle) +bool DBot::Check_LOS (AActor *to, angle_t vangle) { - if (!P_CheckSight (from, to, SF_SEEPASTBLOCKEVERYTHING)) + if (!P_CheckSight (player->mo, to, SF_SEEPASTBLOCKEVERYTHING)) return false; // out of sight if (vangle == ANGLE_MAX) return true; if (vangle == 0) return false; //Looker seems to be blind. - return (angle_t)abs (R_PointToAngle2 (from->x, from->y, to->x, to->y) - from->angle) <= vangle/2; + return (angle_t)abs (R_PointToAngle2 (player->mo->x, player->mo->y, to->x, to->y) - player->mo->angle) <= vangle/2; } //------------------------------------- @@ -132,7 +131,7 @@ bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle) //------------------------------------- //The bot will check if it's time to fire //and do so if that is the case. -void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) +void DBot::Dofire (ticcmd_t *cmd) { bool no_fire; //used to prevent bot from pumping rockets into nearby walls. int aiming_penalty=0; //For shooting at shading target, if screen is red, MAKEME: When screen red. @@ -140,49 +139,48 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) fixed_t dist; angle_t an; int m; - AActor *enemy = actor->player->Bot->enemy; if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) return; - if (actor->player->ReadyWeapon == NULL) + if (player->ReadyWeapon == NULL) return; - if (actor->player->damagecount > actor->player->Bot->skill.isp) + if (player->damagecount > skill.isp) { - actor->player->Bot->first_shot = true; + first_shot = true; return; } //Reaction skill thing. - if (actor->player->Bot->first_shot && - !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) + if (first_shot && + !(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) { - actor->player->Bot->t_react = (100-actor->player->Bot->skill.reaction+1)/((pr_botdofire()%3)+3); + t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3); } - actor->player->Bot->first_shot = false; - if (actor->player->Bot->t_react) + first_shot = false; + if (t_react) return; //MAKEME: Decrease the rocket suicides even more. no_fire = true; - //actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); + //angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); //Distance to enemy. - dist = P_AproxDistance ((actor->x + actor->velx) - (enemy->x + enemy->velx), - (actor->y + actor->vely) - (enemy->y + enemy->vely)); + dist = P_AproxDistance ((player->mo->x + player->mo->velx) - (enemy->x + enemy->velx), + (player->mo->y + player->mo->vely) - (enemy->y + enemy->vely)); //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. - if (actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) + if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) { - if ((actor->player->ReadyWeapon->ProjectileType != NULL)) + if ((player->ReadyWeapon->ProjectileType != NULL)) { - if (actor->player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true)) + if (player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true)) { // This weapon can fire a projectile and has enough ammo to do so goto shootmissile; } - else if (!(actor->player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL)) + else if (!(player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL)) { // Ammo is required, so don't shoot. This is for weapons that shoot // missiles that die at close range, such as the powered-up Phoneix Rod. @@ -195,51 +193,51 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) no_fire = (dist > (MELEERANGE*4)); } } - else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) + else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) { //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=actor->player->Bot->skill.reaction) - if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV)) + if ((pr_botdofire()%200)<=skill.reaction) + if(Check_LOS(enemy, SHOOTFOV)) no_fire = false; } - else if (actor->player->ReadyWeapon->ProjectileType != NULL) + else if (player->ReadyWeapon->ProjectileType != NULL) { - if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { //Special rules for RL - an = FireRox (actor, enemy, cmd); + an = FireRox (enemy, cmd); if(an) { - actor->player->Bot->angle = an; + angle = an; //have to be somewhat precise. to avoid suicide. - if (abs (actor->player->Bot->angle - actor->angle) < 12*ANGLE_1) + if (abs (angle - player->mo->angle) < 12*ANGLE_1) { - actor->player->Bot->t_rocket = 9; + t_rocket = 9; no_fire = false; } } } // prediction aiming shootmissile: - dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); - m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; - SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); - if (Check_LOS (actor, enemy, SHOOTFOV)) + dist = P_AproxDistance (player->mo->x - enemy->x, player->mo->y - enemy->y); + m = dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed; + bglobal.SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); + angle = R_PointToAngle2 (player->mo->x, player->mo->y, bglobal.body1->x, bglobal.body1->y); + if (Check_LOS (enemy, SHOOTFOV)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); + angle = R_PointToAngle2 (player->mo->x, player->mo->y, enemy->x, enemy->y); aiming_penalty = 0; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; - if (enemy->Sector->lightlevelplayer->powers & PW_INFRARED)*/) + if (enemy->Sector->lightlevelpowers & PW_INFRARED)*/) aiming_penalty += pr_botdofire()%40;//Dark - if (actor->player->damagecount) - aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim - aiming_value = actor->player->Bot->skill.aiming - aiming_penalty; + if (player->damagecount) + aiming_penalty += player->damagecount; //Blood in face makes it hard to aim + aiming_value = skill.aiming - aiming_penalty; if (aiming_value <= 0) aiming_value = 1; m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate @@ -248,18 +246,18 @@ shootmissile: if (m) { - if (actor->player->Bot->increase) - actor->player->Bot->angle += m; + if (increase) + angle += m; else - actor->player->Bot->angle -= m; + angle -= m; } - if (abs (actor->player->Bot->angle - actor->angle) < 4*ANGLE_1) + if (abs (angle - player->mo->angle) < 4*ANGLE_1) { - actor->player->Bot->increase = !actor->player->Bot->increase; + increase = !increase; } - if (Check_LOS (actor, enemy, (SHOOTFOV/2))) + if (Check_LOS (enemy, (SHOOTFOV/2))) no_fire = false; } if (!no_fire) //If going to fire weapon @@ -267,7 +265,7 @@ shootmissile: cmd->ucmd.buttons |= BT_ATTACK; } //Prevents bot from jerking, when firing automatic things with low skill. - //actor->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); + //player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); } bool FCajunMaster::IsLeader (player_t *player) @@ -287,7 +285,7 @@ bool FCajunMaster::IsLeader (player_t *player) //This function is called every //tick (for each bot) to set //the mate (teammate coop mate). -AActor *FCajunMaster::Choose_Mate (AActor *bot) +AActor *DBot::Choose_Mate () { int count; fixed_t closest_dist, test; @@ -295,20 +293,20 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) AActor *observer; //is mate alive? - if (bot->player->Bot->mate) + if (mate) { - if (bot->player->Bot->mate->health <= 0) - bot->player->Bot->mate = NULL; + if (mate->health <= 0) + mate = NULL; else - bot->player->Bot->last_mate = bot->player->Bot->mate; + last_mate = mate; } - if (bot->player->Bot->mate) //Still is.. - return bot->player->Bot->mate; + if (mate) //Still is.. + return mate; //Check old_mates status. - if (bot->player->Bot->last_mate) - if (bot->player->Bot->last_mate->health <= 0) - bot->player->Bot->last_mate = NULL; + if (last_mate) + if (last_mate->health <= 0) + last_mate = NULL; target = NULL; closest_dist = FIXED_MAX; @@ -324,17 +322,17 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) if (playeringame[count] && client->mo - && bot != client->mo - && (bot->IsTeammate (client->mo) || !deathmatch) + && player->mo != client->mo + && (player->mo->IsTeammate (client->mo) || !deathmatch) && client->mo->health > 0 && client->mo != observer - && ((bot->health/2) <= client->mo->health || !deathmatch) - && !IsLeader(client)) //taken? + && ((player->mo->health/2) <= client->mo->health || !deathmatch) + && !bglobal.IsLeader(client)) //taken? { - if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) + if (P_CheckSight (player->mo, client->mo, SF_IGNOREVISIBILITY)) { - test = P_AproxDistance (client->mo->x - bot->x, - client->mo->y - bot->y); + test = P_AproxDistance (client->mo->x - player->mo->x, + client->mo->y - player->mo->y); if (test < closest_dist) { @@ -347,15 +345,15 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) /* //Make a introducing to mate. - if(target && target!=bot->player->last_mate) + if(target && target!=last_mate) { if((P_Random()%(200*bglobal.botnum))<3) { - bot->player->chat = c_teamup; + chat = c_teamup; if(target->bot) - strcpy(bot->player->c_target, botsingame[target->bot_id]); + strcpy(c_target, botsingame[target->bot_id]); else if(target->player) - strcpy(bot->player->c_target, player_names[target->play_id]); + strcpy(c_target, player_names[target->play_id]); } } */ @@ -365,7 +363,7 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) } //MAKEME: Make this a smart decision -AActor *FCajunMaster::Find_enemy (AActor *bot) +AActor *DBot::Find_enemy () { int count; fixed_t closest_dist, temp; //To target. @@ -375,15 +373,15 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) if (!deathmatch) { // [RH] Take advantage of the Heretic/Hexen code to be a little smarter - return P_RoughMonsterSearch (bot, 20); + return P_RoughMonsterSearch (player->mo, 20); } //Note: It's hard to ambush a bot who is not alone - if (bot->player->Bot->allround || bot->player->Bot->mate) + if (allround || mate) vangle = ANGLE_MAX; else vangle = ENEMY_SCAN_FOV; - bot->player->Bot->allround = false; + allround = false; target = NULL; closest_dist = FIXED_MAX; @@ -396,21 +394,21 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) { player_t *client = &players[count]; if (playeringame[count] - && !bot->IsTeammate (client->mo) + && !player->mo->IsTeammate (client->mo) && client->mo != observer && client->mo->health > 0 - && bot != client->mo) + && player->mo != client->mo) { - if (Check_LOS (bot, client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. - //if(P_CheckSight(bot, players[count].mo)) + if (Check_LOS (client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. + //if(P_CheckSight(player->mo, players[count].mo)) { - temp = P_AproxDistance (client->mo->x - bot->x, - client->mo->y - bot->y); + temp = P_AproxDistance (client->mo->x - player->mo->x, + client->mo->y - player->mo->y); //Too dark? if (temp > DARK_DIST && client->mo->Sector->lightlevel < WHATS_DARK /*&& - bot->player->Powers & PW_INFRARED*/) + player->Powers & PW_INFRARED*/) continue; if (temp < closest_dist) @@ -494,16 +492,16 @@ fixed_t FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd) return dist; } -angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) +angle_t DBot::FireRox (AActor *enemy, ticcmd_t *cmd) { fixed_t dist; angle_t ang; AActor *actor; int m; - SetBodyAt (bot->x + FixedMul(bot->velx, 5*FRACUNIT), - bot->y + FixedMul(bot->vely, 5*FRACUNIT), - bot->z + (bot->height / 2), 2); + bglobal.SetBodyAt (player->mo->x + FixedMul(player->mo->velx, 5*FRACUNIT), + player->mo->y + FixedMul(player->mo->vely, 5*FRACUNIT), + player->mo->z + (player->mo->height / 2), 2); actor = bglobal.body2; @@ -513,16 +511,16 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) //Predict. m = (((dist+1)/FRACUNIT) / GetDefaultByName("Rocket")->Speed); - SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)), - enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); + bglobal.SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)), + enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); dist = P_AproxDistance(actor->x-bglobal.body1->x, actor->y-bglobal.body1->y); //try the predicted location if (P_CheckSight (actor, bglobal.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile { FCheckPosition tm; - if (SafeCheckPosition (bot, actor->x, actor->y, tm)) + if (bglobal.SafeCheckPosition (player->mo, actor->x, actor->y, tm)) { - if (FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST) + if (bglobal.FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST) { ang = R_PointToAngle2 (actor->x, actor->y, bglobal.body1->x, bglobal.body1->y); return ang; @@ -532,9 +530,9 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) //Try fire straight. if (P_CheckSight (actor, enemy, 0)) { - if (FakeFire (bot, enemy, cmd) >= SAFE_SELF_MISDIST) + if (bglobal.FakeFire (player->mo, enemy, cmd) >= SAFE_SELF_MISDIST) { - ang = R_PointToAngle2(bot->x, bot->y, enemy->x, enemy->y); + ang = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); return ang; } } diff --git a/src/b_move.cpp b/src/b_move.cpp index f020cf4fc..fd0405457 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -27,57 +27,57 @@ static FRandom pr_botnewchasedir ("BotNewChaseDir"); extern dirtype_t opposite[9]; extern dirtype_t diags[4]; -//Called while the bot moves after its player->dest mobj +//Called while the bot moves after its dest mobj //which can be a weapon/enemy/item whatever. -void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd) +void DBot::Roam (ticcmd_t *cmd) { int delta; - if (Reachable(actor, actor->player->Bot->dest)) + if (Reachable(dest)) { // Straight towards it. - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot->dest->x, actor->player->Bot->dest->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y); } - else if (actor->movedir < 8) // turn towards movement direction if not there yet + else if (player->mo->movedir < 8) // turn towards movement direction if not there yet { - actor->player->Bot->angle &= (angle_t)(7<<29); - delta = actor->player->Bot->angle - (actor->movedir << 29); + angle &= (angle_t)(7<<29); + delta = angle - (player->mo->movedir << 29); if (delta > 0) - actor->player->Bot->angle -= ANG45; + angle -= ANG45; else if (delta < 0) - actor->player->Bot->angle += ANG45; + angle += ANG45; } // chase towards destination. - if (--actor->movecount < 0 || !Move (actor, cmd)) + if (--player->mo->movecount < 0 || !Move (cmd)) { - NewChaseDir (actor, cmd); + NewChaseDir (cmd); } } -bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) +bool DBot::Move (ticcmd_t *cmd) { fixed_t tryx, tryy; bool try_ok; int good; - if (actor->movedir == DI_NODIR) + if (player->mo->movedir == DI_NODIR) return false; - if ((unsigned)actor->movedir >= 8) + if ((unsigned)player->mo->movedir >= 8) I_Error ("Weird bot movedir!"); - tryx = actor->x + 8*xspeed[actor->movedir]; - tryy = actor->y + 8*yspeed[actor->movedir]; + tryx = player->mo->x + 8*xspeed[player->mo->movedir]; + tryy = player->mo->y + 8*yspeed[player->mo->movedir]; - try_ok = CleanAhead (actor, tryx, tryy, cmd); + try_ok = bglobal.CleanAhead (player->mo, tryx, tryy, cmd); if (!try_ok) //Anything blocking that could be opened etc.. { if (!spechit.Size ()) return false; - actor->movedir = DI_NODIR; + player->mo->movedir = DI_NODIR; good = 0; line_t *ld; @@ -86,16 +86,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) { bool tryit = true; - if (ld->special == Door_LockedRaise && !P_CheckKeys (actor, ld->args[3], false)) + if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false)) tryit = false; - else if (ld->special == Generic_Door && !P_CheckKeys (actor, ld->args[4], false)) + else if (ld->special == Generic_Door && !P_CheckKeys (player->mo, ld->args[4], false)) tryit = false; if (tryit && - (P_TestActivateLine (ld, actor, 0, SPAC_Use) || - P_TestActivateLine (ld, actor, 0, SPAC_Push))) + (P_TestActivateLine (ld, player->mo, 0, SPAC_Use) || + P_TestActivateLine (ld, player->mo, 0, SPAC_Push))) { - good |= ld == actor->BlockingLine ? 1 : 2; + good |= ld == player->mo->BlockingLine ? 1 : 2; } } if (good && ((pr_botopendoor() >= 203) ^ (good & 1))) @@ -113,16 +113,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) return true; } -bool FCajunMaster::TryWalk (AActor *actor, ticcmd_t *cmd) +bool DBot::TryWalk (ticcmd_t *cmd) { - if (!Move (actor, cmd)) + if (!Move (cmd)) return false; - actor->movecount = pr_bottrywalk() & 60; + player->mo->movecount = pr_bottrywalk() & 60; return true; } -void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) +void DBot::NewChaseDir (ticcmd_t *cmd) { fixed_t deltax; fixed_t deltay; @@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) dirtype_t turnaround; - if (!actor->player->Bot->dest) + if (!dest) { #ifndef BOT_RELEASE_COMPILE Printf ("Bot tried move without destination\n"); @@ -142,11 +142,11 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) return; } - olddir = (dirtype_t)actor->movedir; + olddir = (dirtype_t)player->mo->movedir; turnaround = opposite[olddir]; - deltax = actor->player->Bot->dest->x - actor->x; - deltay = actor->player->Bot->dest->y - actor->y; + deltax = dest->x - player->mo->x; + deltay = dest->y - player->mo->y; if (deltax > 10*FRACUNIT) d[1] = DI_EAST; @@ -165,8 +165,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { - actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; - if (actor->movedir != turnaround && TryWalk(actor, cmd)) + player->mo->movedir = diags[((deltay<0)<<1)+(deltax>0)]; + if (player->mo->movedir != turnaround && TryWalk(cmd)) return; } @@ -186,16 +186,16 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) if (d[1]!=DI_NODIR) { - actor->movedir = d[1]; - if (TryWalk (actor, cmd)) + player->mo->movedir = d[1]; + if (TryWalk (cmd)) return; } if (d[2]!=DI_NODIR) { - actor->movedir = d[2]; + player->mo->movedir = d[2]; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } @@ -203,9 +203,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) // so pick another direction. if (olddir!=DI_NODIR) { - actor->movedir = olddir; + player->mo->movedir = olddir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } @@ -218,9 +218,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) { if (tdir!=turnaround) { - actor->movedir = tdir; + player->mo->movedir = tdir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } } @@ -233,9 +233,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) { if (tdir!=turnaround) { - actor->movedir = tdir; + player->mo->movedir = tdir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } } @@ -243,12 +243,12 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) if (turnaround != DI_NODIR) { - actor->movedir = turnaround; - if (TryWalk(actor, cmd)) + player->mo->movedir = turnaround; + if (TryWalk(cmd)) return; } - actor->movedir = DI_NODIR; // can not move + player->mo->movedir = DI_NODIR; // can not move } @@ -307,48 +307,48 @@ bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cm #define MAXTURN (15*ANGLE_1) //Max degrees turned in one tic. Lower is smother but may cause the bot not getting where it should = crash #define TURNSENS 3 //Higher is smoother but slower turn. -void FCajunMaster::TurnToAng (AActor *actor) +void DBot::TurnToAng () { int maxturn = MAXTURN; - if (actor->player->ReadyWeapon != NULL) + if (player->ReadyWeapon != NULL) { - if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { - if (actor->player->Bot->t_roam && !actor->player->Bot->missile) + if (t_roam && !missile) { //Keep angle that where when shot where decided. return; } } - if(actor->player->Bot->enemy) - if(!actor->player->Bot->dest) //happens when running after item in combat situations, or normal, prevents weak turns - if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) - if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV+5*ANGLE_1)) + if(enemy) + if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns + if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) + if(Check_LOS(enemy, SHOOTFOV+5*ANGLE_1)) maxturn = 3; } - int distance = actor->player->Bot->angle - actor->angle; + int distance = angle - player->mo->angle; - if (abs (distance) < OKAYRANGE && !actor->player->Bot->enemy) + if (abs (distance) < OKAYRANGE && !enemy) return; distance /= TURNSENS; if (abs (distance) > maxturn) distance = distance < 0 ? -maxturn : maxturn; - actor->angle += distance; + player->mo->angle += distance; } -void FCajunMaster::Pitch (AActor *actor, AActor *target) +void DBot::Pitch (AActor *target) { double aim; double diff; - diff = target->z - actor->z; - aim = atan (diff / (double)P_AproxDistance (actor->x - target->x, actor->y - target->y)); - actor->pitch = -(int)(aim * ANGLE_180/M_PI); + diff = target->z - player->mo->z; + aim = atan (diff / (double)P_AproxDistance (player->mo->x - target->x, player->mo->y - target->y)); + player->mo->pitch = -(int)(aim * ANGLE_180/M_PI); } //Checks if a sector is dangerous. @@ -371,4 +371,3 @@ bool FCajunMaster::IsDangerous (sector_t *sec) || special == Damage_InstantDeath || special == sDamage_SuperHellslime; } - diff --git a/src/b_think.cpp b/src/b_think.cpp index e5ee8775c..34baeee9c 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -24,47 +24,49 @@ static FRandom pr_botmove ("BotMove"); //This function is called each tic for each bot, //so this is what the bot does. -void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) +void DBot::Think () { + ticcmd_t *cmd = &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]; + memset (cmd, 0, sizeof(*cmd)); - if (actor->player->Bot->enemy && actor->player->Bot->enemy->health <= 0) - actor->player->Bot->enemy = NULL; + if (enemy && enemy->health <= 0) + enemy = NULL; - if (actor->health > 0) //Still alive + if (player->mo->health > 0) //Still alive { if (teamplay || !deathmatch) - actor->player->Bot->mate = Choose_Mate (actor); + mate = Choose_Mate (); - angle_t oldyaw = actor->angle; - int oldpitch = actor->pitch; + angle_t oldyaw = player->mo->angle; + int oldpitch = player->mo->pitch; - Set_enemy (actor); - ThinkForMove (actor, cmd); - TurnToAng (actor); + Set_enemy (); + ThinkForMove (cmd); + TurnToAng (); - cmd->ucmd.yaw = (short)((actor->angle - oldyaw) >> 16) / ticdup; - cmd->ucmd.pitch = (short)((oldpitch - actor->pitch) >> 16); + cmd->ucmd.yaw = (short)((player->mo->angle - oldyaw) >> 16) / ticdup; + cmd->ucmd.pitch = (short)((oldpitch - player->mo->pitch) >> 16); if (cmd->ucmd.pitch == -32768) cmd->ucmd.pitch = -32767; cmd->ucmd.pitch /= ticdup; - actor->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup; - actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; + player->mo->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup; + player->mo->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; } - if (actor->player->Bot->t_active) actor->player->Bot->t_active--; - if (actor->player->Bot->t_strafe) actor->player->Bot->t_strafe--; - if (actor->player->Bot->t_react) actor->player->Bot->t_react--; - if (actor->player->Bot->t_fight) actor->player->Bot->t_fight--; - if (actor->player->Bot->t_rocket) actor->player->Bot->t_rocket--; - if (actor->player->Bot->t_roam) actor->player->Bot->t_roam--; + if (t_active) t_active--; + if (t_strafe) t_strafe--; + if (t_react) t_react--; + if (t_fight) t_fight--; + if (t_rocket) t_rocket--; + if (t_roam) t_roam--; //Respawn ticker - if (actor->player->Bot->t_respawn) + if (t_respawn) { - actor->player->Bot->t_respawn--; + t_respawn--; } - else if (actor->health <= 0) + else if (player->mo->health <= 0) { // Time to respawn cmd->ucmd.buttons |= BT_USE; } @@ -72,62 +74,57 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) //how the bot moves. //MAIN movement function. -void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) +void DBot::ThinkForMove (ticcmd_t *cmd) { - player_t *b; fixed_t dist; bool stuck; int r; - b = actor->player; - if (b->Bot == NULL) - return; - stuck = false; - dist = b->Bot->dest ? P_AproxDistance(actor->x-b->Bot->dest->x, actor->y-b->Bot->dest->y) : 0; + dist = dest ? P_AproxDistance(player->mo->x-dest->x, player->mo->y-dest->y) : 0; - if (b->Bot->missile && - ((!b->Bot->missile->velx || !b->Bot->missile->vely) || !Check_LOS(actor, b->Bot->missile, SHOOTFOV*3/2))) + if (missile && + ((!missile->velx || !missile->vely) || !Check_LOS(missile, SHOOTFOV*3/2))) { - b->Bot->sleft = !b->Bot->sleft; - b->Bot->missile = NULL; //Probably ended its travel. + sleft = !sleft; + missile = NULL; //Probably ended its travel. } - if (actor->pitch > 0) - actor->pitch -= 80; - else if (actor->pitch <= -60) - actor->pitch += 80; + if (player->mo->pitch > 0) + player->mo->pitch -= 80; + else if (player->mo->pitch <= -60) + player->mo->pitch += 80; //HOW TO MOVE: - if (b->Bot->missile && (P_AproxDistance(actor->x-b->Bot->missile->x, actor->y-b->Bot->missile->y)mo->x-missile->x, player->mo->y-missile->y)Bot->missile); - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->missile->x, b->Bot->missile->y); - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; + Pitch (missile); + angle = R_PointToAngle2(player->mo->x, player->mo->y, missile->x, missile->y); + cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000) - && b->Bot->t_strafe<=0) + if ((P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000) + && t_strafe<=0) { - b->Bot->t_strafe = 5; - b->Bot->sleft = !b->Bot->sleft; + t_strafe = 5; + sleft = !sleft; } //If able to see enemy while avoiding missile, still fire at enemy. - if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV)) - Dofire (actor, cmd); //Order bot to fire current weapon + if (enemy && Check_LOS (enemy, SHOOTFOV)) + Dofire (cmd); //Order bot to fire current weapon } - else if (b->Bot->enemy && P_CheckSight (actor, b->Bot->enemy, 0)) //Fight! + else if (enemy && P_CheckSight (player->mo, enemy, 0)) //Fight! { - Pitch (actor, b->Bot->enemy); + Pitch (enemy); //Check if it's more important to get an item than fight. - if (b->Bot->dest && (b->Bot->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. + if (dest && (dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. { -#define is(x) b->Bot->dest->IsKindOf (PClass::FindClass (#x)) +#define is(x) dest->IsKindOf (PClass::FindClass (#x)) if ( ( - (actor->health < b->Bot->skill.isp && + (player->mo->health < skill.isp && (is (Medikit) || is (Stimpack) || is (Soulsphere) || @@ -140,78 +137,78 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) is (Megasphere) ) || dist < (GETINCOMBAT/4) || - (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) + (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) ) - && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - && Reachable (actor, b->Bot->dest)) + && (dist < GETINCOMBAT || (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + && Reachable (dest)) #undef is { goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. } } - b->Bot->dest = NULL; //To let bot turn right + dest = NULL; //To let bot turn right - if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. + if (player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - if (!(b->Bot->enemy->flags3 & MF3_ISMONSTER)) - b->Bot->t_fight = AFTERTICS; + if (!(enemy->flags3 & MF3_ISMONSTER)) + t_fight = AFTERTICS; - if (b->Bot->t_strafe <= 0 && - (P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000 + if (t_strafe <= 0 && + (P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000 || ((pr_botmove()%30)==10)) ) { stuck = true; - b->Bot->t_strafe = 5; - b->Bot->sleft = !b->Bot->sleft; + t_strafe = 5; + sleft = !sleft; } - b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); - if (b->ReadyWeapon == NULL || - P_AproxDistance(actor->x-b->Bot->enemy->x, actor->y-b->Bot->enemy->y) > - b->ReadyWeapon->MoveCombatDist) + if (player->ReadyWeapon == NULL || + P_AproxDistance(player->mo->x-enemy->x, player->mo->y-enemy->y) > + player->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; + cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; } else if (!stuck) //Too close, so move away. { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; + cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; } //Strafing. - if (b->Bot->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. + if (enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. { - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDEWALK : SIDEWALK; + cmd->ucmd.sidemove = sleft ? -SIDEWALK : SIDEWALK; } else { - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; + cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; } - Dofire (actor, cmd); //Order bot to fire current weapon + Dofire (cmd); //Order bot to fire current weapon } - else if (b->Bot->mate && !b->Bot->enemy && (!b->Bot->dest || b->Bot->dest==b->Bot->mate)) //Follow mate move. + else if (mate && !enemy && (!dest || dest==mate)) //Follow mate move. { fixed_t matedist; - Pitch (actor, b->Bot->mate); + Pitch (mate); - if (!Reachable (actor, b->Bot->mate)) + if (!Reachable (mate)) { - if (b->Bot->mate == b->Bot->dest && pr_botmove.Random() < 32) + if (mate == dest && pr_botmove.Random() < 32) { // [RH] If the mate is the dest, pick a new dest sometimes - b->Bot->dest = NULL; + dest = NULL; } goto roam; } - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->mate->x, b->Bot->mate->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, mate->x, mate->y); - matedist = P_AproxDistance(actor->x - b->Bot->mate->x, actor->y - b->Bot->mate->y); + matedist = P_AproxDistance(player->mo->x - mate->x, player->mo->y - mate->y); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -221,42 +218,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) } else //Roam after something. { - b->Bot->first_shot = true; + first_shot = true; ///// roam: ///// - if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. - Dofire (actor, cmd); //Order bot to fire current weapon + if (enemy && Check_LOS (enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. + Dofire (cmd); //Order bot to fire current weapon - if (b->Bot->dest && !(b->Bot->dest->flags&MF_SPECIAL) && b->Bot->dest->health < 0) + if (dest && !(dest->flags&MF_SPECIAL) && dest->health < 0) { //Roaming after something dead. - b->Bot->dest = NULL; + dest = NULL; } - if (b->Bot->dest == NULL) + if (dest == NULL) { - if (b->Bot->t_fight && b->Bot->enemy) //Enemy/bot has jumped around corner. So what to do? + if (t_fight && enemy) //Enemy/bot has jumped around corner. So what to do? { - if (b->Bot->enemy->player) + if (enemy->player) { - if (((b->Bot->enemy->player->ReadyWeapon != NULL && b->Bot->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>b->Bot->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - b->Bot->dest = b->Bot->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. - else //hide while b->t_fight, but keep view at enemy. - b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); + if (((enemy->player->ReadyWeapon != NULL && enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || + (pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. + else //hide while t_fight, but keep view at enemy. + angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); } //Just a monster, so kill it. else - b->Bot->dest = b->Bot->enemy; + dest = enemy; - //VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. + //VerifFavoritWeapon(player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. } else //Choose a distant target. to get things going. { r = pr_botmove(); if (r < 128) { - TThinkerIterator it (STAT_INVENTORY, firstthing); + TThinkerIterator it (STAT_INVENTORY, bglobal.firstthing); AInventory *item = it.Next(); if (item != NULL || (item = it.Next()) != NULL) @@ -271,60 +268,53 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) { item = it.Next(); } - firstthing = item; - b->Bot->dest = item; + bglobal.firstthing = item; + dest = item; } } - else if (b->Bot->mate && (r < 179 || P_CheckSight(actor, b->Bot->mate))) + else if (mate && (r < 179 || P_CheckSight(player->mo, mate))) { - b->Bot->dest = b->Bot->mate; + dest = mate; } else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) { - b->Bot->dest = players[(r&(MAXPLAYERS-1))].mo; + dest = players[(r&(MAXPLAYERS-1))].mo; } } - if (b->Bot->dest) + if (dest) { - b->Bot->t_roam = MAXROAM; + t_roam = MAXROAM; } } - if (b->Bot->dest) + if (dest) { //Bot has a target so roam after it. - Roam (actor, cmd); + Roam (cmd); } } //End of movement main part. - if (!b->Bot->t_roam && b->Bot->dest) + if (!t_roam && dest) { - b->Bot->prev = b->Bot->dest; - b->Bot->dest = NULL; + prev = dest; + dest = NULL; } - if (b->Bot->t_fight<(AFTERTICS/2)) - actor->flags |= MF_DROPOFF; + if (t_fight<(AFTERTICS/2)) + player->mo->flags |= MF_DROPOFF; - b->Bot->oldx = actor->x; - b->Bot->oldy = actor->y; + oldx = player->mo->x; + oldy = player->mo->y; } //BOT_WhatToGet // //Determines if the bot will roam after an item or not. -void FCajunMaster::WhatToGet (AActor *actor, AActor *item) +void DBot::WhatToGet (AActor *item) { - player_t *b = actor->player; - - if (b == NULL) - { - return; - } - #define typeis(x) item->IsKindOf (PClass::FindClass (#x)) if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == b->Bot->prev) + || item == prev) { return; } @@ -338,7 +328,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) // FIXME AWeapon *heldWeapon; - heldWeapon = static_cast (b->mo->FindInventory (item->GetClass())); + heldWeapon = static_cast (player->mo->FindInventory (item->GetClass())); if (heldWeapon != NULL) { if (!weapgiveammo) @@ -354,39 +344,38 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) { AAmmo *ammo = static_cast (item); const PClass *parent = ammo->GetParentAmmo (); - AInventory *holdingammo = b->mo->FindInventory (parent); + AInventory *holdingammo = player->mo->FindInventory (parent); if (holdingammo != NULL && holdingammo->Amount >= holdingammo->MaxAmount) { return; } } - else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && actor->health >= deh.MaxSoulsphere) + else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) return; - else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) + else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= deh.MaxHealth /*MAXHEALTH*/) return; - if ((b->Bot->dest == NULL || - !(b->Bot->dest->flags & MF_SPECIAL)/* || - !Reachable (actor, b->dest)*/)/* && - Reachable (actor, item)*/) // Calling Reachable slows this down tremendously + if ((dest == NULL || + !(dest->flags & MF_SPECIAL)/* || + !Reachable (dest)*/)/* && + Reachable (item)*/) // Calling Reachable slows this down tremendously { - b->Bot->prev = b->Bot->dest; - b->Bot->dest = item; - b->Bot->t_roam = MAXROAM; + prev = dest; + dest = item; + t_roam = MAXROAM; } } -void FCajunMaster::Set_enemy (AActor *actor) +void DBot::Set_enemy () { AActor *oldenemy; - AActor **enemy = &actor->player->Bot->enemy; - if (*enemy - && (*enemy)->health > 0 - && P_CheckSight (actor, *enemy)) + if (enemy + && enemy->health > 0 + && P_CheckSight (player->mo, enemy)) { - oldenemy = *enemy; + oldenemy = enemy; } else { @@ -395,15 +384,14 @@ void FCajunMaster::Set_enemy (AActor *actor) // [RH] Don't even bother looking for a different enemy if this is not deathmatch // and we already have an existing enemy. - if (deathmatch || !*enemy) + if (deathmatch || !enemy) { - actor->player->Bot->allround = !!*enemy; - *enemy = NULL; - *enemy = Find_enemy(actor); - if (!*enemy) - *enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone) + allround = !!enemy; + enemy = Find_enemy(); + if (!enemy) + enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone) } //Verify that that enemy is really something alive that bot can kill. - if (*enemy && (((*enemy)->health < 0 || !((*enemy)->flags&MF_SHOOTABLE)) || actor->IsFriend(*enemy))) - *enemy = NULL; + if (enemy && ((enemy->health < 0 || !(enemy->flags&MF_SHOOTABLE)) || player->mo->IsFriend(enemy))) + enemy = NULL; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index cf6bccc8a..557f5fa94 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3259,7 +3259,7 @@ void AActor::Tick () else if (flags & MF_SPECIAL) { //Item pickup time //clock (BotWTG); - bglobal.WhatToGet (players[i].mo, this); + players[i].Bot->WhatToGet (this); //unclock (BotWTG); BotWTG++; } @@ -3267,7 +3267,7 @@ void AActor::Tick () { if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. - if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) + if (target != players[i].mo && players[i].Bot->Check_LOS (this, ANGLE_90)) players[i].Bot->missile = this; } } From 8b88f14c96dc7a70c601fbd08c9b9cf8de546496 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 1 Dec 2014 21:04:50 -0600 Subject: [PATCH 270/313] Destroy mod-created CVars when 'restart' is invoked --- src/c_cvars.cpp | 16 ++++++++++++++++ src/c_cvars.h | 4 ++++ src/d_main.cpp | 1 + 3 files changed, 21 insertions(+) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index ce4e88632..c770bcbcf 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -1515,6 +1515,22 @@ void UnlatchCVars (void) } } +void DestroyCVarsFlagged (DWORD flags) +{ + FBaseCVar *cvar = CVars; + FBaseCVar *next = cvar; + + while(cvar) + { + next = cvar->m_Next; + + if(cvar->Flags & flags) + delete cvar; + + cvar = next; + } +} + void C_SetCVarsToDefaults (void) { FBaseCVar *cvar = CVars; diff --git a/src/c_cvars.h b/src/c_cvars.h index 17d3f7b1d..ed2b16755 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -159,6 +159,7 @@ private: friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); friend FBaseCVar *FindCVarSub (const char *var_name, int namelen); friend void UnlatchCVars (void); + friend void DestroyCVarsFlagged (DWORD flags); friend void C_ArchiveCVars (FConfigFile *f, uint32 filter); friend void C_SetCVarsToDefaults (void); friend void FilterCompactCVars (TArray &cvars, uint32 filter); @@ -190,6 +191,9 @@ FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags); // Called from G_InitNew() void UnlatchCVars (void); +// Destroy CVars with the matching flags; called from CCMD(restart) +void DestroyCVarsFlagged (DWORD flags); + // archive cvars to FILE f void C_ArchiveCVars (FConfigFile *f, uint32 filter); diff --git a/src/d_main.cpp b/src/d_main.cpp index 4fea35301..2ceec3926 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2578,6 +2578,7 @@ void D_DoomMain (void) new (&gameinfo) gameinfo_t; // Reset gameinfo S_Shutdown(); // free all channels and delete playlist C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here + DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods GC::FullGC(); // perform one final garbage collection before deleting the class data PClass::ClearRuntimeData(); // clear all runtime generated class data From 3af08f19839db4d285d18d746f4f17ceb6515b2d Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 1 Dec 2014 22:03:22 -0600 Subject: [PATCH 271/313] Fixed weapon drawing rules to account for the death camera [backported from gzdoom], also fixed drawing rules for crosshair --- src/g_shared/shared_sbar.cpp | 2 +- src/r_things.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 2a18801a1..d8e113824 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1123,7 +1123,7 @@ void DBaseStatusBar::DrawCrosshair () ST_LoadCrosshair(); // Don't draw the crosshair if there is none - if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL) + if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || camera->health <= 0) { return; } diff --git a/src/r_things.cpp b/src/r_things.cpp index c0475ee75..e27a5c87e 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -95,6 +95,7 @@ extern fixed_t globaluclip, globaldclip; EXTERN_CVAR (Bool, st_scale) EXTERN_CVAR(Bool, r_shadercolormaps) EXTERN_CVAR(Int, r_drawfuzz) +EXTERN_CVAR(Bool, r_deathcamera); // // Sprite rotation 0 is facing the viewer, @@ -1410,7 +1411,8 @@ void R_DrawPlayerSprites () if (!r_drawplayersprites || !camera->player || - (players[consoleplayer].cheats & CF_CHASECAM)) + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) return; if(fixedlightlev < 0 && viewsector->e && viewsector->e->XFloor.lightlist.Size()) { From c631ffc5fd1abd604432ce050ec3f31d192febd1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 3 Dec 2014 00:50:05 -0600 Subject: [PATCH 272/313] Fixed SV_SAMESPAWNSPOT behavior -- When enabled, use the p->mo->z. Otherwise, rely on ONCEILINGZ/FLOATRANDZ/ONFLOORZ w/ or w/o UsePlayerStartZ as previously done --- src/p_mobj.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 557f5fa94..749747586 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4302,12 +4302,15 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { spawn_x = p->mo->x; spawn_y = p->mo->y; + spawn_z = p->mo->z; + spawn_angle = p->mo->angle; } else { spawn_x = mthing->x; spawn_y = mthing->y; + // Allow full angular precision but avoid roundoff errors for multiples of 45 degrees. if (mthing->angle % 45 != 0) { @@ -4321,14 +4324,14 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { spawn_angle += 1 << ANGLETOFINESHIFT; } - } - if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING) - spawn_z = ONCEILINGZ; - else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT) - spawn_z = FLOATRANDZ; - else - spawn_z = ONFLOORZ; + if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING) + spawn_z = ONCEILINGZ; + else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT) + spawn_z = FLOATRANDZ; + else + spawn_z = ONFLOORZ; + } mobj = static_cast (Spawn (p->cls, spawn_x, spawn_y, spawn_z, NO_REPLACE)); From fbe14d59bba9f82a3f8968861a813553ecce3752 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Dec 2014 13:05:50 +0100 Subject: [PATCH 273/313] - fixed: All powerup blend colors with an alpha of 0 were treated as 'has no color' for PowerupGivers. --- src/g_shared/a_artifacts.cpp | 3 ++- src/thingdef/thingdef_properties.cpp | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 4a2d066cd..410474f31 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -60,7 +60,8 @@ bool APowerupGiver::Use (bool pickup) } if (BlendColor != 0) { - power->BlendColor = BlendColor; + if (BlendColor.a != 0) power->BlendColor = BlendColor; + else power->BlendColor = 0; } if (Mode != NAME_None) { diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index b5fdc8e42..ec68dfb95 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -2020,15 +2020,18 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) "INVERSEMAP", "GOLDMAP", "REDMAP", "GREENMAP", "BLUEMAP", NULL }; int alpha; + bool giver; PalEntry * pBlendColor; if (info->Class->IsDescendantOf(RUNTIME_CLASS(APowerup))) { pBlendColor = &((APowerup*)defaults)->BlendColor; + giver = false; } else if (info->Class->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) { pBlendColor = &((APowerupGiver*)defaults)->BlendColor; + giver = true; } else { @@ -2061,7 +2064,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) else alpha = 255/3; alpha=clamp(alpha, 0, 255); - if (alpha != 0) *pBlendColor = MAKEARGB(alpha, 0, 0, 0) | color; + if (alpha != 0 || giver) *pBlendColor = MAKEARGB(alpha, 0, 0, 0) | color; else *pBlendColor = 0; } From fcc491f7351cb1732c5defd6641657b7d755bb91 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 3 Dec 2014 17:04:47 -0500 Subject: [PATCH 274/313] Make the respawn invulnerability APowerInvulnerable persist through a ClearInventory by making it undroppable --- src/p_mobj.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 557f5fa94..c3593fc6e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4445,7 +4445,8 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { APowerup *invul = static_cast(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable))); invul->EffectTics = 3*TICRATE; - invul->BlendColor = 0; // don't mess with the view + invul->BlendColor = 0; // don't mess with the view + invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect } From e6de24a7dee333fb73b3a35682563f3008261053 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 6 Dec 2014 22:08:39 +0100 Subject: [PATCH 275/313] - turned out that the recent change to allow cancelling a powerup blend through a powerup giver did not work well so now there's an explicit 'Powerup.Color none' to do it that works a bit differently. --- src/g_shared/a_artifacts.cpp | 2 +- src/thingdef/thingdef_properties.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 410474f31..21006776e 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -60,7 +60,7 @@ bool APowerupGiver::Use (bool pickup) } if (BlendColor != 0) { - if (BlendColor.a != 0) power->BlendColor = BlendColor; + if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor; else power->BlendColor = 0; } if (Mode != NAME_None) diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index ec68dfb95..5b17185c4 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -2020,18 +2020,15 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) "INVERSEMAP", "GOLDMAP", "REDMAP", "GREENMAP", "BLUEMAP", NULL }; int alpha; - bool giver; PalEntry * pBlendColor; if (info->Class->IsDescendantOf(RUNTIME_CLASS(APowerup))) { pBlendColor = &((APowerup*)defaults)->BlendColor; - giver = false; } else if (info->Class->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) { pBlendColor = &((APowerupGiver*)defaults)->BlendColor; - giver = true; } else { @@ -2053,6 +2050,10 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) *pBlendColor = MakeSpecialColormap(v); return; } + else if (!stricmp(name, "none") && info->Class->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) + { + *pBlendColor = MakeSpecialColormap(65535); + } color = V_GetColor(NULL, name); } @@ -2064,7 +2065,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) else alpha = 255/3; alpha=clamp(alpha, 0, 255); - if (alpha != 0 || giver) *pBlendColor = MAKEARGB(alpha, 0, 0, 0) | color; + if (alpha != 0) *pBlendColor = MAKEARGB(alpha, 0, 0, 0) | color; else *pBlendColor = 0; } From 8a98be00dc1165564910a4f416a6e956d4311665 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Dec 2014 09:30:16 +0100 Subject: [PATCH 276/313] - fixed: ZDoom did not understand Boom's name for the Red Skull Key pickup message due to a typo. Of course, just changing the name would break many old ZDoom mods so now Boom's name is explicitly being translated into ZDoom's before setting the string. --- src/d_dehacked.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index d14015ca9..66fe2c58d 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2247,6 +2247,8 @@ static int PatchStrings (int dummy) ReplaceSpecialChars (holdstring.LockBuffer()); holdstring.UnlockBuffer(); + // Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message + if (!stricmp(Line1, "GOTREDSKULL")) Line1 = "GOTREDSKUL"; GStrings.SetString (Line1, holdstring); DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars()); } From 95bed868d3e320342ce3d3152f35acd1abe2167f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Dec 2014 14:46:53 +0100 Subject: [PATCH 277/313] - gcc warnings suck... --- src/d_dehacked.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 66fe2c58d..41ad71472 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2248,8 +2248,9 @@ static int PatchStrings (int dummy) ReplaceSpecialChars (holdstring.LockBuffer()); holdstring.UnlockBuffer(); // Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message - if (!stricmp(Line1, "GOTREDSKULL")) Line1 = "GOTREDSKUL"; - GStrings.SetString (Line1, holdstring); + const char *ll = Line1; + if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL"; + GStrings.SetString (ll, holdstring); DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars()); } From b2452b806ecb60e620cb2600cb2b9840f4a1a77d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Dec 2014 12:01:20 +0100 Subject: [PATCH 278/313] - missed a return. --- src/thingdef/thingdef_properties.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 5b17185c4..b548d4ef6 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -2053,6 +2053,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) else if (!stricmp(name, "none") && info->Class->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) { *pBlendColor = MakeSpecialColormap(65535); + return; } color = V_GetColor(NULL, name); From e5340ad6375a2311c11335d2be9df9367ffa0a34 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 9 Dec 2014 12:09:36 -0600 Subject: [PATCH 279/313] - Splitting the pull request in half. - Added THRUREFLECT, MIRRORREFLECT, and AIMREFLECT. All require REFLECTIVE to work. - THRUREFLECT prevents missiles from changing course if reflected, and takes precedence over all reflective flags. - MIRRORREFLECT causes missiles to perform a direct 180 turn-around. - AIMREFLECT turns the missile back to the original shooter, and does not slow the missile down. --- src/actor.h | 7 +++- src/p_map.cpp | 29 +++++++++++++---- src/p_mobj.cpp | 59 +++++++++++++++++++++++++++------- src/thingdef/thingdef_data.cpp | 3 ++ 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/actor.h b/src/actor.h index f46ee7148..88cfa983e 100644 --- a/src/actor.h +++ b/src/actor.h @@ -347,6 +347,11 @@ enum MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. + MF7_THRUREFLECT = 0x00000800, // Actors who are reflective cause the missiles to not slow down or change angles. + MF7_MIRRORREFLECT = 0x00001000, // Actor is turned directly 180 degrees around when reflected. + MF7_AIMREFLECT = 0x00002000, // Actor is directly reflected straight back at the one who fired the projectile. + + // --- mobj.renderflags --- @@ -859,7 +864,7 @@ public: DWORD flags4; // [RH] Even more flags! DWORD flags5; // OMG! We need another one. DWORD flags6; // Shit! Where did all the flags go? - DWORD flags7; // + DWORD flags7; // WHO WANTS TO BET ON 8!? // [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it. DWORD VisibleToTeam; diff --git a/src/p_map.cpp b/src/p_map.cpp index 3097ae824..857597089 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1283,6 +1283,16 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { P_GiveBody(thing, -damage); } + + if ((thing->flags7 & MF7_THRUREFLECT) && (thing->flags2 & MF2_REFLECTIVE) && (tm.thing->flags & MF_MISSILE)) + { + if (tm.thing->flags2 & MF2_SEEKERMISSILE) + { + tm.thing->tracer = tm.thing->target; + } + tm.thing->target = thing; + return true; + } return false; // don't traverse any more } if (thing->flags2 & MF2_PUSHABLE && !(tm.thing->flags2 & MF2_CANNOTPUSH)) @@ -1643,7 +1653,7 @@ bool P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj) { // Don't clip against self continue; } - if ((actor->flags & MF_MISSILE) && thing == actor->target) + if ((actor->flags & MF_MISSILE) && (thing == actor->target)) { // Don't clip against whoever shot the missile. continue; } @@ -2983,18 +2993,20 @@ bool P_BounceWall(AActor *mo) extern FRandom pr_bounce; bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) { + //Don't go through all of this if the actor is reflective and wants things to pass through them. + if (BlockingMobj && ((BlockingMobj->flags2 & MF2_REFLECTIVE) && (BlockingMobj->flags7 & MF7_THRUREFLECT))) return true; if (mo && BlockingMobj && ((mo->BounceFlags & BOUNCE_AllActors) - || ((mo->flags & MF_MISSILE) && (!(mo->flags2 & MF2_RIP) || (BlockingMobj->flags5 & MF5_DONTRIP) || ((mo->flags6 & MF6_NOBOSSRIP) && (BlockingMobj->flags2 & MF2_BOSS))) && (BlockingMobj->flags2 & MF2_REFLECTIVE)) - || ((BlockingMobj->player == NULL) && (!(BlockingMobj->flags3 & MF3_ISMONSTER))) - )) + || ((mo->flags & MF_MISSILE) && (!(mo->flags2 & MF2_RIP) + || (BlockingMobj->flags5 & MF5_DONTRIP) + || ((mo->flags6 & MF6_NOBOSSRIP) && (BlockingMobj->flags2 & MF2_BOSS))) && (BlockingMobj->flags2 & MF2_REFLECTIVE)) + || ((BlockingMobj->player == NULL) && (!(BlockingMobj->flags3 & MF3_ISMONSTER))))) { if (mo->bouncecount > 0 && --mo->bouncecount == 0) return false; if (!ontop) { fixed_t speed; - angle_t angle = R_PointToAngle2(BlockingMobj->x, - BlockingMobj->y, mo->x, mo->y) + ANGLE_1*((pr_bounce() % 16) - 8); + angle_t angle = R_PointToAngle2(BlockingMobj->x,BlockingMobj->y, mo->x, mo->y) + ANGLE_1*((pr_bounce() % 16) - 8); speed = P_AproxDistance(mo->velx, mo->vely); speed = FixedMul(speed, mo->wallbouncefactor); // [GZ] was 0.75, using wallbouncefactor seems more consistent mo->angle = angle; @@ -5090,6 +5102,8 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) // is normally for projectiles which would have exploded by now anyway... if (thing->flags6 & MF6_THRUSPECIES && thing->GetSpecies() == intersect->GetSpecies()) continue; + if ((thing->flags & MF_MISSILE) && (intersect->flags2 & MF2_REFLECTIVE) && (intersect->flags7 & MF7_THRUREFLECT)) + continue; if (!(intersect->flags2 & MF2_PASSMOBJ) || (!(intersect->flags3 & MF3_ISMONSTER) && intersect->Mass > mymass) || (intersect->flags4 & MF4_ACTLIKEBRIDGE) @@ -5098,7 +5112,8 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) // Can't push bridges or things more massive than ourself return 2; } - fixed_t oldz = intersect->z; + fixed_t oldz; + oldz = intersect->z; P_AdjustFloorCeil(intersect, cpos); intersect->z = thing->z + thing->height + 1; if (P_PushUp(intersect, cpos)) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f1cfb6e28..2da365d60 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1660,6 +1660,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) int steps, step, totalsteps; fixed_t startx, starty; fixed_t oldfloorz = mo->floorz; + fixed_t oldz = mo->z; fixed_t maxmove = (mo->waterlevel < 1) || (mo->flags & MF_MISSILE) || (mo->player && mo->player->crouchoffset<-10*FRACUNIT) ? MAXMOVE : MAXMOVE/4; @@ -1949,20 +1950,53 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) } if (BlockingMobj && (BlockingMobj->flags2 & MF2_REFLECTIVE)) { - angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); - - // Change angle for deflection/reflection - if (mo->AdjustReflectionAngle (BlockingMobj, angle)) + bool seeker = (mo->flags2 & MF2_SEEKERMISSILE) ? true : false; + // Don't change the angle if there's THRUREFLECT on the monster. + if (!(BlockingMobj->flags7 & MF7_THRUREFLECT)) { - goto explode; - } + 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); - // Reflect the missile along angle - 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; + // 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) + { + dir = P_FaceMobj(mo, mo->target, &delta); + if (dir) + { // Turn clockwise + mo->angle += delta; + } + else + { // Turn counter clockwise + mo->angle -= delta; + } + 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; + } + + + } if (mo->flags2 & MF2_SEEKERMISSILE) { mo->tracer = mo->target; @@ -2893,6 +2927,7 @@ int AActor::SpecialMissileHit (AActor *victim) bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) { if (flags2 & MF2_DONTREFLECT) return true; + if (thing->flags7 & MF7_THRUREFLECT) return false; // Change angle for reflection if (thing->flags4&MF4_SHIELDREFLECT) diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index e8cb91239..0bc7624a7 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -247,6 +247,9 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7), DEFINE_FLAG(MF7, CAUSEPAIN, AActor, flags7), + DEFINE_FLAG(MF7, THRUREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, MIRRORREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, AIMREFLECT, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), From 533ae959336a4cd7a2e5c2580c70c3bf81294e70 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 9 Dec 2014 12:30:14 -0600 Subject: [PATCH 280/313] - Added three missile impact pointer changing flags. - HITTARGET, HITMASTER, and HITTRACER. - A missile that dies hitting an actor will set this impacted actor as the new target/master/tracer, depending on specifications. --- src/actor.h | 3 +++ src/p_mobj.cpp | 3 +++ src/thingdef/thingdef_data.cpp | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/actor.h b/src/actor.h index f46ee7148..ecf8ad6a1 100644 --- a/src/actor.h +++ b/src/actor.h @@ -347,6 +347,9 @@ enum MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. + MF7_HITTARGET = 0x00004000, // The actor the projectile dies on is set to target, provided it's targetable anyway. + MF7_HITMASTER = 0x00008000, // Same as HITTARGET, except it's master instead of target. + MF7_HITTRACER = 0x00010000, // Same as HITTARGET, but for tracer. // --- mobj.renderflags --- diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f1cfb6e28..4d731f1fc 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1202,6 +1202,9 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) ) { + if (target->flags7 & MF7_HITTARGET) mo->target = target; + if (target->flags7 & MF7_HITMASTER) mo->master = target; + if (target->flags7 & MF7_HITTRACER) mo->tracer = target; if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash); if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index e8cb91239..17c186884 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -247,7 +247,10 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7), DEFINE_FLAG(MF7, CAUSEPAIN, AActor, flags7), - + DEFINE_FLAG(MF7, HITTARGET, AActor, flags7), + DEFINE_FLAG(MF7, HITMASTER, AActor, flags7), + DEFINE_FLAG(MF7, HITTRACER, AActor, flags7), + // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), DEFINE_FLAG2(FX_ROCKET, ROCKETTRAIL, AActor, effects), From c339bb33cf701a983784391ee6bd2a6051a4f68e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Wed, 10 Dec 2014 20:55:00 +1300 Subject: [PATCH 281/313] Added cl_waitforsave - cl_waitforsave (default true) pauses the game timer when a save starts, preventing the network buffer from trying to compensate for slow saves. --- src/g_game.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/g_game.cpp b/src/g_game.cpp index beec9435a..ba53254c2 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -116,6 +116,7 @@ CVAR (Bool, chasedemo, false, 0); CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, longsavemessages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, save_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR (Bool, cl_waitforsave, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); EXTERN_CVAR (Float, con_midtime); //========================================================================== @@ -2147,6 +2148,9 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio filename = G_BuildSaveName ("demosave.zds", -1); } + if (cl_waitforsave) + I_FreezeTime(true); + insave = true; G_SnapshotLevel (); @@ -2156,6 +2160,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio { Printf ("Could not create savegame '%s'\n", filename.GetChars()); insave = false; + I_FreezeTime(false); return; } @@ -2232,6 +2237,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } insave = false; + I_FreezeTime(false); } From 372f7e7002410e33b477aa275b74a74c920f861b Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Wed, 10 Dec 2014 21:11:26 +0100 Subject: [PATCH 282/313] - Various CMake fixes for two problems. 1) Don't show OSX_COCOA_BACKEND option if the host is not OSX; 2) Don't use the '-msse' compiler flag in dumb/ if the architecture does not support it. --- dumb/CMakeLists.txt | 7 ++++++- src/CMakeLists.txt | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index 2b70ee412..9c1a69a79 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required( VERSION 2.4 ) make_release_only() include( CheckFunctionExists ) +include( CheckCXXCompilerFlag ) # DUMB is much slower in a Debug build than a Release build, so we force a Release # build here, since we're not maintaining DUMB, only using it. @@ -104,5 +105,9 @@ add_library( dumb target_link_libraries( dumb ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse ) + CHECK_CXX_COMPILER_FLAG( -msse DUMB_CAN_USE_SSE ) + + if( DUMB_CAN_USE_SSE ) + set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse ) + endif( DUMB_CAN_USE_SSE ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07c13c20c..2cc3bc63e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,9 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON ) -option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) +if( APPLE ) + option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) +endif( APPLE ) if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) From 86372fce34d84e75ba424e20af677cfcfb5cd488 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 12 Dec 2014 16:19:37 -0500 Subject: [PATCH 283/313] - Added Steam detection for Stife: Veteran Edition. - Added Steam install scanning for Linux and OS X. (OS X and Win32 not yet tested.) --- src/CMakeLists.txt | 1 + src/cocoa/i_backend_cocoa.mm | 8 ++ src/d_iwad.cpp | 22 +--- src/sdl/i_steam.cpp | 217 +++++++++++++++++++++++++++++++++++ src/sdl/i_system.h | 4 + src/win32/i_system.cpp | 30 +++-- src/win32/i_system.h | 2 +- 7 files changed, 257 insertions(+), 27 deletions(-) create mode 100644 src/sdl/i_steam.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2cc3bc63e..8b924bd50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,6 +569,7 @@ set( PLAT_SDL_SYSTEM_SOURCES sdl/i_cd.cpp sdl/i_main.cpp sdl/i_movie.cpp + sdl/i_steam.cpp sdl/i_system.cpp sdl/sdlvideo.cpp sdl/st_start.cpp ) diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index bd3dc3115..9085d92db 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1619,6 +1619,14 @@ const char* I_GetBackEndName() } +FString OSX_FindApplicationSupport() +{ + NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; + if(url == nil) + return FString(); + return [[url path] UTF8String]; +} + // --------------------------------------------------------------------------- diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 6b430fa03..c03ed7a2f 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -430,27 +430,11 @@ int FIWadManager::IdentifyVersion (TArray &wadfiles, const char *iwad, } } } -#ifdef _WIN32 - FString steam_path = I_GetSteamPath(); - if (steam_path.IsNotEmpty()) + TArray steam_path = I_GetSteamPath(); + for (i = 0; i < steam_path.Size(); ++i) { - static const char *const steam_dirs[] = - { - "doom 2/base", - "final doom/base", - "heretic shadow of the serpent riders/base", - "hexen/base", - "hexen deathkings of the dark citadel/base", - "ultimate doom/base", - "DOOM 3 BFG Edition/base/wads" - }; - steam_path += "/SteamApps/common/"; - for (i = 0; i < countof(steam_dirs); ++i) - { - CheckIWAD (steam_path + steam_dirs[i], &wads[0]); - } + CheckIWAD (steam_path[i], &wads[0]); } -#endif } if (iwadparm != NULL && !wads[0].Path.IsEmpty()) diff --git a/src/sdl/i_steam.cpp b/src/sdl/i_steam.cpp new file mode 100644 index 000000000..1da7fc5fa --- /dev/null +++ b/src/sdl/i_steam.cpp @@ -0,0 +1,217 @@ +/* +** i_steam.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2013 Braden Obrzut +** 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 "doomerrors.h" +#include "d_main.h" +#include "zstring.h" +#include "sc_man.h" + +static void PSR_FindEndBlock(FScanner &sc) +{ + int depth = 1; + do + { + if(sc.CheckToken('}')) + --depth; + else if(sc.CheckToken('{')) + ++depth; + else + sc.MustGetAnyToken(); + } + while(depth); +} +static void PSR_SkipBlock(FScanner &sc) +{ + sc.MustGetToken('{'); + PSR_FindEndBlock(sc); +} +static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword) +{ + // Finds a block with a given keyword and then enter it (opening brace) + // Should be closed with PSR_FindEndBlock + while(sc.GetToken()) + { + if(sc.TokenType == '}') + { + sc.UnGet(); + return false; + } + + sc.TokenMustBe(TK_StringConst); + if(!sc.Compare(keyword)) + { + if(!sc.CheckToken(TK_StringConst)) + PSR_SkipBlock(sc); + } + else + { + sc.MustGetToken('{'); + return true; + } + } + return false; +} +static TArray PSR_ReadBaseInstalls(FScanner &sc) +{ + TArray result; + + // Get a list of possible install directories. + while(sc.GetToken()) + { + if(sc.TokenType == '}') + break; + + sc.TokenMustBe(TK_StringConst); + FString key(sc.String); + if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0) + { + sc.MustGetToken(TK_StringConst); + result.Push(sc.String); + } + else + { + if(sc.CheckToken('{')) + PSR_FindEndBlock(sc); + else + sc.MustGetToken(TK_StringConst); + } + } + + return result; +} +static TArray ParseSteamRegistry(const char* path) +{ + TArray dirs; + + // Read registry data + FScanner sc; + sc.OpenFile(path); + sc.SetCMode(true); + + // Find the SteamApps listing + if(PSR_FindAndEnterBlock(sc, "InstallConfigStore")) + { + if(PSR_FindAndEnterBlock(sc, "Software")) + { + if(PSR_FindAndEnterBlock(sc, "Valve")) + { + if(PSR_FindAndEnterBlock(sc, "Steam")) + { + dirs = PSR_ReadBaseInstalls(sc); + } + PSR_FindEndBlock(sc); + } + PSR_FindEndBlock(sc); + } + PSR_FindEndBlock(sc); + } + + return dirs; +} + +static struct SteamAppInfo +{ + const char* const BasePath; + const int AppID; +} AppInfo[] = +{ + /*{"doom 2/base", 2300}, + {"final doom/base", 2290}, + {"heretic shadow of the serpent riders/base", 2390}, + {"hexen/base", 2360}, + {"hexen deathkings of the dark citadel/base", 2370}, + {"ultimate doom/base", 2280}, + {"DOOM 3 BFG Edition/base/wads", 208200},*/ + {"Strife", 317040} +}; + +TArray I_GetSteamPath() +{ + TArray result; + TArray SteamInstallFolders; + + // Linux and OS X actually allow the user to install to any location, so + // we need to figure out on an app-by-app basis where the game is installed. + // To do so, we read the virtual registry. +#ifdef __APPLE__ + FString OSX_FindApplicationSupport(); + + FString regPath = OSX_FindApplicationSupport() + "/Steam/config/config.vdf"; + try + { + SteamInstallFolders = ParseSteamRegistry(regPath); + } + catch(class CDoomError &error) + { + // If we can't parse for some reason just pretend we can't find anything. + return result; + } + + SteamInstallFolders.Push(OSX_FindApplicationSupport() + "/Steam/SteamApps/common"); +#else + char* home = getenv("HOME"); + if(home != NULL && *home != '\0') + { + FString regPath; + regPath.Format("%s/.local/share/Steam/config/config.vdf", home); + try + { + SteamInstallFolders = ParseSteamRegistry(regPath); + } + catch(class CDoomError &error) + { + // If we can't parse for some reason just pretend we can't find anything. + return result; + } + + regPath.Format("%s/.local/share/Steam/SteamApps/common", home); + SteamInstallFolders.Push(regPath); + } +#endif + + for(unsigned int i = 0;i < SteamInstallFolders.Size();++i) + { + for(unsigned int app = 0;app < countof(AppInfo);++app) + { + struct stat st; + FString candidate(SteamInstallFolders[i] + "/" + AppInfo[app].BasePath); + if(stat(candidate, &st) == 0 && S_ISDIR(st.st_mode)) + result.Push(candidate); + } + } + + return result; +} diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index a3341f4c5..fa03d3c37 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -120,6 +120,10 @@ void I_SetIWADInfo (); // Pick from multiple IWADs to use int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); +// [RH] Checks the registry for Steam's install path, so we can scan its +// directories for IWADs if the user purchased any through Steam. +TArray I_GetSteamPath(); + // The ini could not be saved at exit bool I_WriteIniFailed (); diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 4248df274..71cc42af0 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1524,20 +1524,36 @@ static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FSt // //========================================================================== -FString I_GetSteamPath() +TArray I_GetSteamPath() { + TArray result; + static const char *const steam_dirs[] = + { + "doom 2/base", + "final doom/base", + "heretic shadow of the serpent riders/base", + "hexen/base", + "hexen deathkings of the dark citadel/base", + "ultimate doom/base", + "DOOM 3 BFG Edition/base/wads", + "Strife" + }; + FString path; - if (QueryPathKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "SteamPath", path)) + if (!QueryPathKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "SteamPath", path)) { - return path; + if (!QueryPathKey(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", "InstallPath", path)) + return result; } - if (QueryPathKey(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", "InstallPath", path)) + path += "/SteamApps/common/"; + + for(unsigned int i = 0; i < countof(steam_dirs); ++i) { - return path; + result.Push(path + steam_dirs[i]); } - path = ""; - return path; + + return result; } //========================================================================== diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 9fbf2db5c..647a08d13 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -142,7 +142,7 @@ void I_SetWndProc(); // [RH] Checks the registry for Steam's install path, so we can scan its // directories for IWADs if the user purchased any through Steam. -FString I_GetSteamPath(); +TArray I_GetSteamPath(); // Damn Microsoft for doing Get/SetWindowLongPtr half-assed. Instead of // giving them proper prototypes under Win32, they are just macros for From 8ee0554d567a693f7bff81e3fe8503b15ace716d Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 12 Dec 2014 16:33:49 -0500 Subject: [PATCH 284/313] - Fixed: Extra Steam paths still need steamapps/common appended to them. --- src/sdl/i_steam.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sdl/i_steam.cpp b/src/sdl/i_steam.cpp index 1da7fc5fa..f8b4c07d9 100644 --- a/src/sdl/i_steam.cpp +++ b/src/sdl/i_steam.cpp @@ -99,7 +99,7 @@ static TArray PSR_ReadBaseInstalls(FScanner &sc) if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0) { sc.MustGetToken(TK_StringConst); - result.Push(sc.String); + result.Push(FString(sc.String) + "/steamapps/common"); } else { @@ -170,6 +170,7 @@ TArray I_GetSteamPath() FString OSX_FindApplicationSupport(); FString regPath = OSX_FindApplicationSupport() + "/Steam/config/config.vdf"; + Printf("Reading %s\n", regPath.GetChars()); try { SteamInstallFolders = ParseSteamRegistry(regPath); @@ -208,6 +209,7 @@ TArray I_GetSteamPath() { struct stat st; FString candidate(SteamInstallFolders[i] + "/" + AppInfo[app].BasePath); + Printf("Checking %s\n", candidate.GetChars()); if(stat(candidate, &st) == 0 && S_ISDIR(st.st_mode)) result.Push(candidate); } From b14eded8d8f6d4e756299f43768178dbe188b4ec Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 12 Dec 2014 17:06:01 -0500 Subject: [PATCH 285/313] - Accidentally committed debuging code. --- src/sdl/i_steam.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sdl/i_steam.cpp b/src/sdl/i_steam.cpp index f8b4c07d9..23e74af5a 100644 --- a/src/sdl/i_steam.cpp +++ b/src/sdl/i_steam.cpp @@ -170,7 +170,6 @@ TArray I_GetSteamPath() FString OSX_FindApplicationSupport(); FString regPath = OSX_FindApplicationSupport() + "/Steam/config/config.vdf"; - Printf("Reading %s\n", regPath.GetChars()); try { SteamInstallFolders = ParseSteamRegistry(regPath); @@ -209,7 +208,6 @@ TArray I_GetSteamPath() { struct stat st; FString candidate(SteamInstallFolders[i] + "/" + AppInfo[app].BasePath); - Printf("Checking %s\n", candidate.GetChars()); if(stat(candidate, &st) == 0 && S_ISDIR(st.st_mode)) result.Push(candidate); } From 8c5a8c54f01a00d9ef691f60574c15d45400c13a Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 13 Dec 2014 15:08:18 -0600 Subject: [PATCH 286/313] - Added rclamp(,). - Usable for DECORATE expressions. Chooses one of the two numbers placed in the field. --- src/namedef.h | 1 + src/sc_man_scanner.re | 1 + src/sc_man_tokens.h | 1 + src/thingdef/thingdef_exp.cpp | 23 ++++++++ src/thingdef/thingdef_exp.h | 21 +++++++ src/thingdef/thingdef_expression.cpp | 83 ++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+) diff --git a/src/namedef.h b/src/namedef.h index 146997309..7eef612c1 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -300,6 +300,7 @@ xx(CallACS) xx(Sqrt) xx(CheckClass) xx(IsPointerEqual) +xx(RClamp) // Various actor names which are used internally xx(MapSpot) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index dd90ce1d1..2e25fb2f7 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -158,6 +158,7 @@ std2: 'random' { RET(TK_Random); } 'random2' { RET(TK_Random2); } 'frandom' { RET(TK_FRandom); } + 'rclamp' { RET(TK_RClamp); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 9dde74972..62622cf00 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -122,4 +122,5 @@ xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") xx(TK_AlignOf, "'alignof'") +xx(TK_RClamp, "'rclamp'") #undef xx diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 773f7ffac..4cef24187 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -371,6 +371,29 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) return new FxRandom(rng, min, max, sc); } + else if (sc.CheckToken(TK_RClamp)) + { + FRandom *rng; + + if (sc.CheckToken('[')) + { + sc.MustGetToken(TK_Identifier); + rng = FRandom::StaticFindRNG(sc.String); + sc.MustGetToken(']'); + } + else + { + rng = &pr_exrandom; + } + sc.MustGetToken('('); + + FxExpression *min = ParseExpressionM(sc, cls); + sc.MustGetToken(','); + FxExpression *max = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + + return new FxRClamp(rng, min, max, sc); + } else if (sc.CheckToken(TK_FRandom)) { FRandom *rng; diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 77af37f53..42e35489d 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -559,6 +559,27 @@ public: // //========================================================================== +class FxRClamp : public FxExpression +{ +protected: + FRandom * rng; + FxExpression *min, *max; + +public: + + FxRClamp(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); + ~FxRClamp(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression(AActor *self); +}; + +//========================================================================== +// +// +// +//========================================================================== + class FxFRandom : public FxRandom { public: diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 7dbf5ec36..5a5510a0b 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1691,6 +1691,89 @@ ExpVal FxRandom::EvalExpression (AActor *self) return val; } +//========================================================================== +// +// +// +//========================================================================== +FxRClamp::FxRClamp(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) +: FxExpression(pos) +{ + if (mi != NULL && ma != NULL) + { + min = new FxIntCast(mi); + max = new FxIntCast(ma); + } + else min = max = NULL; + rng = r; + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxRClamp::~FxRClamp() +{ + SAFE_DELETE(min); + SAFE_DELETE(max); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxRClamp::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + if (min && max) + { + RESOLVE(min, ctx); + RESOLVE(max, ctx); + ABORT(min && max); + } + return this; +}; + + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxRClamp::EvalExpression(AActor *self) +{ + ExpVal val; + val.Type = VAL_Int; + + if (min != NULL && max != NULL) + { + int minval = min->EvalExpression(self).GetInt(); + int maxval = max->EvalExpression(self).GetInt(); + + if (maxval < minval) + { + swapvalues(maxval, minval); + } + + val.Int = (*rng)(2); //rng->operator()(2); //(maxval - minval + 1) + minval; + if (val.Int > 0) + val.Int = maxval; + else + val.Int = minval; + } + else + { + val.Int = (*rng)(); + } + return val; +} + //========================================================================== // // From 785f72d6eb257dd3392a5962c3310503890b53ba Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 13 Dec 2014 15:59:27 -0600 Subject: [PATCH 287/313] - Renamed RClamp to Pick. --- src/namedef.h | 2 +- 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 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/namedef.h b/src/namedef.h index 7eef612c1..e0b7e8ba6 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -300,7 +300,7 @@ xx(CallACS) xx(Sqrt) xx(CheckClass) xx(IsPointerEqual) -xx(RClamp) +xx(Pick) // Various actor names which are used internally xx(MapSpot) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 2e25fb2f7..3a7f717a8 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); } - 'rclamp' { RET(TK_RClamp); } + 'pick' { RET(TK_Pick); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 62622cf00..1c22046c9 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_RClamp, "'rclamp'") +xx(TK_Pick, "'pick'") #undef xx diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 4cef24187..fd0936485 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_RClamp)) + else if (sc.CheckToken(TK_Pick)) { FRandom *rng; @@ -392,7 +392,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) FxExpression *max = ParseExpressionM(sc, cls); sc.MustGetToken(')'); - return new FxRClamp(rng, min, max, sc); + return new FxPick(rng, min, max, sc); } else if (sc.CheckToken(TK_FRandom)) { diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 42e35489d..03e465c3a 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -559,7 +559,7 @@ public: // //========================================================================== -class FxRClamp : public FxExpression +class FxPick : public FxExpression { protected: FRandom * rng; @@ -567,8 +567,8 @@ protected: public: - FxRClamp(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); - ~FxRClamp(); + FxPick(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); + ~FxPick(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression(AActor *self); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 5a5510a0b..dc2cb2209 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1696,7 +1696,7 @@ ExpVal FxRandom::EvalExpression (AActor *self) // // //========================================================================== -FxRClamp::FxRClamp(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) +FxPick::FxPick(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) : FxExpression(pos) { if (mi != NULL && ma != NULL) @@ -1715,7 +1715,7 @@ FxRClamp::FxRClamp(FRandom * r, FxExpression *mi, FxExpression *ma, const FScrip // //========================================================================== -FxRClamp::~FxRClamp() +FxPick::~FxPick() { SAFE_DELETE(min); SAFE_DELETE(max); @@ -1727,7 +1727,7 @@ FxRClamp::~FxRClamp() // //========================================================================== -FxExpression *FxRClamp::Resolve(FCompileContext &ctx) +FxExpression *FxPick::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (min && max) @@ -1746,7 +1746,7 @@ FxExpression *FxRClamp::Resolve(FCompileContext &ctx) // //========================================================================== -ExpVal FxRClamp::EvalExpression(AActor *self) +ExpVal FxPick::EvalExpression(AActor *self) { ExpVal val; val.Type = VAL_Int; From 3f3aab42f1f1f20a522411c163800a78472b24b4 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 14 Dec 2014 04:45:39 -0600 Subject: [PATCH 288/313] - Pick can now hold unlimited numbers to choose from. - I.e. pick(1,4,12,16) --- src/thingdef/thingdef_exp.cpp | 18 ++++++++--- src/thingdef/thingdef_exp.h | 4 +-- src/thingdef/thingdef_expression.cpp | 47 +++++++++++++--------------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index fd0936485..daa7f1a9c 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -374,6 +374,9 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) else if (sc.CheckToken(TK_Pick)) { FRandom *rng; + TArray list; + list.Clear(); + int index = 0; if (sc.CheckToken('[')) { @@ -387,12 +390,17 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) } sc.MustGetToken('('); - FxExpression *min = ParseExpressionM(sc, cls); - sc.MustGetToken(','); - FxExpression *max = ParseExpressionM(sc, cls); - sc.MustGetToken(')'); + while (!(sc.CheckToken(')'))) + { + FxExpression *min = ParseExpressionM(sc, cls); + list.Push(min); + if (sc.CheckToken(')')) + break; + else + sc.MustGetToken(','); + } - return new FxPick(rng, min, max, sc); + return new FxPick(rng, list, sc); } else if (sc.CheckToken(TK_FRandom)) { diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 03e465c3a..b53b8796b 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -563,11 +563,11 @@ class FxPick : public FxExpression { protected: FRandom * rng; - FxExpression *min, *max; + TArray min; public: - FxPick(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); + FxPick(FRandom *, TArray mi, const FScriptPosition &pos); ~FxPick(); FxExpression *Resolve(FCompileContext&); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index dc2cb2209..dd8e0e4c9 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1696,15 +1696,17 @@ ExpVal FxRandom::EvalExpression (AActor *self) // // //========================================================================== -FxPick::FxPick(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) +FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos) : FxExpression(pos) { - if (mi != NULL && ma != NULL) + int index = 0; + int max = mi.Size(); + if (max > 0) { - min = new FxIntCast(mi); - max = new FxIntCast(ma); + for (index = 0; index < max; index++) + min.Push(new FxIntCast(mi[index])); } - else min = max = NULL; + else min.Clear(); rng = r; ValueType = VAL_Int; } @@ -1717,8 +1719,7 @@ FxPick::FxPick(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPos FxPick::~FxPick() { - SAFE_DELETE(min); - SAFE_DELETE(max); + min.Clear(); } //========================================================================== @@ -1729,12 +1730,17 @@ FxPick::~FxPick() FxExpression *FxPick::Resolve(FCompileContext &ctx) { + int index = 0; CHECKRESOLVED(); - if (min && max) + int max = min.Size(); + if (max > 0) { - RESOLVE(min, ctx); - RESOLVE(max, ctx); - ABORT(min && max); + for (index = 0; index < max; index++) + { + RESOLVE(min[index], ctx); + ABORT(min[index]); + } + } return this; }; @@ -1750,22 +1756,11 @@ ExpVal FxPick::EvalExpression(AActor *self) { ExpVal val; val.Type = VAL_Int; - - if (min != NULL && max != NULL) + int max = min.Size(); + if (max > 0) { - int minval = min->EvalExpression(self).GetInt(); - int maxval = max->EvalExpression(self).GetInt(); - - if (maxval < minval) - { - swapvalues(maxval, minval); - } - - val.Int = (*rng)(2); //rng->operator()(2); //(maxval - minval + 1) + minval; - if (val.Int > 0) - val.Int = maxval; - else - val.Int = minval; + int select = (*rng)(max); + val.Int = min[select]->EvalExpression(self).GetInt(); } else { From 6410428715cdcffdc4727ef31ca3549d40105f00 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 14 Dec 2014 12:03:55 +0100 Subject: [PATCH 289/313] - fixed: FxPick leaked the array's content. Also did some cleanup on FxPick code. --- src/thingdef/thingdef_exp.h | 2 +- src/thingdef/thingdef_expression.cpp | 21 +++++---------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index b53b8796b..0e03c661f 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -563,7 +563,7 @@ class FxPick : public FxExpression { protected: FRandom * rng; - TArray min; + TDeletingArray min; public: diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index dd8e0e4c9..913079bd2 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1699,14 +1699,10 @@ ExpVal FxRandom::EvalExpression (AActor *self) FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos) : FxExpression(pos) { - int index = 0; - int max = mi.Size(); - if (max > 0) + for (unsigned int index = 0; index < mi.Size(); index++) { - for (index = 0; index < max; index++) - min.Push(new FxIntCast(mi[index])); + min.Push(new FxIntCast(mi[index])); } - else min.Clear(); rng = r; ValueType = VAL_Int; } @@ -1719,7 +1715,6 @@ FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos FxPick::~FxPick() { - min.Clear(); } //========================================================================== @@ -1730,17 +1725,11 @@ FxPick::~FxPick() FxExpression *FxPick::Resolve(FCompileContext &ctx) { - int index = 0; CHECKRESOLVED(); - int max = min.Size(); - if (max > 0) + for (unsigned int index = 0; index < min.Size(); index++) { - for (index = 0; index < max; index++) - { - RESOLVE(min[index], ctx); - ABORT(min[index]); - } - + RESOLVE(min[index], ctx); + ABORT(min[index]); } return this; }; From b6c5cfb9efc8f398d09cbbfde3da653bdb968eaf Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 15 Dec 2014 10:26:22 -0600 Subject: [PATCH 290/313] - Fixed: The new HIT* flags were checking the wrong actor. --- src/p_mobj.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 39d1ba12d..49b4b983f 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1202,9 +1202,9 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) ) { - if (target->flags7 & MF7_HITTARGET) mo->target = target; - if (target->flags7 & MF7_HITMASTER) mo->master = target; - if (target->flags7 & MF7_HITTRACER) mo->tracer = target; + if (mo->flags7 & MF7_HITTARGET) mo->target = target; + if (mo->flags7 & MF7_HITMASTER) mo->master = target; + if (mo->flags7 & MF7_HITTRACER) mo->tracer = target; if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash); if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); } From 465d9ab89a288e9194b5143eb6a54d92692ba5ea Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 15 Dec 2014 14:50:35 -0600 Subject: [PATCH 291/313] - Added flags for A_CheckLOF: - CLOFF_SETTARGET | CLOFF_SETMASTER | CLOFF_SETTRACER - An actor that causes A_CheckLOF (and only an actor) to succeed will set the intercepting actor as its target, master, and/or tracer, respectively. --- src/thingdef/thingdef_codeptr.cpp | 57 ++++++++++++++++++------------ wadsrc/static/actors/constants.txt | 4 +++ 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a620f0080..2a2da343e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3071,37 +3071,41 @@ DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) enum CLOF_flags { - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, + CLOFF_NOAIM_VERT = 0x00000001, + CLOFF_NOAIM_HORZ = 0x00000002, - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, + CLOFF_JUMPENEMY = 0x00000004, + CLOFF_JUMPFRIEND = 0x00000008, + CLOFF_JUMPOBJECT = 0x00000010, + CLOFF_JUMPNONHOSTILE = 0x00000020, - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, + CLOFF_SKIPENEMY = 0x00000040, + CLOFF_SKIPFRIEND = 0x00000080, + CLOFF_SKIPOBJECT = 0x00000100, + CLOFF_SKIPNONHOSTILE = 0x00000200, - CLOFF_MUSTBESHOOTABLE = 0x400, + CLOFF_MUSTBESHOOTABLE = 0x00000400, - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, + CLOFF_SKIPTARGET = 0x00000800, + CLOFF_ALLOWNULL = 0x00001000, + CLOFF_CHECKPARTIAL = 0x00002000, - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, + CLOFF_MUSTBEGHOST = 0x00004000, + CLOFF_IGNOREGHOST = 0x00008000, - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, + CLOFF_MUSTBESOLID = 0x00010000, + CLOFF_BEYONDTARGET = 0x00020000, - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, + CLOFF_FROMBASE = 0x00040000, + CLOFF_MUL_HEIGHT = 0x00080000, + CLOFF_MUL_WIDTH = 0x00100000, - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, + CLOFF_JUMP_ON_MISS = 0x00200000, + CLOFF_AIM_VERT_NOOFFSET = 0x00400000, + + CLOFF_SETTARGET = 0x00800000, + CLOFF_SETMASTER = 0x01000000, + CLOFF_SETTRACER = 0x02000000, }; struct LOFData @@ -3341,6 +3345,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) { 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); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 22914fbfc..8695c9c19 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -372,6 +372,10 @@ enum CLOFF_JUMP_ON_MISS = 0x200000, CLOFF_AIM_VERT_NOOFFSET = 0x400000, + CLOFF_SETTARGET = 0x800000, + CLOFF_SETMASTER = 0x1000000, + CLOFF_SETTRACER = 0x2000000, + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ }; From fc05a3bb2f602182d7195cd7cb7603ce018b2e84 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 16 Dec 2014 12:37:31 -0600 Subject: [PATCH 292/313] - Added HIT* flag detection for bullet and rail attacks. --- src/p_map.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 857597089..871ad6c8b 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3746,6 +3746,8 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, hity = t1->y + FixedMul(vy, dist); hitz = shootz + FixedMul(vz, dist); + + // Spawn bullet puffs or blood spots, depending on target type. if ((puffDefaults != NULL && puffDefaults->flags3 & MF3_PUFFONACTORS) || (trace.Actor->flags & MF_NOBLOOD) || @@ -3758,6 +3760,13 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING); } + if (puffDefaults != NULL && trace.Actor != NULL) + { + if (puffDefaults->flags7 && MF7_HITTARGET) puffDefaults->target = trace.Actor; + if (puffDefaults->flags7 && MF7_HITMASTER) puffDefaults->master = trace.Actor; + if (puffDefaults->flags7 && MF7_HITTRACER) puffDefaults->tracer = trace.Actor; + } + // Allow puffs to inflict poison damage, so that hitscans can poison, too. if (puffDefaults != NULL && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN) { @@ -3776,7 +3785,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, { dmgflags |= DMG_NO_ARMOR; } - + if (puff == NULL) { // Since the puff is the damage inflictor we need it here @@ -4150,7 +4159,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i int flags; assert(puffclass != NULL); // Because we set it to a default above - AActor *puffDefaults = GetDefaultByType(puffclass->GetReplacement()); + AActor *puffDefaults = GetDefaultByType(puffclass->GetReplacement()); //Contains all the flags such as FOILINVUL, etc. flags = (puffDefaults->flags6 & MF6_NOTRIGGER) ? 0 : TRACE_PCross | TRACE_Impact; rail_data.StopAtInvul = (puffDefaults->flags3 & MF3_FOILINVUL) ? false : true; @@ -4200,6 +4209,12 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i { P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags); } + if (hitactor != NULL && puffDefaults != NULL) + { + if (puffDefaults->flags7 & MF7_HITTARGET) puffDefaults->target = hitactor; + if (puffDefaults->flags7 & MF7_HITMASTER) puffDefaults->master = hitactor; + if (puffDefaults->flags7 & MF7_HITTRACER) puffDefaults->tracer = 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); @@ -4208,6 +4223,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i dmgFlagPass += (puffDefaults->flags3 & MF3_FOILINVUL) ? DMG_FOILINVUL : 0; //[MC]Because the original foilinvul check wasn't working. dmgFlagPass += (puffDefaults->flags7 & MF7_FOILBUDDHA) ? DMG_FOILBUDDHA : 0; int newdam = P_DamageMobj(hitactor, thepuff ? thepuff : source, source, damage, damagetype, dmgFlagPass); + if (bleed) { P_SpawnBlood(x, y, z, (source->angle + angleoffset) - ANG180, newdam > 0 ? newdam : damage, hitactor); From 24f1bfae99c6d2d79bac8fb9f0bdc296b5aaee0d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 16 Dec 2014 12:48:52 -0600 Subject: [PATCH 293/313] - Need to set it on the puff itself, not the defaults. --- src/p_map.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 871ad6c8b..48da5c72c 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3760,11 +3760,11 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING); } - if (puffDefaults != NULL && trace.Actor != NULL) + if (puffDefaults != NULL && trace.Actor != NULL && puff != NULL) { - if (puffDefaults->flags7 && MF7_HITTARGET) puffDefaults->target = trace.Actor; - if (puffDefaults->flags7 && MF7_HITMASTER) puffDefaults->master = trace.Actor; - if (puffDefaults->flags7 && MF7_HITTRACER) puffDefaults->tracer = trace.Actor; + 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; } // Allow puffs to inflict poison damage, so that hitscans can poison, too. @@ -4209,11 +4209,11 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i { P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags); } - if (hitactor != NULL && puffDefaults != NULL) + if (hitactor != NULL && puffDefaults != NULL && thepuff != NULL) { - if (puffDefaults->flags7 & MF7_HITTARGET) puffDefaults->target = hitactor; - if (puffDefaults->flags7 & MF7_HITMASTER) puffDefaults->master = hitactor; - if (puffDefaults->flags7 & MF7_HITTRACER) puffDefaults->tracer = hitactor; + if (puffDefaults->flags7 & MF7_HITTARGET) thepuff->target = hitactor; + if (puffDefaults->flags7 & MF7_HITMASTER) thepuff->master = hitactor; + if (puffDefaults->flags7 & MF7_HITTRACER) thepuff->tracer = hitactor; } if (puffDefaults && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN) { From 30acb720068f714a44787fb95cbb22cd0fc3acf4 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 16:11:07 -0600 Subject: [PATCH 294/313] - Added per-actor teleport fog modifications. - New properties include TeleFogSourceType and TeleFogDestType. - TeleFogSourceType is the fog left behind where the actor teleported away from. - TeleFogDestType is the fog the actor sees when it arrives at its destination. - Added A_SetTeleFog(,) -- oldpos sets TeleFogSourceType, newpos sets TeleFogDestType. --- src/actor.h | 2 ++ src/p_mobj.cpp | 12 ++---------- src/p_spec.h | 1 + src/p_teleport.cpp | 24 ++++++++++++----------- src/p_things.cpp | 6 +++--- src/thingdef/thingdef_codeptr.cpp | 29 +++++++++++++++++++++++++--- src/thingdef/thingdef_properties.cpp | 20 +++++++++++++++++++ wadsrc/static/actors/actor.txt | 1 + 8 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/actor.h b/src/actor.h index 288eb3129..592dca47c 100644 --- a/src/actor.h +++ b/src/actor.h @@ -983,6 +983,8 @@ public: FNameNoInit PainType; FNameNoInit DeathType; + FNameNoInit TeleFogSourceType; + FNameNoInit TeleFogDestType; FState *SpawnState; FState *SeeState; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 49b4b983f..21078b3ec 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2680,18 +2680,10 @@ void P_NightmareRespawn (AActor *mobj) mo->PrevZ = z; // Do not interpolate Z position if we changed it since spawning. // spawn a teleport fog at old spot because of removal of the body? - mo = Spawn ("TeleportFog", mobj->x, mobj->y, mobj->z, ALLOW_REPLACE); - if (mo != NULL) - { - mo->z += TELEFOGHEIGHT; - } + P_SpawnTeleportFog(mobj, mobj->x, mobj->y, mobj->z + TELEFOGHEIGHT, true); // spawn a teleport fog at the new spot - mo = Spawn ("TeleportFog", x, y, z, ALLOW_REPLACE); - if (mo != NULL) - { - mo->z += TELEFOGHEIGHT; - } + P_SpawnTeleportFog(mobj, x, y, z + TELEFOGHEIGHT, false); // remove the old monster mobj->Destroy (); diff --git a/src/p_spec.h b/src/p_spec.h index 906a38a8c..4af527ca8 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -903,6 +903,7 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag); // // P_TELEPT // +void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele = true, bool replace = true, bool setTarget = false); //Spawns teleport fog. Pass the actor to pluck TeleFogFromType and TeleFogToType. 'from' determines if this is the fog to spawn at the old position (true) or new (false). bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, bool useFog, bool sourceFog, bool keepOrientation, bool haltVelocity = true, bool keepHeight = false); bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity = true, bool keepHeight = false); bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBOOL reverse); diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 60308d8ed..d86a87672 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -74,19 +74,23 @@ void ATeleportFog::PostBeginPlay () // //========================================================================== -void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid) +void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool replace, bool setTarget) { - const PClass *fog = P_GetSpawnableType(spawnid); + AActor *mo; + FNameNoInit tf = (beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType); - if (fog == NULL) + if (!tf) //If the actor doesn't have one, initialize the original. { - AActor *mo = Spawn ("TeleportFog", x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE); + mo = Spawn(x, y, z, (replace ? ALLOW_REPLACE : NO_REPLACE)); } else { - AActor *mo = Spawn (fog, x, y, z, ALLOW_REPLACE); - if (mo != NULL) S_Sound(mo, CHAN_BODY, mo->SeeSound, 1.f, ATTN_NORM); + mo = Spawn(tf, x, y, z, (replace ? ALLOW_REPLACE : NO_REPLACE)); } + + if (mo != NULL && setTarget) + mo->target = mobj; + } // @@ -186,8 +190,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, if (sourceFog && !predicting) { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; - AActor *fog = Spawn (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE); - fog->target = thing; + P_SpawnTeleportFog(thing, oldx, oldy, oldz, true, true, true); //Passes the actor through which then pulls the TeleFog metadate types based on properties. } if (useFog) { @@ -195,9 +198,8 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; an = angle >> ANGLETOFINESHIFT; - AActor *fog = Spawn(x + 20 * finecosine[an], - y + 20 * finesine[an], thing->z + fogDelta, ALLOW_REPLACE); - fog->target = thing; + P_SpawnTeleportFog(thing, x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, false, true, true); + } if (thing->player) { diff --git a/src/p_things.cpp b/src/p_things.cpp index 63173564c..a8a34c384 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -92,7 +92,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, mobj->angle = (angle != ANGLE_MAX ? angle : spot->angle); if (fog) { - Spawn (spot->x, spot->y, spot->z + TELEFOGHEIGHT, ALLOW_REPLACE); + P_SpawnTeleportFog(mobj, spot->x, spot->y, spot->z + TELEFOGHEIGHT, false); } if (mobj->flags & MF_SPECIAL) mobj->flags |= MF_DROPPED; // Don't respawn @@ -130,8 +130,8 @@ bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog) { if (fog) { - Spawn (x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE); - Spawn (oldx, oldy, oldz + TELEFOGHEIGHT, ALLOW_REPLACE); + P_SpawnTeleportFog(source, x, y, z); + P_SpawnTeleportFog(source, oldx, oldy, oldz, false); } source->PrevX = x; source->PrevY = y; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2a2da343e..2f6b74e90 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2938,9 +2938,10 @@ 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); @@ -2998,7 +2999,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) if (flags & RSF_FOG) { - Spawn (self->x, self->y, self->z + TELEFOGHEIGHT, ALLOW_REPLACE); + P_SpawnTeleportFog(self, oldx, oldy, oldz, true); + P_SpawnTeleportFog(self, self->x, self->y, self->z, false); } if (self->CountsAsKill()) { @@ -5377,3 +5379,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) } } +//=========================================================================== +// +// A_SetTeleFog +// +// Sets the teleport fog(s) for the calling actor. +// Takes a name of the classes for te source and destination. +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(oldpos, 0); + ACTION_PARAM_NAME(newpos, 1); + + if (oldpos) + self->TeleFogSourceType = oldpos; + if (newpos) + self->TeleFogDestType = newpos; +} + + diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index b548d4ef6..427e64008 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1416,6 +1416,26 @@ DEFINE_PROPERTY(stamina, I, Actor) defaults->stamina = i; } +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(telefogsourcetype, S, Actor) +{ + PROP_STRING_PARM(str, 0); + if (!stricmp(str, "TeleportFog")) defaults->TeleFogSourceType = NAME_None; + else defaults->TeleFogSourceType = str; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(telefogdesttype, S, Actor) +{ + PROP_STRING_PARM(str, 0); + if (!stricmp(str, "TeleportFog")) defaults->TeleFogDestType = NAME_None; + else defaults->TeleFogDestType = str; +} + //========================================================================== // // Special inventory properties diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 2f97c2a71..326f14d20 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -317,6 +317,7 @@ ACTOR Actor native //: Thinker action native A_GiveToSiblings(class itemtype, int amount = 0); action native A_TakeFromChildren(class itemtype, int amount = 0); action native A_TakeFromSiblings(class itemtype, int amount = 0); + action native A_SetTeleFog(name oldpos, name newpos); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From b255e5d67d454c1b4b47f08a5a3f114f68f70424 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 17:12:33 -0600 Subject: [PATCH 295/313] - Thought I was forgetting something. --- src/thingdef/thingdef_properties.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 427e64008..e9153a071 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1422,7 +1422,7 @@ DEFINE_PROPERTY(stamina, I, Actor) DEFINE_PROPERTY(telefogsourcetype, S, Actor) { PROP_STRING_PARM(str, 0); - if (!stricmp(str, "TeleportFog")) defaults->TeleFogSourceType = NAME_None; + if (str == NULL) defaults->TeleFogSourceType = "TeleportFog"; else defaults->TeleFogSourceType = str; } @@ -1432,8 +1432,8 @@ DEFINE_PROPERTY(telefogsourcetype, S, Actor) DEFINE_PROPERTY(telefogdesttype, S, Actor) { PROP_STRING_PARM(str, 0); - if (!stricmp(str, "TeleportFog")) defaults->TeleFogDestType = NAME_None; - else defaults->TeleFogDestType = str; + if (str == NULL) defaults->TeleFogSourceType = "TeleportFog"; + defaults->TeleFogDestType = str; } //========================================================================== From dd3d7789d549e1457b15b972cd5f0fbcd133e71d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 17:16:56 -0600 Subject: [PATCH 296/313] - Always allow replacement. --- src/p_spec.h | 2 +- src/p_teleport.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_spec.h b/src/p_spec.h index 4af527ca8..a225d2379 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -903,7 +903,7 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag); // // P_TELEPT // -void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele = true, bool replace = true, bool setTarget = false); //Spawns teleport fog. Pass the actor to pluck TeleFogFromType and TeleFogToType. 'from' determines if this is the fog to spawn at the old position (true) or new (false). +void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele = true, bool setTarget = false); //Spawns teleport fog. Pass the actor to pluck TeleFogFromType and TeleFogToType. 'from' determines if this is the fog to spawn at the old position (true) or new (false). bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, bool useFog, bool sourceFog, bool keepOrientation, bool haltVelocity = true, bool keepHeight = false); bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity = true, bool keepHeight = false); bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBOOL reverse); diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index d86a87672..df103f37f 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -74,18 +74,18 @@ void ATeleportFog::PostBeginPlay () // //========================================================================== -void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool replace, bool setTarget) +void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool setTarget) { AActor *mo; FNameNoInit tf = (beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType); if (!tf) //If the actor doesn't have one, initialize the original. { - mo = Spawn(x, y, z, (replace ? ALLOW_REPLACE : NO_REPLACE)); + mo = Spawn(x, y, z, ALLOW_REPLACE); } else { - mo = Spawn(tf, x, y, z, (replace ? ALLOW_REPLACE : NO_REPLACE)); + mo = Spawn(tf, x, y, z, ALLOW_REPLACE); } if (mo != NULL && setTarget) From 6f7c9f1f3469aa2c6d68717e484f361441aa3503 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 17:22:47 -0600 Subject: [PATCH 297/313] - Small adjustment for parameters here too. --- src/p_teleport.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index df103f37f..4b728b115 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -190,7 +190,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, if (sourceFog && !predicting) { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; - P_SpawnTeleportFog(thing, oldx, oldy, oldz, true, true, true); //Passes the actor through which then pulls the TeleFog metadate types based on properties. + P_SpawnTeleportFog(thing, oldx, oldy, oldz, true, true); //Passes the actor through which then pulls the TeleFog metadate types based on properties. } if (useFog) { @@ -198,7 +198,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; an = angle >> ANGLETOFINESHIFT; - P_SpawnTeleportFog(thing, x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, false, true, true); + P_SpawnTeleportFog(thing, x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, false, true); } if (thing->player) From 160ded99a96a0228016d032135d8606c37b4ab6c Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 17:42:16 -0600 Subject: [PATCH 298/313] - A few more checks to be safe. --- src/thingdef/thingdef_properties.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index e9153a071..22612fa78 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1422,7 +1422,7 @@ DEFINE_PROPERTY(stamina, I, Actor) DEFINE_PROPERTY(telefogsourcetype, S, Actor) { PROP_STRING_PARM(str, 0); - if (str == NULL) defaults->TeleFogSourceType = "TeleportFog"; + if (str == NULL || *str == 0 || (!stricmp(str,""))) defaults->TeleFogSourceType = "TeleportFog"; else defaults->TeleFogSourceType = str; } @@ -1432,8 +1432,8 @@ DEFINE_PROPERTY(telefogsourcetype, S, Actor) DEFINE_PROPERTY(telefogdesttype, S, Actor) { PROP_STRING_PARM(str, 0); - if (str == NULL) defaults->TeleFogSourceType = "TeleportFog"; - defaults->TeleFogDestType = str; + if (str == NULL || *str == 0 || (!stricmp(str, ""))) defaults->TeleFogDestType = "TeleportFog"; + else defaults->TeleFogDestType = str; } //========================================================================== From 93ca8502dd5ac32bb271a435c7cd3e929706d450 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 21:47:00 -0600 Subject: [PATCH 299/313] - Rewrote a bunch of things for stability. --- src/actor.h | 4 +- src/p_acs.cpp | 81 +++++++++++++++++++++++++++- src/p_mobj.cpp | 6 ++- src/p_teleport.cpp | 6 +-- src/thingdef/thingdef_codeptr.cpp | 27 ++++++++-- src/thingdef/thingdef_properties.cpp | 8 +-- src/version.h | 2 +- 7 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/actor.h b/src/actor.h index 592dca47c..57736f43b 100644 --- a/src/actor.h +++ b/src/actor.h @@ -983,8 +983,8 @@ public: FNameNoInit PainType; FNameNoInit DeathType; - FNameNoInit TeleFogSourceType; - FNameNoInit TeleFogDestType; + const PClass *TeleFogSourceType; + const PClass *TeleFogDestType; FState *SpawnState; FState *SeeState; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 866087688..54a7110a1 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4438,6 +4438,8 @@ enum EACSFunctions ACSF_PickActor, ACSF_IsPointerEqual, ACSF_CanRaiseActor, + ACSF_SetActorTeleFog, // 86 + ACSF_SwapActorTeleFog, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -4749,6 +4751,72 @@ static void SetActorPitch(AActor *activator, int tid, int angle, bool interpolat } } +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. + const PClass *check; + if (tid == 0) + { + if (activator != NULL) + { + check = PClass::FindClass(telefogsrc); + if (check != NULL) + activator->TeleFogSourceType = check; + check = PClass::FindClass(telefogdest); + if (check != NULL) + activator->TeleFogDestType = check; + } + } + else + { + FActorIterator iterator(tid); + AActor *actor; + + while ((actor = iterator.Next())) + { + check = PClass::FindClass(telefogsrc); + if (check != NULL) + activator->TeleFogSourceType = check; + check = PClass::FindClass(telefogdest); + if (check != NULL) + activator->TeleFogDestType = check; + } + } +} + +static int SwapActorTeleFog(AActor *activator, int tid) +{ + int count = 0; + if (tid == 0) + { + if ((activator == NULL) || (activator->TeleFogSourceType = activator->TeleFogDestType)) + return 0; //Does nothing if they're the same. + else + { + const PClass *temp = activator->TeleFogSourceType; + activator->TeleFogSourceType = activator->TeleFogDestType; + activator->TeleFogDestType = temp; + return 1; + } + } + else + { + FActorIterator iterator(tid); + AActor *actor; + + while ((actor = iterator.Next())) + { + if (activator->TeleFogSourceType == activator->TeleFogDestType) + continue; //They're the same. Save the effort. + const PClass *temp = activator->TeleFogSourceType; + activator->TeleFogSourceType = activator->TeleFogDestType; + activator->TeleFogDestType = temp; + count++; + } + } + return count; +} + int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const SDWORD *stack, int stackdepth) @@ -5662,7 +5730,18 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) SetActorPitch(activator, args[0], args[1], argCount > 2 ? !!args[2] : false); } break; - + case ACSF_SetActorTeleFog: + if (argCount >= 3) + { + SetActorTeleFog(activator, args[0], FBehavior::StaticLookupString(args[1]), FBehavior::StaticLookupString(args[2])); + } + break; + case ACSF_SwapActorTeleFog: + if (argCount >= 1) + { + return SwapActorTeleFog(activator, args[0]); + } + break; case ACSF_PickActor: if (argCount >= 5) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 21078b3ec..e1eed19c1 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -333,7 +333,11 @@ void AActor::Serialize (FArchive &arc) { arc << FriendPlayer; } - + if (SaveVersion >= 4518) + { + arc << TeleFogSourceType + << TeleFogDestType; + } { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 4b728b115..a73ff75e7 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -77,15 +77,13 @@ void ATeleportFog::PostBeginPlay () void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool setTarget) { AActor *mo; - FNameNoInit tf = (beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType); - - if (!tf) //If the actor doesn't have one, initialize the original. + if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL) //If the actor doesn't have one, initialize the original. { mo = Spawn(x, y, z, ALLOW_REPLACE); } else { - mo = Spawn(tf, x, y, z, ALLOW_REPLACE); + mo = Spawn((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType), x, y, z, ALLOW_REPLACE); } if (mo != NULL && setTarget) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2f6b74e90..aa643de8c 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5385,6 +5385,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) // // 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) @@ -5392,11 +5394,28 @@ 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) + self->TeleFogSourceType = check; - if (oldpos) - self->TeleFogSourceType = oldpos; - if (newpos) - self->TeleFogDestType = newpos; + check = PClass::FindClass(newpos); + if (check != NULL) + 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; + } +} diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 22612fa78..0c4ec25cb 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1422,8 +1422,8 @@ DEFINE_PROPERTY(stamina, I, Actor) DEFINE_PROPERTY(telefogsourcetype, S, Actor) { PROP_STRING_PARM(str, 0); - if (str == NULL || *str == 0 || (!stricmp(str,""))) defaults->TeleFogSourceType = "TeleportFog"; - else defaults->TeleFogSourceType = str; + if (!stricmp(str,"") || *str == 0) defaults->TeleFogSourceType = PClass::FindClass("TeleportFog"); + else defaults->TeleFogSourceType = FindClassTentative(str,"TeleportFog"); } //========================================================================== @@ -1432,8 +1432,8 @@ DEFINE_PROPERTY(telefogsourcetype, S, Actor) DEFINE_PROPERTY(telefogdesttype, S, Actor) { PROP_STRING_PARM(str, 0); - if (str == NULL || *str == 0 || (!stricmp(str, ""))) defaults->TeleFogDestType = "TeleportFog"; - else defaults->TeleFogDestType = str; + if (!stricmp(str, "") || *str == 0) defaults->TeleFogDestType = PClass::FindClass("TeleportFog"); + else defaults->TeleFogDestType = FindClassTentative(str, "TeleportFog"); } //========================================================================== diff --git a/src/version.h b/src/version.h index 15d01d315..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 4516 +#define SAVEVER 4518 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 753eae4b68b6b9662a228aef833a2cdd99eecbd6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 21:51:47 -0600 Subject: [PATCH 300/313] - Went one up too high on the save count. --- src/p_mobj.cpp | 2 +- src/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e1eed19c1..8c76d793e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -333,7 +333,7 @@ void AActor::Serialize (FArchive &arc) { arc << FriendPlayer; } - if (SaveVersion >= 4518) + if (SaveVersion >= 4517) { arc << TeleFogSourceType << TeleFogDestType; diff --git a/src/version.h b/src/version.h index 09b830438..6863f0573 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 4518 +#define SAVEVER 4517 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From a2bb673370e721664d5df801c4a35190f1c8332f Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 17 Dec 2014 21:58:30 -0600 Subject: [PATCH 301/313] - I really need to stop coding so late at night. --- wadsrc/static/actors/actor.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 326f14d20..f422bbc36 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -318,6 +318,7 @@ ACTOR Actor native //: Thinker action native A_TakeFromChildren(class itemtype, int amount = 0); action native A_TakeFromSiblings(class itemtype, int amount = 0); action native A_SetTeleFog(name oldpos, name newpos); + action native A_SwapTeleFog(); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From dcab57b236d79fd40229e6feb90c8bc9d360ad70 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 18 Dec 2014 09:19:39 -0600 Subject: [PATCH 302/313] - Allow NULL. - Instead of reverting the teleport fog back to defaults, if there wasn't a class or if the class failed to be found, set it to NULL. - P_SpawnTeleportFog will not spawn anything if it's NULL. - Added "" so it can be used to mean 'don't change anything' for A_SetTeleFog. --- src/p_teleport.cpp | 3 ++- src/thingdef/thingdef_codeptr.cpp | 14 ++++++++++++-- src/thingdef/thingdef_properties.cpp | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index a73ff75e7..762cd01fe 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -79,7 +79,8 @@ void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool befo AActor *mo; if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL) //If the actor doesn't have one, initialize the original. { - mo = Spawn(x, y, z, ALLOW_REPLACE); + //Do nothing. + mo = NULL; } else { diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index aa643de8c..9eda64a81 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5395,11 +5395,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) ACTION_PARAM_NAME(oldpos, 0); ACTION_PARAM_NAME(newpos, 1); const PClass *check = PClass::FindClass(oldpos); - if (check != NULL) + 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) + 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; } diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 0c4ec25cb..b6a2937ae 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1422,7 +1422,7 @@ DEFINE_PROPERTY(stamina, I, Actor) DEFINE_PROPERTY(telefogsourcetype, S, Actor) { PROP_STRING_PARM(str, 0); - if (!stricmp(str,"") || *str == 0) defaults->TeleFogSourceType = PClass::FindClass("TeleportFog"); + if (!stricmp(str, "") || (!stricmp(str, "none")) || (!stricmp(str, "null")) || *str == 0) defaults->TeleFogSourceType = NULL; else defaults->TeleFogSourceType = FindClassTentative(str,"TeleportFog"); } @@ -1432,7 +1432,7 @@ DEFINE_PROPERTY(telefogsourcetype, S, Actor) DEFINE_PROPERTY(telefogdesttype, S, Actor) { PROP_STRING_PARM(str, 0); - if (!stricmp(str, "") || *str == 0) defaults->TeleFogDestType = PClass::FindClass("TeleportFog"); + if (!stricmp(str, "") || (!stricmp(str, "none")) || (!stricmp(str, "null")) || *str == 0) defaults->TeleFogDestType = NULL; else defaults->TeleFogDestType = FindClassTentative(str, "TeleportFog"); } From ba346616e56c4e1df84f7cdceddf66f03114dca3 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 18 Dec 2014 09:24:35 -0600 Subject: [PATCH 303/313] - Fixed: ACS version of SetTeleFog and SwapTeleFog targeted the caller despite TID. --- src/p_acs.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 54a7110a1..a86212679 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4776,10 +4776,10 @@ static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName { check = PClass::FindClass(telefogsrc); if (check != NULL) - activator->TeleFogSourceType = check; + actor->TeleFogSourceType = check; check = PClass::FindClass(telefogdest); if (check != NULL) - activator->TeleFogDestType = check; + actor->TeleFogDestType = check; } } } @@ -4806,11 +4806,11 @@ static int SwapActorTeleFog(AActor *activator, int tid) while ((actor = iterator.Next())) { - if (activator->TeleFogSourceType == activator->TeleFogDestType) + if (actor->TeleFogSourceType == actor->TeleFogDestType) continue; //They're the same. Save the effort. - const PClass *temp = activator->TeleFogSourceType; - activator->TeleFogSourceType = activator->TeleFogDestType; - activator->TeleFogDestType = temp; + const PClass *temp = actor->TeleFogSourceType; + actor->TeleFogSourceType = actor->TeleFogDestType; + actor->TeleFogDestType = temp; count++; } } From 2747f9a9f37b3a452cd0e008a61b6698f038a80b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 18 Dec 2014 09:29:23 -0600 Subject: [PATCH 304/313] - Make ACS SetTeleFog follow the same format as the DECORATE version too. --- src/p_acs.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index a86212679..69f489627 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4760,10 +4760,15 @@ static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName if (activator != NULL) { check = PClass::FindClass(telefogsrc); - if (check != NULL) + if (check != NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) + activator->TeleFogSourceType = NULL; + else activator->TeleFogSourceType = check; + check = PClass::FindClass(telefogdest); - if (check != NULL) + if (check != NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) + activator->TeleFogDestType = NULL; + else activator->TeleFogDestType = check; } } @@ -4775,10 +4780,15 @@ static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName while ((actor = iterator.Next())) { check = PClass::FindClass(telefogsrc); - if (check != NULL) + if (check != NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) + actor->TeleFogSourceType = NULL; + else actor->TeleFogSourceType = check; + check = PClass::FindClass(telefogdest); - if (check != NULL) + if (check != NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) + actor->TeleFogDestType = NULL; + else actor->TeleFogDestType = check; } } From c6fd4c5aaf50788f80acf9035295db5e7cf17314 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 18 Dec 2014 09:46:19 -0600 Subject: [PATCH 305/313] - Added the two definitions to actor.txt. --- wadsrc/static/actors/actor.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index f422bbc36..cd718b1c0 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -26,6 +26,8 @@ ACTOR Actor native //: Thinker DesignatedTeam 255 PainType Normal DeathType Normal + TeleFogSourceType "TeleportFog" + TeleFogDestType "TeleportFog" // Variables for the expression evaluator // NOTE: fixed_t and angle_t are only used here to ensure proper conversion From a150e0686c37956b91d17f1f1e56a72c2416de29 Mon Sep 17 00:00:00 2001 From: Blue-Shadow Date: Thu, 18 Dec 2014 23:05:59 +0300 Subject: [PATCH 306/313] Added 'lifestealmax' parameter and STEALARMOR flag to A_Saw and A_CustomPunch. --- src/g_doom/a_doomweaps.cpp | 31 +++++++++++++++++++++-- src/thingdef/thingdef_codeptr.cpp | 31 +++++++++++++++++++++-- wadsrc/static/actors/constants.txt | 2 ++ wadsrc/static/actors/shared/inventory.txt | 4 +-- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 976ffbfc4..527b885bf 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -109,6 +109,7 @@ enum SAW_Flags SF_NOUSEAMMO = 16, SF_NOPULLIN = 32, SF_NOTURN = 64, + SF_STEALARMOR = 128, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) @@ -119,7 +120,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) AActor *linetarget; int actualdamage; - ACTION_PARAM_START(9); + ACTION_PARAM_START(11); ACTION_PARAM_SOUND(fullsound, 0); ACTION_PARAM_SOUND(hitsound, 1); ACTION_PARAM_INT(damage, 2); @@ -129,6 +130,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) ACTION_PARAM_ANGLE(Spread_XY, 6); ACTION_PARAM_ANGLE(Spread_Z, 7); ACTION_PARAM_FIXED(LifeSteal, 8); + ACTION_PARAM_INT(lifestealmax, 9); + ACTION_PARAM_CLASS(armorbonustype, 10); if (NULL == (player = self->player)) { @@ -184,7 +187,31 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) } if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); + { + if (flags & SF_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); + } + } S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 2a2da343e..31d059393 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1385,17 +1385,20 @@ enum CPF_PULLIN = 4, CPF_NORANDOMPUFFZ = 8, CPF_NOTURN = 16, + CPF_STEALARMOR = 32, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) { - ACTION_PARAM_START(5); + 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; @@ -1428,7 +1431,31 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) if (linetarget) { if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); + { + 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) { diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 8695c9c19..5669d96b1 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -16,6 +16,7 @@ const int SF_NOUSEAMMOMISS = 8; const int SF_NOUSEAMMO = 16; const int SF_NOPULLIN = 32; const int SF_NOTURN = 64; +const int SF_STEALARMOR = 128; // Flags for A_CustomMissile const int CMF_AIMOFFSET = 1; @@ -176,6 +177,7 @@ const int CPF_DAGGER = 2; const int CPF_PULLIN = 4; const int CPF_NORANDOMPUFFZ = 8; const int CPF_NOTURN = 16; +const int CPF_STEALARMOR = 32; // Flags for A_CustomMissile const int FPF_AIMATANGLE = 1; diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index e9f57a607..7860b9387 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -8,7 +8,7 @@ ACTOR Inventory native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG" action native A_JumpIfNoAmmo(state label); - action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0); + action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); action native A_FireBullets(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, float range = 0); action native A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, bool aimatangle = false, float pitch = 0); action native A_RailAttack(int damage, int spawnofs_xy = 0, int useammo = true, color color1 = "", color color2 = "", int flags = 0, 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); @@ -41,7 +41,7 @@ ACTOR Inventory native action native A_ClearReFire(); action native A_CheckReload(); action native A_GunFlash(state flash = "", int flags = 0); - action native A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0); + action native A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); action native A_CheckForReload(int counter, state label, bool dontincrement = false); action native A_ResetReloadCounter(); action native A_RestoreSpecialPosition(); From 80bc95d3bc3b15987074c912bfd9abed68301e94 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 19 Dec 2014 21:57:34 +1300 Subject: [PATCH 307/313] Added absent cl_capfps and cl_maxdecals entries --- wadsrc/static/menudef.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index abc654252..4e33029b4 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -665,6 +665,7 @@ OptionMenu "VideoOptions" Slider "Screen size", "screenblocks", 3.0, 12.0, 1.0, 0 Slider "Brightness", "Gamma", 0.75, 3.0, 0.05, 2 Option "Vertical Sync", "vid_vsync", "OnOff" + Option "Rendering Interpolation", "cl_capfps", "OffOn" Option "Column render mode", "r_columnmethod", "ColumnMethods" StaticText " " @@ -685,6 +686,7 @@ OptionMenu "VideoOptions" Option "Blood Type", "cl_bloodtype", "BloodTypes" Option "Bullet Puff Type", "cl_pufftype", "PuffTypes" Slider "Number of particles", "r_maxparticles", 100, 10000, 100, 0 + Slider "Number of decals", "cl_maxdecals", 0, 10000, 100, 0 Option "Show player sprites", "r_drawplayersprites", "OnOff" Option "Death camera", "r_deathcamera", "OnOff" Option "Teleporter zoom", "telezoom", "OnOff" From 7bc2e5c67e339228d1fd56011c7d24d7760b1035 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Dec 2014 06:21:37 -0600 Subject: [PATCH 308/313] - Tiny fix to ACS. --- src/p_acs.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 69f489627..e005c3c67 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4760,13 +4760,13 @@ static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName if (activator != NULL) { check = PClass::FindClass(telefogsrc); - if (check != NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) + if (check == NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) activator->TeleFogSourceType = NULL; else activator->TeleFogSourceType = check; check = PClass::FindClass(telefogdest); - if (check != NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) + if (check == NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) activator->TeleFogDestType = NULL; else activator->TeleFogDestType = check; @@ -4780,13 +4780,13 @@ static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName while ((actor = iterator.Next())) { check = PClass::FindClass(telefogsrc); - if (check != NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) + if (check == NULL || !stricmp(telefogsrc, "none") || !stricmp(telefogsrc, "null")) actor->TeleFogSourceType = NULL; else actor->TeleFogSourceType = check; check = PClass::FindClass(telefogdest); - if (check != NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) + if (check == NULL || !stricmp(telefogdest, "none") || !stricmp(telefogdest, "null")) actor->TeleFogDestType = NULL; else actor->TeleFogDestType = check; From 86b0065c0bf99b18a0bf95ca5c86b620ec025f0f Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Dec 2014 12:37:02 -0600 Subject: [PATCH 309/313] - Added a multitude of flags for A_Teleport. - TF_KEEPVELOCITY: Keep the velocity after teleporting. - TF_KEEPANGLE: Don't use the special spot's angle. - TF_USESPOTZ: Normally, this function - TF_NOSRCFOG: Don't leave fog at the previous location. - TF_NODESTFOG: Don't leave fog at the arriving location. - TF_USEACTORFOG: Use the actor's TeleFogSourceType and TeleFogDestType properties. - TF_NOJUMP: Don't require or cause a jump. In this case, put 0 (or "") in for the jump destination when using CustomInventory items to teleport actors around. --- src/p_teleport.cpp | 2 +- src/thingdef/thingdef_codeptr.cpp | 78 +++++++++++++++++++++--------- wadsrc/static/actors/constants.txt | 19 ++++++-- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 762cd01fe..40e432af0 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -77,7 +77,7 @@ void ATeleportFog::PostBeginPlay () void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool setTarget) { AActor *mo; - if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL) //If the actor doesn't have one, initialize the original. + if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL) { //Do nothing. mo = NULL; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 9eda64a81..f6fb603e2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4121,9 +4121,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) //=========================================================================== enum T_Flags { - TF_TELEFRAG = 1, // Allow telefrag in order to teleport. - TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) - TF_FORCED = 4, // Forget what's in the way. TF_Telefrag takes precedence though. + 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) @@ -4154,14 +4161,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) if (pr_teleport() >= chance[chanceindex]) return; } - if (TeleportState == NULL) - { - // Default to Teleport. - TeleportState = self->FindState("Teleport"); - // If still nothing, then return. - if (!TeleportState) return; - } - DSpotState *state = DSpotState::GetSpotState(); if (state == NULL) return; @@ -4173,34 +4172,67 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) fixed_t prevX = self->x; fixed_t prevY = self->y; fixed_t prevZ = self->z; - bool teleResult = false; //Take precedence and cooperate with telefragging first. - if (P_TeleportMove(self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) - teleResult = true; + 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; + } if (teleResult) { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (FogType) - { - Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + //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; - ACTION_JUMP(TeleportState); + if (!(Flags & TF_KEEPANGLE)) + self->angle = spot->angle; - self->z = self->floorz; - self->angle = spot->angle; - self->velx = self->vely = self->velz = 0; + if (!(Flags & TF_KEEPVELOCITY)) + self->velx = self->vely = self->velz = 0; + + if (!(Flags & TF_NOJUMP)) + { + if (TeleportState == NULL) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) return; + + ACTION_JUMP(TeleportState); + return; + } + } } + ACTION_SET_RESULT(teleResult); } //=========================================================================== diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 8695c9c19..c722e0062 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -182,9 +182,22 @@ const int FPF_AIMATANGLE = 1; const int FPF_TRANSFERTRANSLATION = 2; // Flags for A_Teleport -const int TF_TELEFRAG = 1; -const int TF_RANDOMDECIDE = 2; -const int TF_FORCED = 4; +enum +{ + 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. + + TF_KEEPORIENTATION = TF_KEEPVELOCITY|TF_KEEPANGLE, + TF_NOFOG = TF_NOSRCFOG|TF_NODESTFOG, +}; // Flags for A_WolfAttack const int WAF_NORANDOM = 1; From 31ad6428436216b20bd04bb7810dc261ec287552 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 19 Dec 2014 13:43:24 -0600 Subject: [PATCH 310/313] - HITTARGET, HITMASTER and HITTRACER now apply to actors that bounce off of other actors. --- src/p_map.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_map.cpp b/src/p_map.cpp index 48da5c72c..496abbf0d 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3003,6 +3003,10 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) { if (mo->bouncecount > 0 && --mo->bouncecount == 0) return false; + if (mo->flags7 & MF7_HITTARGET) mo->target = BlockingMobj; + if (mo->flags7 & MF7_HITMASTER) mo->master = BlockingMobj; + if (mo->flags7 & MF7_HITTRACER) mo->tracer = BlockingMobj; + if (!ontop) { fixed_t speed; From 08cd03dbb7e69820492fde35584bdf54ebc7a5c5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 20 Dec 2014 11:49:19 +0200 Subject: [PATCH 311/313] Fixed hanging on startup in native OS X backend when Xbox 360 gamepad is connected Apple's HID Utilities has a issue with device elements enumeration while using unofficial drivers from here: http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver --- src/cocoa/i_joystick.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp index 56db8f815..9940c6ca8 100644 --- a/src/cocoa/i_joystick.cpp +++ b/src/cocoa/i_joystick.cpp @@ -152,23 +152,33 @@ IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device ) : m_device( device ) , m_sensitivity( DEFAULT_SENSITIVITY ) { - IOHIDElementRef element = HIDGetFirstDeviceElement( device, kHIDElementTypeInput ); - - while ( NULL != element ) + assert(NULL != device); + assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device)); + + CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + assert(NULL != elements); + assert(CFArrayGetTypeID() == CFGetTypeID(elements)); + + for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) { + const IOHIDElementRef element = + static_cast(const_cast(CFArrayGetValueAtIndex(elements, i))); + assert(NULL != element); + assert(IOHIDElementGetTypeID() == CFGetTypeID(element)); + const uint32_t usagePage = IOHIDElementGetUsagePage( element ); - + if ( kHIDPage_GenericDesktop == usagePage ) { const uint32_t usage = IOHIDElementGetUsage( element ); - - if ( kHIDUsage_GD_Slider == usage + + 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 ) { AxisInfo axis; memset( &axis, 0, sizeof( axis ) ); - + if ( const CFStringRef name = IOHIDElementGetName( element ) ) { CFStringGetCString( name, axis.name, sizeof( axis.name ) - 1, kCFStringEncodingUTF8 ); @@ -177,22 +187,22 @@ IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device ) { 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 ); - } + } } else if ( kHIDPage_Button == usagePage ) { @@ -200,10 +210,10 @@ IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device ) HIDQueueElement( m_device, element ); } - - element = HIDGetNextDeviceElement( element, kHIDElementTypeInput ); } + CFRelease(elements); + SetDefaultConfig(); } From 2a53ebb6b9329c57080a7173983e5008d2e14c2f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 20 Dec 2014 14:48:22 +0100 Subject: [PATCH 312/313] - fixed state checking in A_Teleport. --- src/thingdef/thingdef_codeptr.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 44e993f34..98451795b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4246,17 +4246,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) if (!(Flags & TF_NOJUMP)) { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! if (TeleportState == NULL) { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! // Default to Teleport. TeleportState = self->FindState("Teleport"); // If still nothing, then return. if (!TeleportState) return; - - ACTION_JUMP(TeleportState); - return; } + ACTION_JUMP(TeleportState); + return; } } ACTION_SET_RESULT(teleResult); From 9446edc0689342d4a411161a5b941065fe5393fe Mon Sep 17 00:00:00 2001 From: Blue-Shadow Date: Sun, 21 Dec 2014 01:10:54 +0300 Subject: [PATCH 313/313] Fixed a case of an undeclared variable with A_Saw. --- src/g_doom/a_doomweaps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 527b885bf..898bf7c5b 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -188,7 +188,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) { - if (flags & SF_STEALARMOR) + if (Flags & SF_STEALARMOR) { if (!armorbonustype) armorbonustype = PClass::FindClass("ArmorBonus");