mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-25 05:21:16 +00:00
Merge remote-tracking branch 'zdoom/master' into openal
Conflicts: output_sdl/CMakeLists.txt src/namedef.h
This commit is contained in:
commit
a8348b13de
192 changed files with 19866 additions and 10872 deletions
180
FindSDL2.cmake
Normal file
180
FindSDL2.cmake
Normal file
|
@ -0,0 +1,180 @@
|
|||
# Locate SDL2 library
|
||||
# This module defines
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2_main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARY variable.
|
||||
#
|
||||
# Don't forget to include SDL2main.h and SDL2main.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
# module will automatically add the -framework Cocoa on your behalf.
|
||||
#
|
||||
#
|
||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
||||
#
|
||||
#
|
||||
# $SDL2DIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$SDL2DIR
|
||||
# used in building SDL2.
|
||||
# l.e.galup 9-20-02
|
||||
#
|
||||
# Modified by Eric Wing.
|
||||
# Added code to assist with automated building by using environmental variables
|
||||
# and providing a more controlled/consistent search behavior.
|
||||
# Added new modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
# Also corrected the header search path to follow "proper" SDL2 guidelines.
|
||||
# Added a search for SDL2main which is needed by some platforms.
|
||||
# Added a search for threads which is needed by some platforms.
|
||||
# Added needed compile switches for MinGW.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
#
|
||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
||||
# This needed to change because "proper" SDL2 convention
|
||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
||||
#
|
||||
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
|
||||
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
|
||||
# was not created for redistribution, and exists temporarily pending official
|
||||
# SDL2 CMake modules.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2003-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES include/SDL2 include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include/SDL2
|
||||
/usr/include/SDL2
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
)
|
||||
#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}")
|
||||
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
||||
NAMES SDL2
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
|
||||
#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}")
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
# frameworks may already provide it.
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
IF(NOT APPLE)
|
||||
FIND_PACKAGE(Threads)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# MinGW needs an additional library, mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
|
||||
# (Actually on second look, I think it only needs one of the m* libraries.)
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
SET(SDL2_FOUND "NO")
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
# I think it has something to do with the CACHE STRING.
|
||||
# So I use a temporary variable until the end so I can set the
|
||||
# "real" variable in one-shot.
|
||||
IF(APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
||||
ENDIF(APPLE)
|
||||
|
||||
# For threads, as mentioned Apple doesn't need this.
|
||||
# In fact, there seems to be a problem if I used the Threads package
|
||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
||||
IF(NOT APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# For MinGW library
|
||||
IF(MINGW)
|
||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(MINGW)
|
||||
|
||||
# Set the final string here so the GUI reflects the final state.
|
||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
||||
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
|
||||
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
|
||||
|
||||
SET(SDL2_FOUND "YES")
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2
|
||||
REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
|
@ -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 )
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
cmake_minimum_required( VERSION 2.4 )
|
||||
if( NOT NO_FMOD AND FMOD_INCLUDE_DIR )
|
||||
add_library( output_sdl MODULE output_sdl.c )
|
||||
include_directories( ${FMOD_INCLUDE_DIR} ${SDL_INCLUDE_DIR} )
|
||||
include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} )
|
||||
target_link_libraries( output_sdl ${SDL2_LIBRARY} )
|
||||
|
||||
FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" )
|
||||
add_custom_command( TARGET output_sdl POST_BUILD
|
||||
|
|
|
@ -92,30 +92,33 @@ Note: All <bool> fields default to false unless mentioned otherwise.
|
|||
|
||||
linedef
|
||||
{
|
||||
alpha = <float>; // Translucency of this line, default is 1.0
|
||||
renderstyle = <string>; // Render style, can be "translucent" or "add",
|
||||
// default is "translucent".
|
||||
playeruseback = <bool>; // New SPAC flag, true = player can use from back side.
|
||||
anycross = <bool>; // New SPAC flag, true = any non-projectile
|
||||
// crossing will trigger this line
|
||||
monsteractivate = <bool>; // Monsters can trigger this line.
|
||||
// For compatibility only because this flag's
|
||||
// semantics can not be fully reproduced with
|
||||
// explicit trigger flags.
|
||||
blockplayers = <bool>; // Line blocks players' movement.
|
||||
blockeverything = <bool>; // Line blocks everything.
|
||||
firstsideonly = <bool>; // Line can only be triggered from the front side.
|
||||
zoneboundary = <bool>; // Line is a boundary for sound reverb zones.
|
||||
clipmidtex = <bool>; // Line's mid textures are clipped to floor and ceiling.
|
||||
wrapmidtex = <bool>; // Line's mid textures are wrapped.
|
||||
midtex3d = <bool>; // Actors can walk on mid texture.
|
||||
checkswitchrange = <bool>;// Switches can only be activated when vertically reachable.
|
||||
blockprojectiles = <bool>;// Line blocks all projectiles
|
||||
blockuse = <bool>; // Line blocks all use actions
|
||||
blocksight = <bool>; // Line blocks monster line of sight
|
||||
blockhitscan = <bool>; // Line blocks hitscan attacks
|
||||
locknumber = <int>; // Line special is locked
|
||||
arg0str = <string>; // Alternate string-based version of arg0
|
||||
alpha = <float>; // Translucency of this line, default is 1.0
|
||||
renderstyle = <string>; // Render style, can be "translucent" or "add",
|
||||
// default is "translucent".
|
||||
playeruseback = <bool> ; // New SPAC flag, true = player can use from back side.
|
||||
anycross = <bool>; // New SPAC flag, true = any non-projectile
|
||||
// crossing will trigger this line
|
||||
monsteractivate = <bool>; // Monsters can trigger this line.
|
||||
// For compatibility only because this flag's
|
||||
// semantics can not be fully reproduced with
|
||||
// explicit trigger flags.
|
||||
blockplayers = <bool>; // Line blocks players' movement.
|
||||
blockeverything = <bool>; // Line blocks everything.
|
||||
firstsideonly = <bool>; // Line can only be triggered from the front side.
|
||||
zoneboundary = <bool>; // Line is a boundary for sound reverb zones.
|
||||
clipmidtex = <bool>; // Line's mid textures are clipped to floor and ceiling.
|
||||
wrapmidtex = <bool>; // Line's mid textures are wrapped.
|
||||
midtex3d = <bool>; // Actors can walk on mid texture.
|
||||
midtex3dimpassible = <bool>;// Used in conjuction with midtex3d - causes the mid
|
||||
// texture to behave like an impassible line (projectiles
|
||||
// pass through it).
|
||||
checkswitchrange = <bool>; // Switches can only be activated when vertically reachable.
|
||||
blockprojectiles = <bool>; // Line blocks all projectiles
|
||||
blockuse = <bool>; // Line blocks all use actions
|
||||
blocksight = <bool>; // Line blocks monster line of sight
|
||||
blockhitscan = <bool>; // Line blocks hitscan attacks
|
||||
locknumber = <int>; // Line special is locked
|
||||
arg0str = <string>; // Alternate string-based version of arg0
|
||||
|
||||
transparent = <bool>; // true = line is a Strife transparent line (alpha 0.25)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ endif( COMMAND cmake_policy )
|
|||
include( CheckCXXSourceCompiles )
|
||||
include( CheckFunctionExists )
|
||||
include( CheckCXXCompilerFlag )
|
||||
include( CheckLibraryExists )
|
||||
include( FindPkgConfig )
|
||||
|
||||
if( NOT APPLE )
|
||||
|
@ -26,6 +27,10 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
|||
|
||||
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" 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 )
|
||||
endif( CMAKE_SIZEOF_VOID_P MATCHES "8" )
|
||||
|
@ -186,19 +191,6 @@ else( WIN32 )
|
|||
set( NO_GTK ON )
|
||||
endif( GTK2_FOUND )
|
||||
endif( NOT NO_GTK )
|
||||
|
||||
# Check for Xcursor library and header files
|
||||
find_library( XCURSOR_LIB Xcursor )
|
||||
if( XCURSOR_LIB )
|
||||
find_file( XCURSOR_HEADER "X11/Xcursor/Xcursor.h" )
|
||||
if( XCURSOR_HEADER )
|
||||
add_definitions( -DUSE_XCURSOR=1 )
|
||||
message( STATUS "Found Xcursor at ${XCURSOR_LIB}" )
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${XCURSOR_LIB} )
|
||||
else( XCURSOR_HEADER )
|
||||
unset( XCURSOR_LIB )
|
||||
endif( XCURSOR_HEADER )
|
||||
endif( XCURSOR_LIB )
|
||||
endif( APPLE )
|
||||
set( NASM_NAMES nasm )
|
||||
|
||||
|
@ -206,13 +198,12 @@ else( WIN32 )
|
|||
add_definitions( -DNO_GTK=1 )
|
||||
endif( NO_GTK )
|
||||
|
||||
# Non-Windows version also needs SDL
|
||||
find_package( SDL )
|
||||
if( NOT SDL_FOUND )
|
||||
message( SEND_ERROR "SDL is required for building." )
|
||||
endif( NOT SDL_FOUND )
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" )
|
||||
include_directories( "${SDL_INCLUDE_DIR}" )
|
||||
# Non-Windows version also needs SDL except native OS X backend
|
||||
if( NOT APPLE OR NOT OSX_COCOA_BACKEND )
|
||||
find_package( SDL2 REQUIRED )
|
||||
include_directories( "${SDL2_INCLUDE_DIR}" )
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL2_LIBRARY}" )
|
||||
endif( NOT APPLE OR NOT OSX_COCOA_BACKEND )
|
||||
|
||||
find_path( FPU_CONTROL_DIR fpu_control.h )
|
||||
if( FPU_CONTROL_DIR )
|
||||
|
@ -384,19 +375,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}"
|
||||
|
@ -455,9 +446,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}" )
|
||||
|
@ -591,25 +583,36 @@ set( PLAT_WIN32_SOURCES
|
|||
win32/i_system.cpp
|
||||
win32/st_start.cpp
|
||||
win32/win32video.cpp )
|
||||
set( PLAT_POSIX_SOURCES
|
||||
posix/i_cd.cpp
|
||||
posix/i_movie.cpp
|
||||
posix/i_steam.cpp
|
||||
posix/i_system.cpp
|
||||
posix/st_start.cpp )
|
||||
set( PLAT_SDL_SOURCES
|
||||
sdl/crashcatcher.c
|
||||
sdl/hardware.cpp
|
||||
sdl/i_cd.cpp
|
||||
sdl/i_input.cpp
|
||||
sdl/i_joystick.cpp
|
||||
sdl/i_main.cpp
|
||||
sdl/i_movie.cpp
|
||||
sdl/i_system.cpp
|
||||
sdl/sdlvideo.cpp
|
||||
sdl/st_start.cpp )
|
||||
set( PLAT_MAC_SOURCES
|
||||
sdl/SDLMain.m
|
||||
sdl/iwadpicker_cocoa.mm
|
||||
sdl/i_system_cocoa.mm )
|
||||
posix/sdl/crashcatcher.c
|
||||
posix/sdl/hardware.cpp
|
||||
posix/sdl/i_gui.cpp
|
||||
posix/sdl/i_input.cpp
|
||||
posix/sdl/i_joystick.cpp
|
||||
posix/sdl/i_main.cpp
|
||||
posix/sdl/i_timer.cpp
|
||||
posix/sdl/sdlvideo.cpp )
|
||||
set( PLAT_OSX_SOURCES
|
||||
posix/osx/iwadpicker_cocoa.mm
|
||||
posix/osx/zdoom.icns )
|
||||
set( PLAT_COCOA_SOURCES
|
||||
posix/cocoa/critsec.cpp
|
||||
posix/cocoa/i_input.mm
|
||||
posix/cocoa/i_joystick.cpp
|
||||
posix/cocoa/i_main.mm
|
||||
posix/cocoa/i_timer.cpp
|
||||
posix/cocoa/i_video.mm )
|
||||
|
||||
if( WIN32 )
|
||||
set( SYSTEM_SOURCES_DIR win32 )
|
||||
set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ${PLAT_MAC_SOURCES} )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} )
|
||||
|
||||
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||
# CMake is not set up to compile and link rc files with GCC. :(
|
||||
|
@ -620,15 +623,26 @@ if( WIN32 )
|
|||
else( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc )
|
||||
endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||
elseif( APPLE )
|
||||
if( OSX_COCOA_BACKEND )
|
||||
set( SYSTEM_SOURCES_DIR posix posix/cocoa )
|
||||
set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} )
|
||||
else( OSX_COCOA_BACKEND )
|
||||
set( SYSTEM_SOURCES_DIR posix posix/sdl )
|
||||
set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} )
|
||||
set( PLAT_OSX_SOURCES ${PLAT_OSX_SOURCES} posix/sdl/i_system.mm )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} )
|
||||
endif( OSX_COCOA_BACKEND )
|
||||
|
||||
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" )
|
||||
|
||||
set_source_files_properties( posix/osx/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources )
|
||||
set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks )
|
||||
else( WIN32 )
|
||||
set( SYSTEM_SOURCES_DIR sdl )
|
||||
set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} )
|
||||
if( APPLE )
|
||||
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_MAC_SOURCES} )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} )
|
||||
else( APPLE )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_MAC_SOURCES} )
|
||||
endif( APPLE )
|
||||
set( SYSTEM_SOURCES_DIR posix posix/sdl )
|
||||
set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} )
|
||||
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} )
|
||||
endif( WIN32 )
|
||||
|
||||
if( NOT ASM_SOURCES )
|
||||
|
@ -692,8 +706,14 @@ endif( DYN_FLUIDSYNTH )
|
|||
# there's generally a new cpp for every header so this file will get changed
|
||||
if( WIN32 )
|
||||
set( EXTRA_HEADER_DIRS win32/*.h )
|
||||
elseif( APPLE )
|
||||
if( OSX_COCOA_BACKEND )
|
||||
set( EXTRA_HEADER_DIRS posix/*.h posix/cocoa/*.h )
|
||||
else( OSX_COCOA_BACKEND )
|
||||
set( EXTRA_HEADER_DIRS posix/*.h posix/sdl/*.h )
|
||||
endif( OSX_COCOA_BACKEND )
|
||||
else( WIN32 )
|
||||
set( EXTRA_HEADER_DIRS sdl/*.h )
|
||||
set( EXTRA_HEADER_DIRS posix/*.h posix/sdl/*.h )
|
||||
endif( WIN32 )
|
||||
file( GLOB HEADER_FILES
|
||||
${EXTRA_HEADER_DIRS}
|
||||
|
@ -708,9 +728,11 @@ file( GLOB HEADER_FILES
|
|||
menu/*.h
|
||||
oplsynth/*.h
|
||||
oplsynth/dosbox/*.h
|
||||
posix/*.h
|
||||
posix/cocoa/*.h
|
||||
posix/sdl/*.h
|
||||
r_data/*.h
|
||||
resourcefiles/*.h
|
||||
sdl/*.h
|
||||
sfmt/*.h
|
||||
sound/*.h
|
||||
textures/*.h
|
||||
|
@ -813,7 +835,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
|
||||
|
@ -1033,6 +1055,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
|
||||
|
@ -1208,6 +1231,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 Carbon -framework Cocoa -framework IOKit -framework OpenGL"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" )
|
||||
|
||||
# Fix fmod link so that it can be found in the app bundle.
|
||||
find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" )
|
||||
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 "$<TARGET_FILE:zdoom>"
|
||||
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/.+")
|
||||
|
@ -1231,7 +1273,10 @@ source_group("Render Data\\Resource Sources" REGULAR_EXPRESSION "^${CMAKE_CURREN
|
|||
source_group("Render Data\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/textures/.+")
|
||||
source_group("Render Interface" FILES r_defs.h r_renderer.h r_sky.cpp r_sky.h r_state.h r_utility.cpp r_utility.h)
|
||||
source_group("Resource Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/resourcefiles/.+")
|
||||
source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sdl/.+")
|
||||
source_group("POSIX Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/.+")
|
||||
source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/cocoa/.+")
|
||||
source_group("OS X Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/.+")
|
||||
source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/sdl/.+")
|
||||
source_group("SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+")
|
||||
source_group("Shared Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_shared/.+")
|
||||
source_group("Versioning" FILES version.h win32/zdoom.rc)
|
||||
|
|
29
src/actor.h
29
src/actor.h
|
@ -341,6 +341,20 @@ 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.
|
||||
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.
|
||||
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.
|
||||
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 ---
|
||||
|
||||
|
@ -713,6 +727,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);
|
||||
|
||||
|
@ -768,6 +785,7 @@ public:
|
|||
// These also set CF_INTERPVIEW for players.
|
||||
void SetPitch(int p, bool interpolate);
|
||||
void SetAngle(angle_t ang, bool interpolate);
|
||||
void SetRoll(angle_t roll, bool interpolate);
|
||||
|
||||
const PClass *GetBloodType(int type = 0) const
|
||||
{
|
||||
|
@ -850,7 +868,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;
|
||||
|
@ -937,9 +955,6 @@ public:
|
|||
TObjPtr<AInventory> 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)
|
||||
|
@ -965,9 +980,15 @@ public:
|
|||
FNameNoInit DamageType;
|
||||
FNameNoInit DamageTypeReceived;
|
||||
fixed_t DamageFactor;
|
||||
fixed_t DamageMultiply;
|
||||
|
||||
FNameNoInit PainType;
|
||||
FNameNoInit DeathType;
|
||||
const PClass *TeleFogSourceType;
|
||||
const PClass *TeleFogDestType;
|
||||
int RipperLevel;
|
||||
int RipLevelMin;
|
||||
int RipLevelMax;
|
||||
|
||||
FState *SpawnState;
|
||||
FState *SeeState;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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];
|
||||
|
|
117
src/b_bot.cpp
117
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,102 @@
|
|||
#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 ()
|
||||
: DThinker(STAT_BOT)
|
||||
{
|
||||
Clear ();
|
||||
}
|
||||
|
||||
void DBot::Clear ()
|
||||
{
|
||||
player = NULL;
|
||||
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);
|
||||
|
||||
if (SaveVersion < 4515)
|
||||
{
|
||||
angle_t savedyaw;
|
||||
int savedpitch;
|
||||
arc << savedyaw
|
||||
<< savedpitch;
|
||||
}
|
||||
else
|
||||
{
|
||||
arc << player;
|
||||
}
|
||||
|
||||
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
|
||||
<< increase
|
||||
<< oldx
|
||||
<< oldy;
|
||||
}
|
||||
|
||||
void DBot::Tick ()
|
||||
{
|
||||
Super::Tick ();
|
||||
|
||||
if (player->mo == NULL || bglobal.freeze)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BotThinkCycles.Clock();
|
||||
bglobal.m_Thinking = true;
|
||||
Think ();
|
||||
bglobal.m_Thinking = false;
|
||||
BotThinkCycles.Unclock();
|
||||
}
|
||||
|
||||
CVAR (Int, bot_next_color, 11, 0)
|
||||
CVAR (Bool, bot_observer, false, 0)
|
||||
|
||||
|
@ -55,9 +151,14 @@ 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)
|
||||
{
|
||||
players[i].Bot->Destroy ();
|
||||
players[i].Bot = NULL;
|
||||
}
|
||||
players[i].~player_t();
|
||||
::new(&players[i]) player_t;
|
||||
players[i].userinfo.Reset();
|
||||
|
@ -66,6 +167,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);
|
||||
}
|
||||
|
||||
|
@ -91,7 +198,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++;
|
||||
}
|
||||
|
|
136
src/b_bot.h
136
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
|
||||
|
@ -60,6 +61,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 +77,7 @@ struct botinfo_t
|
|||
char *name;
|
||||
char *info;
|
||||
botskill_t skill;
|
||||
bool inuse;
|
||||
int inuse;
|
||||
int lastteam;
|
||||
};
|
||||
|
||||
|
@ -81,35 +89,29 @@ public:
|
|||
|
||||
void ClearPlayer (int playernum, bool keepTeam);
|
||||
|
||||
//(B_Game.c)
|
||||
void Main (int buf);
|
||||
//(b_game.cpp)
|
||||
void Main ();
|
||||
void Init ();
|
||||
void End();
|
||||
void CleanBotstuff (player_t *p);
|
||||
bool SpawnBot (const char *name, int color = NOCOLOR);
|
||||
void TryAddBot (BYTE **stream, int player);
|
||||
void RemoveAllBots (bool fromlist);
|
||||
bool LoadBots ();
|
||||
void ForgetBots ();
|
||||
void DoAddBot (int bnum, char *info);
|
||||
void RemoveAllBots (bool fromlist);
|
||||
|
||||
//(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 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<FString> 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;
|
||||
|
@ -123,31 +125,93 @@ public:
|
|||
bool m_Thinking;
|
||||
|
||||
private:
|
||||
//(B_Func.c)
|
||||
bool Reachable (AActor *actor, AActor *target);
|
||||
void Dofire (AActor *actor, ticcmd_t *cmd);
|
||||
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 Think (AActor *actor, ticcmd_t *cmd);
|
||||
void ThinkForMove (AActor *actor, ticcmd_t *cmd);
|
||||
void Set_enemy (AActor *actor);
|
||||
//(b_game.cpp)
|
||||
bool DoAddBot (BYTE *info, botskill_t skill);
|
||||
|
||||
protected:
|
||||
bool ctf;
|
||||
int loaded_bots;
|
||||
int t_join;
|
||||
bool observer; //Consoleplayer is observer.
|
||||
};
|
||||
|
||||
class DBot : public DThinker
|
||||
{
|
||||
DECLARE_CLASS(DBot,DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
DBot ();
|
||||
|
||||
void Clear ();
|
||||
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<AActor> dest; // Move Destination.
|
||||
TObjPtr<AActor> prev; // Previous move destination.
|
||||
TObjPtr<AActor> enemy; // The dead meat.
|
||||
TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
|
||||
TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
|
||||
TObjPtr<AActor> 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 first_shot; // Used for reaction skill.
|
||||
bool sleft; // If false, strafe is right.
|
||||
bool allround;
|
||||
bool increase;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
//Externs
|
||||
extern FCajunMaster bglobal;
|
||||
extern cycle_t BotThinkCycles, BotSupportCycles;
|
||||
|
||||
EXTERN_CVAR (Float, bot_flag_return_time)
|
||||
EXTERN_CVAR (Int, bot_next_color)
|
||||
|
@ -158,7 +222,3 @@ EXTERN_CVAR (Bool, bot_watersplash)
|
|||
EXTERN_CVAR (Bool, bot_chat)
|
||||
|
||||
#endif // __B_BOT_H__
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
255
src/b_func.cpp
255
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,50 +139,48 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
|
|||
fixed_t dist;
|
||||
angle_t an;
|
||||
int m;
|
||||
static bool inc[MAXPLAYERS];
|
||||
AActor *enemy = actor->player->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->skill.isp)
|
||||
if (player->damagecount > skill.isp)
|
||||
{
|
||||
actor->player->first_shot = true;
|
||||
first_shot = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//Reaction skill thing.
|
||||
if (actor->player->first_shot &&
|
||||
!(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
|
||||
if (first_shot &&
|
||||
!(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
|
||||
{
|
||||
actor->player->t_react = (100-actor->player->skill.reaction+1)/((pr_botdofire()%3)+3);
|
||||
t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3);
|
||||
}
|
||||
actor->player->first_shot = false;
|
||||
if (actor->player->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.
|
||||
|
@ -196,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->skill.reaction)
|
||||
if(Check_LOS(actor, actor->player->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->angle = an;
|
||||
angle = an;
|
||||
//have to be somewhat precise. to avoid suicide.
|
||||
if (abs (actor->player->angle - actor->angle) < 12*ANGLE_1)
|
||||
if (abs (angle - player->mo->angle) < 12*ANGLE_1)
|
||||
{
|
||||
actor->player->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->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->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->lightlevel<WHATS_DARK/* && !(actor->player->powers & PW_INFRARED)*/)
|
||||
if (enemy->Sector->lightlevel<WHATS_DARK/* && !(player->powers & 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->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
|
||||
|
@ -249,18 +246,18 @@ shootmissile:
|
|||
|
||||
if (m)
|
||||
{
|
||||
if (inc[actor->id])
|
||||
actor->player->angle += m;
|
||||
if (increase)
|
||||
angle += m;
|
||||
else
|
||||
actor->player->angle -= m;
|
||||
angle -= m;
|
||||
}
|
||||
|
||||
if (abs (actor->player->angle - actor->angle) < 4*ANGLE_1)
|
||||
if (abs (angle - player->mo->angle) < 4*ANGLE_1)
|
||||
{
|
||||
inc[actor->id] = !inc[actor->id];
|
||||
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
|
||||
|
@ -268,53 +265,48 @@ 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)
|
||||
{
|
||||
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
|
||||
//the mate (teammate coop mate).
|
||||
AActor *FCajunMaster::Choose_Mate (AActor *bot)
|
||||
AActor *DBot::Choose_Mate ()
|
||||
{
|
||||
int count;
|
||||
int count2;
|
||||
fixed_t closest_dist, test;
|
||||
AActor *target;
|
||||
AActor *observer;
|
||||
bool p_leader[MAXPLAYERS];
|
||||
|
||||
//is mate alive?
|
||||
if (bot->player->mate)
|
||||
if (mate)
|
||||
{
|
||||
if (bot->player->mate->health <= 0)
|
||||
bot->player->mate = NULL;
|
||||
if (mate->health <= 0)
|
||||
mate = NULL;
|
||||
else
|
||||
bot->player->last_mate = bot->player->mate;
|
||||
last_mate = mate;
|
||||
}
|
||||
if (bot->player->mate) //Still is..
|
||||
return bot->player->mate;
|
||||
if (mate) //Still is..
|
||||
return mate;
|
||||
|
||||
//Check old_mates status.
|
||||
if (bot->player->last_mate)
|
||||
if (bot->player->last_mate->health <= 0)
|
||||
bot->player->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[count].isbot
|
||||
&& players[count2].mate == players[count].mo)
|
||||
{
|
||||
p_leader[count] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_mate)
|
||||
if (last_mate->health <= 0)
|
||||
last_mate = NULL;
|
||||
|
||||
target = NULL;
|
||||
closest_dist = FIXED_MAX;
|
||||
|
@ -330,18 +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)
|
||||
&& !p_leader[count]) //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)
|
||||
{
|
||||
|
@ -354,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]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -372,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.
|
||||
|
@ -382,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->allround || bot->player->mate)
|
||||
if (allround || mate)
|
||||
vangle = ANGLE_MAX;
|
||||
else
|
||||
vangle = ENEMY_SCAN_FOV;
|
||||
bot->player->allround = false;
|
||||
allround = false;
|
||||
|
||||
target = NULL;
|
||||
closest_dist = FIXED_MAX;
|
||||
|
@ -403,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)
|
||||
|
@ -501,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;
|
||||
|
||||
|
@ -520,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;
|
||||
|
@ -539,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;
|
||||
}
|
||||
}
|
||||
|
@ -559,3 +550,25 @@ bool FCajunMaster::SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FChec
|
|||
actor->flags = savedFlags;
|
||||
return res;
|
||||
}
|
||||
|
||||
void FCajunMaster::StartTravel ()
|
||||
{
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (players[i].Bot != NULL)
|
||||
{
|
||||
players[i].Bot->ChangeStatNum (STAT_TRAVELLING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FCajunMaster::FinishTravel ()
|
||||
{
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (players[i].Bot != NULL)
|
||||
{
|
||||
players[i].Bot->ChangeStatNum (STAT_BOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
292
src/b_game.cpp
292
src/b_game.cpp
|
@ -89,49 +89,27 @@ enum
|
|||
BOTCFG_TEAM
|
||||
};
|
||||
|
||||
static bool waitingforspawn[MAXPLAYERS];
|
||||
|
||||
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 (consoleplayer != Net_Arbitrator || demoplayback)
|
||||
if (demoplayback || gamestate != GS_LEVEL || consoleplayer != Net_Arbitrator)
|
||||
return;
|
||||
|
||||
if (gamestate != GS_LEVEL)
|
||||
return;
|
||||
|
||||
m_Thinking = true;
|
||||
|
||||
//Think for bots.
|
||||
if (botnum)
|
||||
{
|
||||
BotThinkCycles.Clock();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && players[i].mo && !freeze && players[i].isbot)
|
||||
Think (players[i].mo, &netcmds[i][buf]);
|
||||
}
|
||||
BotThinkCycles.Unclock();
|
||||
}
|
||||
|
||||
//Add new bots?
|
||||
if (wanted_botnum > botnum && !freeze)
|
||||
{
|
||||
if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY))
|
||||
{
|
||||
if (!SpawnBot (getspawned[spawn_tries]))
|
||||
if (!SpawnBot (getspawned[spawn_tries]))
|
||||
wanted_botnum--;
|
||||
spawn_tries++;
|
||||
spawn_tries++;
|
||||
}
|
||||
|
||||
t_join--;
|
||||
|
@ -156,14 +134,10 @@ void FCajunMaster::Main (int buf)
|
|||
players[consoleplayer].mo->flags2 &= ~MF2_FLY;
|
||||
players[consoleplayer].mo->LinkToWorld ();
|
||||
}
|
||||
|
||||
m_Thinking = false;
|
||||
}
|
||||
|
||||
void FCajunMaster::Init ()
|
||||
{
|
||||
int i;
|
||||
|
||||
botnum = 0;
|
||||
firstthing = NULL;
|
||||
spawn_tries = 0;
|
||||
|
@ -172,18 +146,6 @@ 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].isbot)
|
||||
{
|
||||
CleanBotstuff (&players[i]);
|
||||
players[i].isbot = false;
|
||||
botingame[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctf && teamplay == false)
|
||||
teamplay = true; //Need teamplay for ctf. (which is not done yet)
|
||||
|
||||
|
@ -199,7 +161,7 @@ void FCajunMaster::Init ()
|
|||
|
||||
while (thebot != NULL)
|
||||
{
|
||||
thebot->inuse = false;
|
||||
thebot->inuse = BOTINUSE_No;
|
||||
thebot = thebot->next;
|
||||
}
|
||||
}
|
||||
|
@ -212,19 +174,16 @@ void FCajunMaster::End ()
|
|||
|
||||
//Arrange wanted botnum and their names, so they can be spawned next level.
|
||||
getspawned.Clear();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (deathmatch)
|
||||
{
|
||||
if (playeringame[i] && players[i].isbot)
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (deathmatch)
|
||||
if (players[i].Bot != NULL)
|
||||
{
|
||||
getspawned.Push(players[i].userinfo.GetName());
|
||||
}
|
||||
CleanBotstuff (&players[i]);
|
||||
}
|
||||
}
|
||||
if (deathmatch)
|
||||
{
|
||||
|
||||
wanted_botnum = botnum;
|
||||
}
|
||||
}
|
||||
|
@ -240,12 +199,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] =
|
||||
{
|
||||
|
@ -262,64 +219,69 @@ 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;
|
||||
botinfo_t *thebot = botinfo;
|
||||
int botshift = 0;
|
||||
|
||||
if (name)
|
||||
{
|
||||
thebot = botinfo;
|
||||
|
||||
// Check if exist or already in the game.
|
||||
while (thebot && stricmp (name, thebot->name))
|
||||
{
|
||||
botshift++;
|
||||
thebot = thebot->next;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (botnum < loaded_bots)
|
||||
{
|
||||
bool vacant = false; //Spawn a random bot from bots.cfg if no name given.
|
||||
while (!vacant)
|
||||
{
|
||||
int rnum = (pr_botspawn() % loaded_bots);
|
||||
thebot = botinfo;
|
||||
while (rnum)
|
||||
--rnum, thebot = thebot->next;
|
||||
if (!thebot->inuse)
|
||||
vacant = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Couldn't spawn bot; no bot left in %s\n", BOTFILENAME);
|
||||
return false;
|
||||
//Spawn a random bot from bots.cfg if no name given.
|
||||
TArray<botinfo_t *> BotInfoAvailable;
|
||||
|
||||
while (thebot)
|
||||
{
|
||||
if (thebot->inuse == BOTINUSE_No)
|
||||
BotInfoAvailable.Push (thebot);
|
||||
|
||||
thebot = thebot->next;
|
||||
}
|
||||
|
||||
if (BotInfoAvailable.Size () == 0)
|
||||
{
|
||||
Printf ("Couldn't spawn bot; no bot left in %s\n", BOTFILENAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
thebot = BotInfoAvailable[pr_botspawn() % BotInfoAvailable.Size ()];
|
||||
|
||||
botinfo_t *thebot2 = botinfo;
|
||||
while (thebot2)
|
||||
{
|
||||
if (thebot == thebot2)
|
||||
break;
|
||||
|
||||
botshift++;
|
||||
thebot2 = thebot2->next;
|
||||
}
|
||||
}
|
||||
|
||||
waitingforspawn[playernumber] = true;
|
||||
thebot->inuse = BOTINUSE_Waiting;
|
||||
|
||||
Net_WriteByte (DEM_ADDBOT);
|
||||
Net_WriteByte (playernumber);
|
||||
Net_WriteByte (botshift);
|
||||
{
|
||||
//Set color.
|
||||
char concat[512];
|
||||
|
@ -335,52 +297,106 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
|
|||
}
|
||||
Net_WriteString (concat);
|
||||
}
|
||||
|
||||
players[playernumber].skill = thebot->skill;
|
||||
|
||||
thebot->inuse = true;
|
||||
|
||||
//Increment this.
|
||||
botnum++;
|
||||
Net_WriteByte(thebot->skill.aiming);
|
||||
Net_WriteByte(thebot->skill.perfection);
|
||||
Net_WriteByte(thebot->skill.reaction);
|
||||
Net_WriteByte(thebot->skill.isp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FCajunMaster::DoAddBot (int bnum, char *info)
|
||||
void FCajunMaster::TryAddBot (BYTE **stream, int player)
|
||||
{
|
||||
BYTE *infob = (BYTE *)info;
|
||||
D_ReadUserInfoStrings (bnum, &infob, false);
|
||||
int botshift = ReadByte (stream);
|
||||
char *info = ReadString (stream);
|
||||
botskill_t skill;
|
||||
skill.aiming = ReadByte (stream);
|
||||
skill.perfection = ReadByte (stream);
|
||||
skill.reaction = ReadByte (stream);
|
||||
skill.isp = ReadByte (stream);
|
||||
|
||||
botinfo_t *thebot = NULL;
|
||||
|
||||
if (consoleplayer == player)
|
||||
{
|
||||
thebot = botinfo;
|
||||
|
||||
while (botshift > 0)
|
||||
{
|
||||
thebot = thebot->next;
|
||||
botshift--;
|
||||
}
|
||||
}
|
||||
|
||||
if (DoAddBot ((BYTE *)info, skill))
|
||||
{
|
||||
//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;
|
||||
players[bnum].Bot->player = &players[bnum];
|
||||
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].isbot = true;
|
||||
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());
|
||||
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)
|
||||
|
@ -389,13 +405,13 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
|
|||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (playeringame[i] && botingame[i])
|
||||
if (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)
|
||||
{
|
||||
|
@ -415,34 +431,10 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
|
|||
if (fromlist)
|
||||
{
|
||||
wanted_botnum = 0;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
waitingforspawn[i] = false;
|
||||
}
|
||||
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->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.
|
||||
//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;
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
//------------------
|
||||
//Reads data for bot from
|
||||
|
@ -495,7 +487,6 @@ void FCajunMaster::ForgetBots ()
|
|||
}
|
||||
|
||||
botinfo = NULL;
|
||||
loaded_bots = 0;
|
||||
}
|
||||
|
||||
bool FCajunMaster::LoadBots ()
|
||||
|
@ -503,6 +494,7 @@ bool FCajunMaster::LoadBots ()
|
|||
FScanner sc;
|
||||
FString tmp;
|
||||
bool gotteam = false;
|
||||
int loaded_bots = 0;
|
||||
|
||||
bglobal.ForgetBots ();
|
||||
tmp = M_GetCajunPath(BOTFILENAME);
|
||||
|
@ -619,9 +611,9 @@ bool FCajunMaster::LoadBots ()
|
|||
newinfo->next = bglobal.botinfo;
|
||||
newinfo->lastteam = TEAM_NONE;
|
||||
bglobal.botinfo = newinfo;
|
||||
bglobal.loaded_bots++;
|
||||
loaded_bots++;
|
||||
}
|
||||
Printf ("%d bots read from %s\n", bglobal.loaded_bots, BOTFILENAME);
|
||||
Printf ("%d bots read from %s\n", loaded_bots, BOTFILENAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
142
src/b_move.cpp
142
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,62 +26,58 @@ 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<line_t *> spechit;
|
||||
|
||||
//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->dest))
|
||||
if (Reachable(dest))
|
||||
{ // Straight towards it.
|
||||
actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->dest->x, actor->player->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->angle &= (angle_t)(7<<29);
|
||||
delta = actor->player->angle - (actor->movedir << 29);
|
||||
angle &= (angle_t)(7<<29);
|
||||
delta = angle - (player->mo->movedir << 29);
|
||||
|
||||
if (delta > 0)
|
||||
actor->player->angle -= ANG45;
|
||||
angle -= ANG45;
|
||||
else if (delta < 0)
|
||||
actor->player->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;
|
||||
|
@ -103,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)))
|
||||
|
@ -130,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;
|
||||
|
@ -151,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
|
|||
|
||||
dirtype_t turnaround;
|
||||
|
||||
if (!actor->player->dest)
|
||||
if (!dest)
|
||||
{
|
||||
#ifndef BOT_RELEASE_COMPILE
|
||||
Printf ("Bot tried move without destination\n");
|
||||
|
@ -159,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->dest->x - actor->x;
|
||||
deltay = actor->player->dest->y - actor->y;
|
||||
deltax = dest->x - player->mo->x;
|
||||
deltay = dest->y - player->mo->y;
|
||||
|
||||
if (deltax > 10*FRACUNIT)
|
||||
d[1] = DI_EAST;
|
||||
|
@ -182,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;
|
||||
}
|
||||
|
||||
|
@ -203,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;
|
||||
}
|
||||
|
||||
|
@ -220,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;
|
||||
}
|
||||
|
||||
|
@ -235,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;
|
||||
}
|
||||
}
|
||||
|
@ -250,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;
|
||||
}
|
||||
}
|
||||
|
@ -260,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
|
||||
}
|
||||
|
||||
|
||||
|
@ -324,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->t_roam && !actor->player->missile)
|
||||
if (t_roam && !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->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
|
||||
if(Check_LOS(actor, actor->player->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->angle - actor->angle;
|
||||
int distance = angle - player->mo->angle;
|
||||
|
||||
if (abs (distance) < OKAYRANGE && !actor->player->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.
|
||||
|
@ -388,4 +371,3 @@ bool FCajunMaster::IsDangerous (sector_t *sec)
|
|||
|| special == Damage_InstantDeath
|
||||
|| special == sDamage_SuperHellslime;
|
||||
}
|
||||
|
||||
|
|
284
src/b_think.cpp
284
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->enemy && actor->player->enemy->health <= 0)
|
||||
actor->player->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->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->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 (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->t_respawn)
|
||||
if (t_respawn)
|
||||
{
|
||||
actor->player->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->isbot)
|
||||
return;
|
||||
|
||||
stuck = false;
|
||||
dist = b->dest ? P_AproxDistance(actor->x-b->dest->x, actor->y-b->dest->y) : 0;
|
||||
dist = dest ? P_AproxDistance(player->mo->x-dest->x, player->mo->y-dest->y) : 0;
|
||||
|
||||
if (b->missile &&
|
||||
((!b->missile->velx || !b->missile->vely) || !Check_LOS(actor, b->missile, SHOOTFOV*3/2)))
|
||||
if (missile &&
|
||||
((!missile->velx || !missile->vely) || !Check_LOS(missile, SHOOTFOV*3/2)))
|
||||
{
|
||||
b->sleft = !b->sleft;
|
||||
b->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->missile && (P_AproxDistance(actor->x-b->missile->x, actor->y-b->missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
|
||||
if (missile && (P_AproxDistance(player->mo->x-missile->x, player->mo->y-missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
|
||||
{
|
||||
Pitch (actor, b->missile);
|
||||
actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->missile->x, b->missile->y);
|
||||
cmd->ucmd.sidemove = b->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->oldx, actor->y-b->oldy)<50000)
|
||||
&& b->t_strafe<=0)
|
||||
if ((P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000)
|
||||
&& t_strafe<=0)
|
||||
{
|
||||
b->t_strafe = 5;
|
||||
b->sleft = !b->sleft;
|
||||
t_strafe = 5;
|
||||
sleft = !sleft;
|
||||
}
|
||||
|
||||
//If able to see enemy while avoiding missile, still fire at enemy.
|
||||
if (b->enemy && Check_LOS (actor, b->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->enemy && P_CheckSight (actor, b->enemy, 0)) //Fight!
|
||||
else if (enemy && P_CheckSight (player->mo, enemy, 0)) //Fight!
|
||||
{
|
||||
Pitch (actor, b->enemy);
|
||||
Pitch (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 (dest && (dest->flags&MF_SPECIAL)) //Must be an item, that is close enough.
|
||||
{
|
||||
#define is(x) b->dest->IsKindOf (PClass::FindClass (#x))
|
||||
#define is(x) dest->IsKindOf (PClass::FindClass (#x))
|
||||
if (
|
||||
(
|
||||
(actor->health < b->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->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->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->enemy->flags3 & MF3_ISMONSTER))
|
||||
b->t_fight = AFTERTICS;
|
||||
if (!(enemy->flags3 & MF3_ISMONSTER))
|
||||
t_fight = AFTERTICS;
|
||||
|
||||
if (b->t_strafe <= 0 &&
|
||||
(P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000
|
||||
if (t_strafe <= 0 &&
|
||||
(P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000
|
||||
|| ((pr_botmove()%30)==10))
|
||||
)
|
||||
{
|
||||
stuck = true;
|
||||
b->t_strafe = 5;
|
||||
b->sleft = !b->sleft;
|
||||
t_strafe = 5;
|
||||
sleft = !sleft;
|
||||
}
|
||||
|
||||
b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y);
|
||||
angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y);
|
||||
|
||||
if (b->ReadyWeapon == NULL ||
|
||||
P_AproxDistance(actor->x-b->enemy->x, actor->y-b->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->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->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN;
|
||||
cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN;
|
||||
}
|
||||
|
||||
//Strafing.
|
||||
if (b->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->sleft ? -SIDEWALK : SIDEWALK;
|
||||
cmd->ucmd.sidemove = sleft ? -SIDEWALK : SIDEWALK;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->ucmd.sidemove = b->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->mate && !b->enemy && (!b->dest || b->dest==b->mate)) //Follow mate move.
|
||||
else if (mate && !enemy && (!dest || dest==mate)) //Follow mate move.
|
||||
{
|
||||
fixed_t matedist;
|
||||
|
||||
Pitch (actor, b->mate);
|
||||
Pitch (mate);
|
||||
|
||||
if (!Reachable (actor, b->mate))
|
||||
if (!Reachable (mate))
|
||||
{
|
||||
if (b->mate == b->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->dest = NULL;
|
||||
dest = NULL;
|
||||
}
|
||||
goto roam;
|
||||
}
|
||||
|
||||
actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->mate->x, b->mate->y);
|
||||
angle = R_PointToAngle2(player->mo->x, player->mo->y, mate->x, mate->y);
|
||||
|
||||
matedist = P_AproxDistance(actor->x - b->mate->x, actor->y - b->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->first_shot = true;
|
||||
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.
|
||||
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->dest && !(b->dest->flags&MF_SPECIAL) && b->dest->health < 0)
|
||||
if (dest && !(dest->flags&MF_SPECIAL) && dest->health < 0)
|
||||
{ //Roaming after something dead.
|
||||
b->dest = NULL;
|
||||
dest = NULL;
|
||||
}
|
||||
|
||||
if (b->dest == NULL)
|
||||
if (dest == NULL)
|
||||
{
|
||||
if (b->t_fight && b->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->enemy->player)
|
||||
if (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.
|
||||
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);
|
||||
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->dest = b->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<AInventory> it (STAT_INVENTORY, firstthing);
|
||||
TThinkerIterator<AInventory> 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->dest = item;
|
||||
bglobal.firstthing = item;
|
||||
dest = item;
|
||||
}
|
||||
}
|
||||
else if (b->mate && (r < 179 || P_CheckSight(actor, b->mate)))
|
||||
else if (mate && (r < 179 || P_CheckSight(player->mo, mate)))
|
||||
{
|
||||
b->dest = b->mate;
|
||||
dest = mate;
|
||||
}
|
||||
else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0)
|
||||
{
|
||||
b->dest = players[(r&(MAXPLAYERS-1))].mo;
|
||||
dest = players[(r&(MAXPLAYERS-1))].mo;
|
||||
}
|
||||
}
|
||||
|
||||
if (b->dest)
|
||||
if (dest)
|
||||
{
|
||||
b->t_roam = MAXROAM;
|
||||
t_roam = MAXROAM;
|
||||
}
|
||||
}
|
||||
if (b->dest)
|
||||
if (dest)
|
||||
{ //Bot has a target so roam after it.
|
||||
Roam (actor, cmd);
|
||||
Roam (cmd);
|
||||
}
|
||||
|
||||
} //End of movement main part.
|
||||
|
||||
if (!b->t_roam && b->dest)
|
||||
if (!t_roam && dest)
|
||||
{
|
||||
b->prev = b->dest;
|
||||
b->dest = NULL;
|
||||
prev = dest;
|
||||
dest = NULL;
|
||||
}
|
||||
|
||||
if (b->t_fight<(AFTERTICS/2))
|
||||
actor->flags |= MF_DROPOFF;
|
||||
if (t_fight<(AFTERTICS/2))
|
||||
player->mo->flags |= MF_DROPOFF;
|
||||
|
||||
b->oldx = actor->x;
|
||||
b->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->prev)
|
||||
|| item == prev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -338,7 +328,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
|
|||
// FIXME
|
||||
AWeapon *heldWeapon;
|
||||
|
||||
heldWeapon = static_cast<AWeapon *> (b->mo->FindInventory (item->GetClass()));
|
||||
heldWeapon = static_cast<AWeapon *> (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<AAmmo *> (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->dest == NULL ||
|
||||
!(b->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->prev = b->dest;
|
||||
b->dest = item;
|
||||
b->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->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->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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ())
|
||||
|
@ -882,8 +900,8 @@ CCMD(info)
|
|||
linetarget->SpawnHealth());
|
||||
PrintMiscActorInfo(linetarget);
|
||||
}
|
||||
else Printf("No target found. Info cannot find actors that have\
|
||||
the NOBLOCKMAP flag or have height/radius of 0.\n");
|
||||
else Printf("No target found. Info cannot find actors that have "
|
||||
"the NOBLOCKMAP flag or have height/radius of 0.\n");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1421,7 +1426,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
|
||||
|
@ -1545,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'));
|
||||
|
@ -1569,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'));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -1514,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;
|
||||
|
|
|
@ -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<FBaseCVar *> &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);
|
||||
|
||||
|
|
|
@ -185,6 +185,9 @@ static const char *KeyConfCommands[] =
|
|||
"clearplayerclasses"
|
||||
};
|
||||
|
||||
static TArray<FString> StoredStartupSets;
|
||||
static bool RunningStoredStartups;
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS (DWaitingCommand)
|
||||
|
@ -537,6 +540,18 @@ void ResetButtonStates ()
|
|||
}
|
||||
}
|
||||
|
||||
void C_ExecStoredSets()
|
||||
{
|
||||
assert(!RunningStoredStartups);
|
||||
RunningStoredStartups = true;
|
||||
for (unsigned i = 0; i < StoredStartupSets.Size(); ++i)
|
||||
{
|
||||
C_DoCommand(StoredStartupSets[i]);
|
||||
}
|
||||
StoredStartupSets.Clear();
|
||||
RunningStoredStartups = false;
|
||||
}
|
||||
|
||||
void C_DoCommand (const char *cmd, int keynum)
|
||||
{
|
||||
FConsoleCommand *com;
|
||||
|
@ -612,7 +627,22 @@ void C_DoCommand (const char *cmd, int keynum)
|
|||
|
||||
if ( (com = FindNameInHashTable (Commands, beg, len)) )
|
||||
{
|
||||
if (gamestate != GS_STARTUP || ParsingKeyConf ||
|
||||
if (gamestate == GS_STARTUP && !RunningStoredStartups &&
|
||||
len == 3 && strnicmp(beg, "set", 3) == 0)
|
||||
{
|
||||
// Save setting of unknown cvars for later, in case a loaded wad has a
|
||||
// CVARINFO that defines it.
|
||||
FCommandLine args(beg);
|
||||
if (args.argc() > 1 && FindCVar(args[1], NULL) == NULL)
|
||||
{
|
||||
StoredStartupSets.Push(beg);
|
||||
}
|
||||
else
|
||||
{
|
||||
com->Run(args, players[consoleplayer].mo, keynum);
|
||||
}
|
||||
}
|
||||
else if (gamestate != GS_STARTUP || ParsingKeyConf ||
|
||||
(len == 3 && strnicmp (beg, "set", 3) == 0) ||
|
||||
(len == 7 && strnicmp (beg, "logfile", 7) == 0) ||
|
||||
(len == 9 && strnicmp (beg, "unbindall", 9) == 0) ||
|
||||
|
@ -657,12 +687,15 @@ void C_DoCommand (const char *cmd, int keynum)
|
|||
}
|
||||
else
|
||||
{ // We don't know how to handle this command
|
||||
char cmdname[64];
|
||||
size_t minlen = MIN<size_t> (len, 63);
|
||||
|
||||
memcpy (cmdname, beg, minlen);
|
||||
cmdname[len] = 0;
|
||||
Printf ("Unknown command \"%s\"\n", cmdname);
|
||||
if (gamestate == GS_STARTUP && !RunningStoredStartups)
|
||||
{
|
||||
// Save it for later, in case a CVARINFO defines it.
|
||||
StoredStartupSets.Push(beg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Unknown command \"%.*s\"\n", len, beg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class APlayerPawn;
|
|||
extern bool CheckCheatmode (bool printmsg = true);
|
||||
|
||||
void C_ExecCmdLineParams ();
|
||||
void C_ExecStoredSets();
|
||||
|
||||
// Add commands to the console as if they were typed in. Can handle wait
|
||||
// and semicolon-separated commands. This function may modify the source
|
||||
|
|
|
@ -82,6 +82,7 @@ enum
|
|||
CP_SETWALLYSCALE,
|
||||
CP_SETTHINGZ,
|
||||
CP_SETTAG,
|
||||
CP_SETTHINGFLAGS,
|
||||
};
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
@ -141,6 +142,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 }
|
||||
};
|
||||
|
@ -317,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();
|
||||
|
@ -539,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
@ -273,7 +281,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)
|
||||
{
|
||||
|
@ -334,6 +343,10 @@ static void CT_ClearChatMessage ()
|
|||
|
||||
static void ShoveChatStr (const char *str, BYTE who)
|
||||
{
|
||||
// Don't send empty messages
|
||||
if (str == NULL || str[0] == '\0')
|
||||
return;
|
||||
|
||||
FString substBuff;
|
||||
|
||||
if (str[0] == '/' &&
|
||||
|
|
|
@ -2247,7 +2247,10 @@ static int PatchStrings (int dummy)
|
|||
|
||||
ReplaceSpecialChars (holdstring.LockBuffer());
|
||||
holdstring.UnlockBuffer();
|
||||
GStrings.SetString (Line1, holdstring);
|
||||
// Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message
|
||||
const char *ll = Line1;
|
||||
if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL";
|
||||
GStrings.SetString (ll, holdstring);
|
||||
DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars());
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ typedef enum
|
|||
ga_loadlevel,
|
||||
ga_newgame,
|
||||
ga_newgame2,
|
||||
ga_recordgame,
|
||||
ga_loadgame,
|
||||
ga_loadgamehidecon,
|
||||
ga_loadgameplaydemo,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -430,27 +430,11 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
FString steam_path = I_GetSteamPath();
|
||||
if (steam_path.IsNotEmpty())
|
||||
TArray<FString> 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())
|
||||
|
|
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -756,9 +757,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
|
||||
|
@ -832,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
|
||||
|
@ -974,25 +983,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].isbot && players[i].mo)
|
||||
{
|
||||
players[i].savedyaw = players[i].mo->angle;
|
||||
players[i].savedpitch = players[i].mo->pitch;
|
||||
}
|
||||
}
|
||||
bglobal.Main (maketic%BACKUPTICS);
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && players[i].isbot && players[i].mo)
|
||||
{
|
||||
players[i].mo->angle = players[i].savedyaw;
|
||||
players[i].mo->pitch = players[i].savedpitch;
|
||||
}
|
||||
}
|
||||
if (advancedemo)
|
||||
D_DoAdvanceDemo ();
|
||||
C_Ticker ();
|
||||
|
@ -1337,6 +1327,7 @@ CCMD (endgame)
|
|||
{
|
||||
gameaction = ga_fullconsole;
|
||||
demosequence = -1;
|
||||
G_CheckDemoStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2282,6 +2273,8 @@ void D_DoomMain (void)
|
|||
execFiles = Args->GatherFiles ("-exec");
|
||||
D_MultiExec (execFiles, true);
|
||||
|
||||
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
|
||||
|
||||
CopyFiles(allwads, pwads);
|
||||
|
||||
// Since this function will never leave we must delete this array here manually.
|
||||
|
@ -2297,7 +2290,8 @@ void D_DoomMain (void)
|
|||
// Now that wads are loaded, define mod-specific cvars.
|
||||
ParseCVarInfo();
|
||||
|
||||
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
|
||||
// Try setting previously unknown cvars again, as a CVARINFO may have made them known.
|
||||
C_ExecStoredSets();
|
||||
|
||||
// [RH] Initialize localizable strings.
|
||||
GStrings.LoadStrings (false);
|
||||
|
@ -2587,6 +2581,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
|
||||
|
|
437
src/d_net.cpp
437
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"
|
||||
|
@ -110,13 +109,15 @@ 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][BACKUPTICS]; // Used for storing network delay times.
|
||||
int lastaverage;
|
||||
|
||||
int nodeforplayer[MAXPLAYERS];
|
||||
int playerfornode[MAXNETNODES];
|
||||
|
||||
int maketic;
|
||||
int skiptics;
|
||||
int ticdup;
|
||||
int ticdup;
|
||||
|
||||
void D_ProcessEvents (void);
|
||||
void G_BuildTiccmd (ticcmd_t *cmd);
|
||||
|
@ -151,6 +152,32 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|||
}
|
||||
}
|
||||
|
||||
CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE)
|
||||
CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE)
|
||||
{
|
||||
if (self < 0)
|
||||
{
|
||||
self = 0;
|
||||
}
|
||||
else if (self > 2)
|
||||
{
|
||||
self = 2;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
CVAR(Int, net_fakelatency, 0, 0);
|
||||
|
||||
struct PacketStore
|
||||
{
|
||||
int timer;
|
||||
doomcom_t message;
|
||||
};
|
||||
|
||||
static TArray<PacketStore> InBuffer;
|
||||
static TArray<PacketStore> OutBuffer;
|
||||
#endif
|
||||
|
||||
// [RH] Special "ticcmds" get stored in here
|
||||
static struct TicSpecial
|
||||
{
|
||||
|
@ -158,7 +185,7 @@ static struct TicSpecial
|
|||
size_t used[BACKUPTICS];
|
||||
BYTE *streamptr;
|
||||
size_t streamoffs;
|
||||
int specialsize;
|
||||
size_t specialsize;
|
||||
int lastmaketic;
|
||||
bool okay;
|
||||
|
||||
|
@ -197,11 +224,11 @@ static struct TicSpecial
|
|||
}
|
||||
|
||||
// Make more room for special commands.
|
||||
void GetMoreSpace ()
|
||||
void GetMoreSpace (size_t needed)
|
||||
{
|
||||
int i;
|
||||
|
||||
specialsize <<= 1;
|
||||
specialsize = MAX(specialsize * 2, needed + 30);
|
||||
|
||||
DPrintf ("Expanding special size to %d\n", specialsize);
|
||||
|
||||
|
@ -213,8 +240,8 @@ static struct TicSpecial
|
|||
|
||||
void CheckSpace (size_t needed)
|
||||
{
|
||||
if (streamoffs >= specialsize - needed)
|
||||
GetMoreSpace ();
|
||||
if (streamoffs + needed >= specialsize)
|
||||
GetMoreSpace (streamoffs + needed);
|
||||
|
||||
streamoffs += needed;
|
||||
}
|
||||
|
@ -347,6 +374,9 @@ int NetbufferSize ()
|
|||
k += netbuffer[k] + 1;
|
||||
}
|
||||
|
||||
// Network delay byte
|
||||
k++;
|
||||
|
||||
if (netbuffer[0] & NCMD_MULTI)
|
||||
{
|
||||
count = netbuffer[k];
|
||||
|
@ -487,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
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -509,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)
|
||||
{
|
||||
|
@ -570,6 +653,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());
|
||||
|
@ -583,87 +669,63 @@ void PlayerIsGone (int netnode, int netconsole)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = netnode + 1; i < doomcom.numnodes; ++i)
|
||||
if (nodeingame[netnode])
|
||||
{
|
||||
if (nodeingame[i])
|
||||
break;
|
||||
}
|
||||
if (i == doomcom.numnodes)
|
||||
{
|
||||
doomcom.numnodes = netnode;
|
||||
}
|
||||
|
||||
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)
|
||||
for (i = netnode + 1; i < doomcom.numnodes; ++i)
|
||||
{
|
||||
players[ii].camera = players[ii].mo;
|
||||
if (ii == consoleplayer && StatusBar != NULL)
|
||||
{
|
||||
StatusBar->AttachToPlayer (&players[ii]);
|
||||
}
|
||||
if (nodeingame[i])
|
||||
break;
|
||||
}
|
||||
if (i == doomcom.numnodes)
|
||||
{
|
||||
doomcom.numnodes = netnode;
|
||||
}
|
||||
}
|
||||
|
||||
// [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();
|
||||
if (playeringame[netconsole])
|
||||
{
|
||||
players[netconsole].playerstate = PST_GONE;
|
||||
}
|
||||
players[netconsole].mo = NULL;
|
||||
players[netconsole].camera = NULL;
|
||||
nodeingame[netnode] = false;
|
||||
nodejustleft[netnode] = false;
|
||||
}
|
||||
// [RH] Let the scripts know the player left
|
||||
FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, netconsole);
|
||||
else if (nodejustleft[netnode]) // Packet Server
|
||||
{
|
||||
if (netnode + 1 == doomcom.numnodes)
|
||||
{
|
||||
doomcom.numnodes = netnode;
|
||||
}
|
||||
if (playeringame[netconsole])
|
||||
{
|
||||
players[netconsole].playerstate = PST_GONE;
|
||||
}
|
||||
nodejustleft[netnode] = false;
|
||||
}
|
||||
else return;
|
||||
|
||||
if (netconsole == Net_Arbitrator)
|
||||
{
|
||||
bglobal.RemoveAllBots (true);
|
||||
Printf ("Removed all bots\n");
|
||||
|
||||
// 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].Bot == NULL)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -741,7 +803,6 @@ void GetPackets (void)
|
|||
else
|
||||
{
|
||||
nodeingame[netnode] = false;
|
||||
playeringame[netconsole] = false;
|
||||
nodejustleft[netnode] = true;
|
||||
}
|
||||
continue;
|
||||
|
@ -772,7 +833,6 @@ void GetPackets (void)
|
|||
}
|
||||
|
||||
if (netbuffer[0] & NCMD_QUITTERS)
|
||||
|
||||
{
|
||||
numplayers = netbuffer[k++];
|
||||
for (int i = 0; i < numplayers; ++i)
|
||||
|
@ -782,6 +842,9 @@ void GetPackets (void)
|
|||
}
|
||||
}
|
||||
|
||||
// Pull current network delay from node
|
||||
netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++];
|
||||
|
||||
playerbytes[0] = netconsole;
|
||||
if (netbuffer[0] & NCMD_MULTI)
|
||||
{
|
||||
|
@ -847,64 +910,18 @@ void GetPackets (void)
|
|||
|
||||
for (i = 0; i < numplayers; ++i)
|
||||
{
|
||||
int node = !players[playerbytes[i]].isbot ?
|
||||
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]].isbot)
|
||||
{
|
||||
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].isbot && players[i].mo)
|
||||
{
|
||||
players[i].savedyaw = players[i].mo->angle;
|
||||
players[i].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].isbot && players[i].mo)
|
||||
{
|
||||
players[i].mo->angle = players[i].savedyaw;
|
||||
players[i].mo->pitch = players[i].savedpitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// NetUpdate
|
||||
// Builds ticcmds for console player,
|
||||
|
@ -934,7 +951,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;
|
||||
|
@ -951,9 +968,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 ();
|
||||
|
@ -963,11 +978,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)
|
||||
|
@ -1047,8 +1057,6 @@ void NetUpdate (void)
|
|||
}
|
||||
}
|
||||
|
||||
UnadjustBots ();
|
||||
|
||||
if (singletics)
|
||||
return; // singletic update is synchronous
|
||||
|
||||
|
@ -1068,19 +1076,16 @@ void NetUpdate (void)
|
|||
|
||||
if (consoleplayer == Net_Arbitrator)
|
||||
{
|
||||
for (j = 0; j < MAXPLAYERS; j++)
|
||||
if (NetMode == NET_PacketServer)
|
||||
{
|
||||
if (playeringame[j])
|
||||
for (j = 0; j < MAXPLAYERS; j++)
|
||||
{
|
||||
if (players[j].isbot || NetMode == NET_PacketServer)
|
||||
if (playeringame[j] && players[j].Bot == NULL)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NetMode == NET_PacketServer)
|
||||
{
|
||||
// The loop above added the local player to the count a second time,
|
||||
// and it also added the player being sent the packet to the count.
|
||||
count -= 2;
|
||||
|
@ -1150,11 +1155,18 @@ 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);
|
||||
|
||||
resendto[i] = MAX (0, lowtic - doomcom.extratics);
|
||||
switch (net_extratic)
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
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])
|
||||
{
|
||||
|
@ -1190,6 +1202,10 @@ void NetUpdate (void)
|
|||
}
|
||||
}
|
||||
|
||||
// Send current network delay
|
||||
// The number of tics we just made should be removed from the count.
|
||||
netbuffer[k++] = ((maketic - newtics - gametic) / ticdup);
|
||||
|
||||
if (numtics > 0)
|
||||
{
|
||||
int l;
|
||||
|
@ -1199,11 +1215,11 @@ void NetUpdate (void)
|
|||
netbuffer[0] |= NCMD_MULTI;
|
||||
netbuffer[k++] = count;
|
||||
|
||||
for (l = 1, j = 0; j < MAXPLAYERS; j++)
|
||||
if (NetMode == NET_PacketServer)
|
||||
{
|
||||
if (playeringame[j] && j != playerfornode[i] && j != consoleplayer)
|
||||
for (l = 1, j = 0; j < MAXPLAYERS; j++)
|
||||
{
|
||||
if (players[j].isbot || NetMode == NET_PacketServer)
|
||||
if (playeringame[j] && players[j].Bot == NULL && j != playerfornode[i] && j != consoleplayer)
|
||||
{
|
||||
playerbytes[l++] = j;
|
||||
netbuffer[k++] = j;
|
||||
|
@ -1227,7 +1243,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);
|
||||
|
@ -1242,24 +1258,17 @@ void NetUpdate (void)
|
|||
}
|
||||
else if (i != 0)
|
||||
{
|
||||
if (players[playerbytes[l]].isbot)
|
||||
{
|
||||
int len;
|
||||
BYTE *spec;
|
||||
|
||||
WriteWord (0, &cmddata); // fake consistancy word
|
||||
}
|
||||
else
|
||||
WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata);
|
||||
spec = NetSpecs[playerbytes[l]][start].GetData (&len);
|
||||
if (spec != NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
memcpy (cmddata, spec, len);
|
||||
cmddata += len;
|
||||
}
|
||||
|
||||
WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd,
|
||||
prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata);
|
||||
}
|
||||
|
@ -1299,9 +1308,37 @@ void NetUpdate (void)
|
|||
// that it won't adapt. Fortunately, player prediction helps
|
||||
// alleviate the lag somewhat.
|
||||
|
||||
if (NetMode != NET_PacketServer)
|
||||
if (NetMode == NET_PeerToPeer)
|
||||
{
|
||||
mastertics = nettics[nodeforplayer[Net_Arbitrator]];
|
||||
int totalavg = 0;
|
||||
if (net_ticbalance)
|
||||
{
|
||||
// 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++)
|
||||
{
|
||||
arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j];
|
||||
nodeavg += netdelay[0][j];
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg;
|
||||
}
|
||||
if (nettics[0] <= mastertics)
|
||||
{
|
||||
|
@ -1346,9 +1383,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
|
||||
//
|
||||
|
@ -1429,10 +1465,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;
|
||||
|
@ -1497,9 +1532,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);
|
||||
|
@ -1634,10 +1668,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 prone to running too slow on some internet configurations."
|
||||
"\nIf the game is running well below expected 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)
|
||||
{
|
||||
|
@ -1647,15 +1690,23 @@ 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");
|
||||
}
|
||||
|
||||
if (Args->CheckParm("-extratic"))
|
||||
{
|
||||
net_extratic = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// [RH] Setup user info
|
||||
|
@ -1683,6 +1734,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);
|
||||
}
|
||||
|
@ -1809,6 +1865,9 @@ void TryRunTics (void)
|
|||
{
|
||||
C_Ticker();
|
||||
M_Ticker();
|
||||
// Repredict the player for new buffered movement
|
||||
P_UnPredictPlayer();
|
||||
P_PredictPlayer(&players[consoleplayer]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1844,6 +1903,9 @@ void TryRunTics (void)
|
|||
{
|
||||
C_Ticker ();
|
||||
M_Ticker ();
|
||||
// Repredict the player for new buffered movement
|
||||
P_UnPredictPlayer();
|
||||
P_PredictPlayer(&players[consoleplayer]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1857,6 +1919,7 @@ void TryRunTics (void)
|
|||
// run the count tics
|
||||
if (counts > 0)
|
||||
{
|
||||
P_UnPredictPlayer();
|
||||
while (counts--)
|
||||
{
|
||||
if (gametic > lowtic)
|
||||
|
@ -1876,6 +1939,7 @@ void TryRunTics (void)
|
|||
|
||||
NetUpdate (); // check for new console commands
|
||||
}
|
||||
P_PredictPlayer(&players[consoleplayer]);
|
||||
S_UpdateSounds (players[consoleplayer].camera); // move positional sounds
|
||||
}
|
||||
}
|
||||
|
@ -2130,10 +2194,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.TryAddBot (stream, player);
|
||||
break;
|
||||
|
||||
case DEM_KILLBOTS:
|
||||
|
@ -2589,10 +2650,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;
|
||||
|
@ -2713,7 +2777,6 @@ void Net_SkipCommand (int type, BYTE **stream)
|
|||
CCMD (pings)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],
|
||||
|
@ -2755,7 +2818,7 @@ static void Network_Controller (int playernum, bool add)
|
|||
return;
|
||||
}
|
||||
|
||||
if (players[playernum].isbot)
|
||||
if (players[playernum].Bot != NULL)
|
||||
{
|
||||
Printf ("Bots cannot be added to the controller list.\n");
|
||||
return;
|
||||
|
|
|
@ -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
|
||||
|
@ -143,6 +142,8 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS];
|
|||
|
||||
extern int maketic;
|
||||
extern int nettics[MAXNETNODES];
|
||||
extern int netdelay[MAXNETNODES][BACKUPTICS];
|
||||
extern int nodeforplayer[MAXPLAYERS];
|
||||
|
||||
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
|
||||
extern int ticdup;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -76,8 +76,6 @@ FPlayerColorSet *P_GetPlayerColorSet(FName classname, int setnum);
|
|||
void P_EnumPlayerColorSets(FName classname, TArray<int> *out);
|
||||
const char *GetPrintableDisplayName(const PClass *cls);
|
||||
|
||||
class player_t;
|
||||
|
||||
class APlayerPawn : public AActor
|
||||
{
|
||||
DECLARE_CLASS (APlayerPawn, AActor)
|
||||
|
@ -176,7 +174,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;
|
||||
|
||||
|
||||
|
@ -206,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;
|
||||
|
@ -398,8 +399,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
|
||||
|
@ -444,47 +444,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<AActor> dest; // Move Destination.
|
||||
TObjPtr<AActor> prev; // Previous move destination.
|
||||
|
||||
|
||||
TObjPtr<AActor> enemy; // The dead meat.
|
||||
TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
|
||||
TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
|
||||
TObjPtr<AActor> last_mate; // If bots mate disappeared (not if died) that mate is
|
||||
// pointed to by this. Allows bot to roam to it if
|
||||
// necessary.
|
||||
TObjPtr<DBot> 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;
|
||||
|
|
|
@ -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: 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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -136,6 +137,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;
|
||||
|
|
|
@ -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<ABasicArmorBonus *>(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);
|
||||
|
||||
|
@ -546,6 +573,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 +587,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 +610,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,14 +624,24 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
121
src/g_game.cpp
121
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"
|
||||
|
@ -115,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);
|
||||
|
||||
//==========================================================================
|
||||
|
@ -140,6 +142,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
|
||||
|
@ -161,6 +164,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;
|
||||
|
@ -874,7 +879,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 != NULL)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -1013,10 +1018,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1041,6 +1052,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 ();
|
||||
|
@ -1114,6 +1129,9 @@ void G_Ticker ()
|
|||
// check, not just the player's x position like BOOM.
|
||||
DWORD rngsum = FRandom::StaticSumSeeds ();
|
||||
|
||||
//Added by MC: For some of that bot stuff. The main bot function.
|
||||
bglobal.Main ();
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
|
@ -1133,13 +1151,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
|
||||
|
@ -1149,7 +1167,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 == NULL && !demoplayback && (gametic%ticdup) == 0)
|
||||
{
|
||||
//players[i].inconsistant = 0;
|
||||
if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy)
|
||||
|
@ -1331,10 +1349,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 *Bot; //Added by MC:
|
||||
|
||||
p = &players[player];
|
||||
|
||||
|
@ -1344,18 +1362,19 @@ void G_PlayerReborn (int player)
|
|||
itemcount = p->itemcount;
|
||||
secretcount = p->secretcount;
|
||||
currclass = p->CurrentPlayerClass;
|
||||
b_skill = p->skill; //Added by MC:
|
||||
userinfo.TransferFrom(p->userinfo);
|
||||
actor = p->mo;
|
||||
cls = p->cls;
|
||||
log = p->LogText;
|
||||
chasecam = p->cheats & CF_CHASECAM;
|
||||
Bot = p->Bot; //Added by MC:
|
||||
|
||||
// Reset player structure to its defaults
|
||||
p->~player_t();
|
||||
::new(p) player_t;
|
||||
|
||||
memcpy (p->frags, frags, sizeof(p->frags));
|
||||
p->health = actor->health;
|
||||
p->fragcount = fragcount;
|
||||
p->killcount = killcount;
|
||||
p->itemcount = itemcount;
|
||||
|
@ -1366,8 +1385,7 @@ void G_PlayerReborn (int player)
|
|||
p->cls = cls;
|
||||
p->LogText = log;
|
||||
p->cheats |= chasecam;
|
||||
|
||||
p->skill = b_skill; //Added by MC:
|
||||
p->Bot = Bot; //Added by MC:
|
||||
|
||||
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
|
||||
p->original_oldbuttons = ~0;
|
||||
|
@ -1375,15 +1393,19 @@ void G_PlayerReborn (int player)
|
|||
|
||||
if (gamestate != GS_TITLELEVEL)
|
||||
{
|
||||
// [GRB] Give inventory specified in DECORATE
|
||||
actor->GiveDefaultInventory ();
|
||||
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 (p->Bot != NULL)
|
||||
{
|
||||
botskill_t skill = p->Bot->skill;
|
||||
p->Bot->Clear ();
|
||||
p->Bot->player = p;
|
||||
p->Bot->skill = skill;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1658,6 +1680,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;
|
||||
|
@ -2076,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 ();
|
||||
|
||||
|
@ -2085,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;
|
||||
}
|
||||
|
||||
|
@ -2161,6 +2237,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
|
|||
}
|
||||
|
||||
insave = false;
|
||||
I_FreezeTime(false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2373,6 +2450,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]);
|
||||
|
@ -2555,7 +2642,7 @@ void G_DoPlayDemo (void)
|
|||
{
|
||||
FixPathSeperator (defdemoname);
|
||||
DefaultExtension (defdemoname, ".lmp");
|
||||
M_ReadFile (defdemoname, &demobuffer);
|
||||
M_ReadFileMalloc (defdemoname, &demobuffer);
|
||||
}
|
||||
demo_p = demobuffer;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_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_DONTTHRUST))
|
||||
{
|
||||
randVal = pr_foo();
|
||||
if (randVal > 160)
|
||||
|
|
|
@ -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 <filename> <map name>\n");
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD (open)
|
||||
{
|
||||
if (netgame)
|
||||
|
@ -236,6 +276,18 @@ void G_NewInit ()
|
|||
{
|
||||
int i;
|
||||
|
||||
// Destory all old player refrences that may still exist
|
||||
TThinkerIterator<APlayerPawn> 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;
|
||||
|
@ -1112,6 +1164,8 @@ void G_StartTravel ()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bglobal.StartTravel ();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -1183,6 +1237,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)
|
||||
{
|
||||
|
@ -1200,6 +1263,8 @@ void G_FinishTravel ()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bglobal.FinishTravel ();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -1222,6 +1287,7 @@ void G_InitLevelLocals ()
|
|||
level.teamdamage = teamdamage;
|
||||
level.flags = 0;
|
||||
level.flags2 = 0;
|
||||
level.flags3 = 0;
|
||||
|
||||
info = FindLevelInfo (level.MapName);
|
||||
|
||||
|
@ -1275,6 +1341,7 @@ void G_InitLevelLocals ()
|
|||
level.clusterflags = clus ? clus->flags : 0;
|
||||
level.flags |= info->flags;
|
||||
level.flags2 |= info->flags2;
|
||||
level.flags3 |= info->flags3;
|
||||
level.levelnum = info->levelnum;
|
||||
level.Music = info->Music;
|
||||
level.musicorder = info->musicorder;
|
||||
|
|
|
@ -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;
|
||||
|
@ -216,6 +216,9 @@ enum ELevelFlags
|
|||
LEVEL2_ENDGAME = 0x20000000, // This is an epilogue level that cannot be quit.
|
||||
LEVEL2_NOAUTOSAVEHINT = 0x40000000, // tell the game that an autosave for this level does not need to be kept
|
||||
LEVEL2_FORGETSTATE = 0x80000000, // forget this map's state in a hub
|
||||
|
||||
// More flags!
|
||||
LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled
|
||||
};
|
||||
|
||||
|
||||
|
@ -285,6 +288,8 @@ struct level_info_t
|
|||
int sucktime;
|
||||
DWORD flags;
|
||||
DWORD flags2;
|
||||
DWORD flags3;
|
||||
|
||||
FString Music;
|
||||
FString LevelName;
|
||||
SBYTE WallVertLight, WallHorizLight;
|
||||
|
@ -398,6 +403,7 @@ struct FLevelLocals
|
|||
|
||||
DWORD flags;
|
||||
DWORD flags2;
|
||||
DWORD flags3;
|
||||
|
||||
DWORD fadeto; // The color the palette fades to (usually black)
|
||||
DWORD outsidefog; // The fog for sectors with sky ceilings
|
||||
|
|
|
@ -1190,6 +1190,9 @@ enum EMIType
|
|||
MITYPE_SETFLAG2,
|
||||
MITYPE_CLRFLAG2,
|
||||
MITYPE_SCFLAGS2,
|
||||
MITYPE_SETFLAG3,
|
||||
MITYPE_CLRFLAG3,
|
||||
MITYPE_SCFLAGS3,
|
||||
MITYPE_COMPATFLAG,
|
||||
};
|
||||
|
||||
|
@ -1275,6 +1278,7 @@ MapFlagHandlers[] =
|
|||
{ "rememberstate", MITYPE_CLRFLAG2, LEVEL2_FORGETSTATE, 0 },
|
||||
{ "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 },
|
||||
{ "spawnwithweaponraised", MITYPE_SETFLAG2, LEVEL2_PRERAISEWEAPON, 0 },
|
||||
{ "forcefakecontrast", MITYPE_SETFLAG3, LEVEL3_FORCEFAKECONTRAST, 0 },
|
||||
{ "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes
|
||||
{ "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX, 0 },
|
||||
{ "compat_stairs", MITYPE_COMPATFLAG, COMPATF_STAIRINDEX, 0 },
|
||||
|
@ -1306,6 +1310,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 },
|
||||
|
@ -1371,6 +1376,20 @@ void FMapInfoParser::ParseMapDefinition(level_info_t &info)
|
|||
info.flags2 = (info.flags2 & handler->data2) | handler->data1;
|
||||
break;
|
||||
|
||||
case MITYPE_SETFLAG3:
|
||||
info.flags3 |= handler->data1;
|
||||
info.flags3 |= handler->data2;
|
||||
break;
|
||||
|
||||
case MITYPE_CLRFLAG3:
|
||||
info.flags3 &= ~handler->data1;
|
||||
info.flags3 |= handler->data2;
|
||||
break;
|
||||
|
||||
case MITYPE_SCFLAGS3:
|
||||
info.flags3 = (info.flags3 & handler->data2) | handler->data1;
|
||||
break;
|
||||
|
||||
case MITYPE_COMPATFLAG:
|
||||
{
|
||||
int set = 1;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -60,7 +60,8 @@ bool APowerupGiver::Use (bool pickup)
|
|||
}
|
||||
if (BlendColor != 0)
|
||||
{
|
||||
power->BlendColor = BlendColor;
|
||||
if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor;
|
||||
else power->BlendColor = 0;
|
||||
}
|
||||
if (Mode != NAME_None)
|
||||
{
|
||||
|
@ -1296,6 +1297,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 ();
|
||||
|
@ -1384,6 +1397,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_BUDDHA;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerBuddha :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerBuddha::EndEffect ()
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
Owner->player->cheats &= ~CF_BUDDHA;
|
||||
}
|
||||
|
||||
// Scanner powerup ----------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS (APowerScanner)
|
||||
|
|
|
@ -173,6 +173,7 @@ protected:
|
|||
void EndEffect ();
|
||||
void PositionAccuracy ();
|
||||
void Travelled ();
|
||||
bool HandlePickup(AInventory *item);
|
||||
};
|
||||
|
||||
class APowerFrightener : public APowerup
|
||||
|
@ -183,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 )
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "doomstat.h"
|
||||
#include "g_level.h"
|
||||
#include "farchive.h"
|
||||
#include "p_enemy.h"
|
||||
|
||||
static FRandom pr_morphmonst ("MorphMonster");
|
||||
|
||||
|
@ -527,19 +528,26 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
|
|||
if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
|
||||
{
|
||||
AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
|
||||
if ((fakeme->UnmorphTime) &&
|
||||
(fakeme->MorphStyle & MORPH_UNDOBYDEATH) &&
|
||||
(fakeme->UnmorphedMe))
|
||||
AActor *realme = fakeme->UnmorphedMe;
|
||||
if (realme != NULL)
|
||||
{
|
||||
AActor *realme = fakeme->UnmorphedMe;
|
||||
int realstyle = fakeme->MorphStyle;
|
||||
int realhealth = fakeme->health;
|
||||
if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
|
||||
if ((fakeme->UnmorphTime) &&
|
||||
(fakeme->MorphStyle & MORPH_UNDOBYDEATH))
|
||||
{
|
||||
*morphed = realme;
|
||||
*morphedstyle = realstyle;
|
||||
*morphedhealth = realhealth;
|
||||
return true;
|
||||
int realstyle = fakeme->MorphStyle;
|
||||
int realhealth = fakeme->health;
|
||||
if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
|
||||
{
|
||||
*morphed = realme;
|
||||
*morphedstyle = realstyle;
|
||||
*morphedhealth = realhealth;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (realme->flags4 & MF4_BOSSDEATH)
|
||||
{
|
||||
realme->health = 0; // make sure that A_BossDeath considers it dead.
|
||||
CALL_ACTION(A_BossDeath, realme);
|
||||
}
|
||||
}
|
||||
fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
@ -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 (players[i].Bot != NULL && this == players[i].Bot->dest)
|
||||
players[i].Bot->dest = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -1215,7 +1215,11 @@ public:
|
|||
if(Scaled)
|
||||
{
|
||||
if(cx != 0 || cy != 0)
|
||||
{
|
||||
screen->VirtualToRealCoords(dcx, dcy, tmp, tmp, script->resW, script->resH, true);
|
||||
if (cx == 0) dcx = 0;
|
||||
if (cy == 0) dcy = 0;
|
||||
}
|
||||
if(cr != 0 || cb != 0 || clearDontDraw)
|
||||
screen->VirtualToRealCoords(dcr, dcb, tmp, tmp, script->resW, script->resH, true);
|
||||
screen->VirtualToRealCoords(dx, dy, w, h, script->resW, script->resH, true);
|
||||
|
|
|
@ -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 <time.h>
|
||||
|
||||
|
@ -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,51 @@ 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 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)
|
||||
{
|
||||
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 +1030,7 @@ void DrawHUD()
|
|||
if (idmypos) DrawCoordinates(CPlayer);
|
||||
|
||||
DrawTime();
|
||||
DrawLatency();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ int ALoreShot::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|||
{
|
||||
FVector3 thrust;
|
||||
|
||||
if (this->target != NULL)
|
||||
if (this->target != NULL && !(this->target->flags7 & MF7_DONTTHRUST))
|
||||
{
|
||||
thrust.X = float(this->target->x - target->x);
|
||||
thrust.Y = float(this->target->y - target->y);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -601,7 +601,7 @@ private:
|
|||
screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff,
|
||||
DTA_CleanNoMove, true, TAG_DONE);
|
||||
|
||||
if (CPlayer->LogText != NULL)
|
||||
if (CPlayer->LogText.IsNotEmpty())
|
||||
{
|
||||
FBrokenLines *lines = V_BreakLines(SmallFont2, 272, CPlayer->LogText);
|
||||
for (i = 0; lines[i].Width >= 0; ++i)
|
||||
|
|
|
@ -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<<s), \
|
||||
"d,d" (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;
|
||||
}
|
||||
|
|
|
@ -290,6 +290,8 @@ void FMapInfoParser::ParseGameInfo()
|
|||
else gameinfo.mCheatMapArrow = "";
|
||||
}
|
||||
// Insert valid keys here.
|
||||
GAMEINFOKEY_STRING(mCheatKey, "cheatKey")
|
||||
GAMEINFOKEY_STRING(mEasyKey, "easyKey")
|
||||
GAMEINFOKEY_STRING(TitlePage, "titlePage")
|
||||
GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false)
|
||||
GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true)
|
||||
|
|
1
src/gi.h
1
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;
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
#include "d_player.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "gstrings.h"
|
||||
#include "d_net.h"
|
||||
#include "c_dispatch.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
@ -61,7 +63,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 ----------------------------------------------
|
||||
|
||||
|
@ -116,6 +118,8 @@ int STACK_ARGS compareteams (const void *arg1, const void *arg2)
|
|||
return diff;
|
||||
}
|
||||
|
||||
bool SB_ForceActive = false;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
@ -228,7 +232,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 +313,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 +331,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 +341,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 +386,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 +396,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 +422,18 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ())
|
||||
{
|
||||
FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()];
|
||||
|
@ -473,3 +495,8 @@ int HU_GetRowColor(player_t *player, bool highlight)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCMD (togglescoreboard)
|
||||
{
|
||||
SB_ForceActive = !SB_ForceActive;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -134,8 +135,8 @@ struct PreGamePacket
|
|||
};
|
||||
struct
|
||||
{
|
||||
u_long address;
|
||||
u_short port;
|
||||
DWORD address;
|
||||
WORD port;
|
||||
BYTE player;
|
||||
BYTE pad;
|
||||
} machines[MAXNETNODES];
|
||||
|
@ -208,11 +209,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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -654,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;
|
||||
|
@ -822,6 +834,10 @@ bool Guest_WaitForOthers (void *userdata)
|
|||
}
|
||||
}
|
||||
|
||||
packet.Fake = PRE_FAKE;
|
||||
packet.Message = PRE_KEEPALIVE;
|
||||
PreSend(&packet, 2, &sendaddress[1]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -938,11 +954,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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
@ -323,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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -300,7 +300,12 @@ void FListMenuItem::DrawSelector(int xofs, int yofs, FTextureID tex)
|
|||
if ((DMenu::MenuTime%8) < 6)
|
||||
{
|
||||
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
|
||||
mXpos + xofs, mYpos + yofs, "\xd", DTA_Clean, true, TAG_DONE);
|
||||
(mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2,
|
||||
(mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2,
|
||||
"\xd",
|
||||
DTA_CellX, 8 * CleanXfac,
|
||||
DTA_CellY, 8 * CleanYfac,
|
||||
TAG_DONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -247,8 +247,12 @@ static void BuildModesList (int hiwidth, int hiheight, int hi_bits)
|
|||
if (Video != NULL)
|
||||
{
|
||||
while ((haveMode = Video->NextMode (&width, &height, &letterbox)) &&
|
||||
(ratiomatch >= 0 && CheckRatio (width, height) != ratiomatch))
|
||||
ratiomatch >= 0)
|
||||
{
|
||||
int ratio;
|
||||
CheckRatio (width, height, &ratio);
|
||||
if (ratio == ratiomatch)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -298,6 +298,9 @@ xx(Abs)
|
|||
xx(ACS_NamedExecuteWithResult)
|
||||
xx(CallACS)
|
||||
xx(Sqrt)
|
||||
xx(CheckClass)
|
||||
xx(IsPointerEqual)
|
||||
xx(Pick)
|
||||
|
||||
// Various actor names which are used internally
|
||||
xx(MapSpot)
|
||||
|
@ -418,6 +421,7 @@ xx(Passuse)
|
|||
xx(Repeatspecial)
|
||||
xx(Conversation)
|
||||
xx(Locknumber)
|
||||
xx(Midtex3dimpassible)
|
||||
|
||||
xx(Playercross)
|
||||
xx(Playeruse)
|
||||
|
@ -597,4 +601,4 @@ xx(NeverSwitchOnPickup)
|
|||
xx(MoveBob)
|
||||
xx(StillBob)
|
||||
xx(PlayerClass)
|
||||
xx(Wi_NoAutostartMap)
|
||||
xx(Wi_NoAutostartMap)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
979
src/oplsynth/nukedopl3.cpp
Normal file
979
src/oplsynth/nukedopl3.cpp
Normal file
|
@ -0,0 +1,979 @@
|
|||
/*
|
||||
* 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(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, Olli Niemitalo):
|
||||
OPL2 ROMs.
|
||||
*/
|
||||
|
||||
//version 1.5
|
||||
|
||||
/* 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.
|
||||
v1.5:
|
||||
Optimizations
|
||||
*/
|
||||
|
||||
|
||||
/* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "nukedopl3.h"
|
||||
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
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(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,
|
||||
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
|
||||
};
|
||||
|
||||
Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) {
|
||||
if (reg_rate == 0x00) {
|
||||
return 0x00;
|
||||
}
|
||||
Bit8u rate = (reg_rate << 2) + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2));
|
||||
if (rate > 0x3c) {
|
||||
rate = 0x3c;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
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:
|
||||
slot->eg_rate = envelope_calc_rate(slot, slot->reg_ar);
|
||||
break;
|
||||
case envelope_gen_num_decay:
|
||||
slot->eg_rate = envelope_calc_rate(slot, slot->reg_dr);
|
||||
break;
|
||||
case envelope_gen_num_sustain:
|
||||
case envelope_gen_num_release:
|
||||
slot->eg_rate = envelope_calc_rate(slot, slot->reg_rr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 (slot->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) {
|
||||
inc = 8;
|
||||
}
|
||||
else if (eg_incsh[rate_h] > 0) {
|
||||
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][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]);
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
slot->key |= type;
|
||||
}
|
||||
|
||||
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(opl_chip *chip) {
|
||||
if (chip->noise & 0x01) {
|
||||
chip->noise ^= 0x800302;
|
||||
}
|
||||
chip->noise >>= 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Slot
|
||||
//
|
||||
|
||||
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 slot_write40(opl_slot *slot, Bit8u data) {
|
||||
slot->reg_ksl = (data >> 6) & 0x03;
|
||||
slot->reg_tl = data & 0x3f;
|
||||
envelope_update_ksl(slot);
|
||||
}
|
||||
|
||||
void slot_write60(opl_slot *slot, Bit8u data) {
|
||||
slot->reg_ar = (data >> 4) & 0x0f;
|
||||
slot->reg_dr = data & 0x0f;
|
||||
envelope_update_rate(slot);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
else {
|
||||
slot->fbmod = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Channel
|
||||
//
|
||||
|
||||
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 (chip->rhy & 0x01) {
|
||||
eg_keyon(&chip->slot[14], egk_drum);
|
||||
}
|
||||
else {
|
||||
eg_keyoff(&chip->slot[14], egk_drum);
|
||||
}
|
||||
//tc
|
||||
if (chip->rhy & 0x02) {
|
||||
eg_keyon(&chip->slot[17], egk_drum);
|
||||
}
|
||||
else {
|
||||
eg_keyoff(&chip->slot[17], egk_drum);
|
||||
}
|
||||
//tom
|
||||
if (chip->rhy & 0x04) {
|
||||
eg_keyon(&chip->slot[16], egk_drum);
|
||||
}
|
||||
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 chan_writea0(opl_channel *channel, Bit8u data) {
|
||||
if (channel->chip->newm && channel->chtype == ch_4op2) {
|
||||
return;
|
||||
}
|
||||
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 chan_writeb0(opl_channel *channel, Bit8u data) {
|
||||
if (channel->chip->newm && channel->chtype == ch_4op2) {
|
||||
return;
|
||||
}
|
||||
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 chan_setupalg(opl_channel *channel) {
|
||||
if (channel->chtype == ch_drum) {
|
||||
return;
|
||||
}
|
||||
if (channel->alg & 0x08) {
|
||||
return;
|
||||
}
|
||||
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 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 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 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 {
|
||||
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 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 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 (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 {
|
||||
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 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 (channel->chtype == ch_2op || channel->chtype == ch_drum) {
|
||||
eg_keyon(channel->slots[0], egk_norm);
|
||||
eg_keyon(channel->slots[1], egk_norm);
|
||||
}
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bit16s limshort(Bit32s a) {
|
||||
if (a > 32767) {
|
||||
a = 32767;
|
||||
}
|
||||
else if (a < -32768) {
|
||||
a = -32768;
|
||||
}
|
||||
return (Bit16s)a;
|
||||
}
|
||||
|
||||
void NukedOPL3::Reset() {
|
||||
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 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]);
|
||||
}
|
||||
opl3.noise = 0x306600;
|
||||
}
|
||||
|
||||
void NukedOPL3::WriteReg(int reg, int v) {
|
||||
v &= 0xff;
|
||||
reg &= 0x1ff;
|
||||
Bit8u high = (reg >> 8) & 0x01;
|
||||
Bit8u regm = reg & 0xff;
|
||||
switch (regm & 0xf0) {
|
||||
case 0x00:
|
||||
if (high) {
|
||||
switch (regm & 0x0f) {
|
||||
case 0x04:
|
||||
chan_set4op(&opl3, v);
|
||||
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) {
|
||||
slot_write20(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
if (ad_slot[regm & 0x1f] >= 0) {
|
||||
slot_write40(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
|
||||
}
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
if (ad_slot[regm & 0x1f] >= 0) {
|
||||
slot_write60(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
|
||||
}
|
||||
break;
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
if (ad_slot[regm & 0x1f] >= 0) {
|
||||
slot_write80(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);;
|
||||
}
|
||||
break;
|
||||
case 0xe0:
|
||||
case 0xf0:
|
||||
if (ad_slot[regm & 0x1f] >= 0) {
|
||||
slot_writee0(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
|
||||
}
|
||||
break;
|
||||
case 0xa0:
|
||||
if ((regm & 0x0f) < 9) {
|
||||
chan_writea0(&opl3.channel[9 * high + (regm & 0x0f)], v);
|
||||
}
|
||||
break;
|
||||
case 0xb0:
|
||||
if (regm == 0xbd && !high) {
|
||||
opl3.dam = v >> 7;
|
||||
opl3.dvb = (v >> 6) & 0x01;
|
||||
chan_updaterhythm(&opl3, v);
|
||||
}
|
||||
else if ((regm & 0x0f) < 9) {
|
||||
chan_writeb0(&opl3.channel[9 * high + (regm & 0x0f)], v);
|
||||
if (v & 0x20) {
|
||||
chan_enable(&opl3.channel[9 * high + (regm & 0x0f)]);
|
||||
}
|
||||
else {
|
||||
chan_disable(&opl3.channel[9 * high + (regm & 0x0f)]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xc0:
|
||||
if ((regm & 0x0f) < 9) {
|
||||
chan_writec0(&opl3.channel[9 * high + (regm & 0x0f)], v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NukedOPL3::Update(float* sndptr, int numsamples) {
|
||||
Bit32s outa, outb;
|
||||
for (Bit32u i = 0; i < (Bit32u)numsamples; i++) {
|
||||
outa = 0;
|
||||
outb = 0;
|
||||
for (Bit8u ii = 0; ii < 36; ii++) {
|
||||
slot_calgfb(&opl3.slot[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)(accm * opl3.channel[ii].fcha);
|
||||
outb += (Bit16s)(accm * opl3.channel[ii].fchb);
|
||||
}
|
||||
else {
|
||||
outa += (Bit16s)(accm & opl3.channel[ii].cha);
|
||||
outb += (Bit16s)(accm & opl3.channel[ii].chb);
|
||||
}
|
||||
}
|
||||
for (Bit8u ii = 0; ii < 36; ii++) {
|
||||
envelope_calc(&opl3.slot[ii]);
|
||||
pg_generate(&opl3.slot[ii]);
|
||||
}
|
||||
n_generate(&opl3);
|
||||
opl3.timer++;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void NukedOPL3::SetPanning(int c, float left, float right) {
|
||||
if (FullPan) {
|
||||
opl3.channel[c].fcha = left;
|
||||
opl3.channel[c].fchb = right;
|
||||
}
|
||||
}
|
||||
|
||||
NukedOPL3::NukedOPL3(bool stereo) {
|
||||
FullPan = stereo;
|
||||
Reset();
|
||||
}
|
||||
|
||||
OPLEmul *NukedOPL3Create(bool stereo) {
|
||||
return new NukedOPL3(stereo);
|
||||
}
|
234
src/oplsynth/nukedopl3.h
Normal file
234
src/oplsynth/nukedopl3.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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(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, Olli Niemitalo):
|
||||
OPL2 ROMs.
|
||||
*/
|
||||
|
||||
//version 1.5
|
||||
|
||||
#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;
|
||||
|
||||
// Channel types
|
||||
|
||||
enum {
|
||||
ch_2op = 0,
|
||||
ch_4op = 1,
|
||||
ch_4op2 = 2,
|
||||
ch_drum = 3
|
||||
};
|
||||
|
||||
// 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;
|
||||
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;
|
||||
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 opl_chip {
|
||||
opl_channel channel[18];
|
||||
opl_slot slot[36];
|
||||
Bit16u timer;
|
||||
Bit8u newm;
|
||||
Bit8u nts;
|
||||
Bit8u dvb;
|
||||
Bit8u dam;
|
||||
Bit8u rhy;
|
||||
Bit8u vibpos;
|
||||
Bit8u tremval;
|
||||
Bit8u tremtval;
|
||||
Bit8u tremdir;
|
||||
Bit32u noise;
|
||||
Bit16s zeromod;
|
||||
};
|
||||
|
||||
|
||||
class NukedOPL3 : public OPLEmul {
|
||||
private:
|
||||
opl_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);
|
||||
};
|
|
@ -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 */
|
||||
|
|
|
@ -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_bottom<height)
|
||||
{
|
||||
|
@ -499,12 +531,10 @@ void P_Recalculate3DFloors(sector_t * sector)
|
|||
clipped->flags|=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,29 @@ 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<F3DFloor*> & 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));
|
||||
|
||||
IGNORE_FORMAT_PRE
|
||||
Printf("FFloor %d @ top = %f (model = %d), bottom = %f (model = %d), flags = %B, alpha = %d %s %s\n",
|
||||
i, height / 65536., ffloors[i]->top.model->sectornum,
|
||||
bheight / 65536., ffloors[i]->bottom.model->sectornum,
|
||||
ffloors[i]->flags, ffloors[i]->alpha, (ffloors[i]->flags&FF_EXISTS)? "Exists":"", (ffloors[i]->flags&FF_DYNAMIC)? "Dynamic":"");
|
||||
IGNORE_FORMAT_POST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
310
src/p_acs.cpp
310
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);
|
||||
|
@ -1873,7 +1881,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 +2066,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 +2359,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 +2375,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)
|
||||
{
|
||||
|
@ -2385,7 +2393,7 @@ void FBehavior::LoadScriptsDirectory ()
|
|||
int size = LittleLong(scripts.dw[1]);
|
||||
if (size >= 6)
|
||||
{
|
||||
int script_num = LittleShort(scripts.w[4]);
|
||||
int script_num = LittleShort(scripts.sw[4]);
|
||||
ScriptPtr *ptr = const_cast<ScriptPtr *>(FindScript(script_num));
|
||||
if (ptr != NULL)
|
||||
{
|
||||
|
@ -2681,7 +2689,7 @@ BYTE *FBehavior::FindChunk (DWORD id) const
|
|||
{
|
||||
return chunk;
|
||||
}
|
||||
chunk += ((DWORD *)chunk)[1] + 8;
|
||||
chunk += LittleLong(((DWORD *)chunk)[1]) + 8;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2689,14 +2697,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;
|
||||
}
|
||||
|
@ -2881,9 +2889,57 @@ 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 (script)
|
||||
{
|
||||
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;
|
||||
Scripts = NULL;
|
||||
LastScript = NULL;
|
||||
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 +3025,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);
|
||||
|
||||
|
@ -3624,6 +3681,7 @@ enum
|
|||
APROP_AttackZOffset = 40,
|
||||
APROP_StencilColor = 41,
|
||||
APROP_Friction = 42,
|
||||
APROP_DamageMultiplier=43,
|
||||
};
|
||||
|
||||
// These are needed for ACS's APROP_RenderStyle
|
||||
|
@ -3813,6 +3871,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);
|
||||
|
@ -3843,6 +3905,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<APlayerPawn *>(actor)->ViewHeight = value;
|
||||
|
@ -3880,6 +3946,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.
|
||||
|
@ -4224,7 +4291,7 @@ int DLevelScript::DoClassifyActor(int tid)
|
|||
{
|
||||
classify |= ACTOR_VOODOODOLL;
|
||||
}
|
||||
if (actor->player->isbot)
|
||||
if (actor->player->Bot != NULL)
|
||||
{
|
||||
classify |= ACTOR_BOT;
|
||||
}
|
||||
|
@ -4369,6 +4436,13 @@ enum EACSFunctions
|
|||
ACSF_GetArmorInfo,
|
||||
ACSF_DropInventory,
|
||||
ACSF_PickActor,
|
||||
ACSF_IsPointerEqual,
|
||||
ACSF_CanRaiseActor,
|
||||
ACSF_SetActorTeleFog, // 86
|
||||
ACSF_SwapActorTeleFog,
|
||||
ACSF_SetActorRoll,
|
||||
ACSF_ChangeActorRoll,
|
||||
ACSF_GetActorRoll,
|
||||
|
||||
/* Zandronum's - these must be skipped when we reach 99!
|
||||
-100:ResetMap(0),
|
||||
|
@ -4680,6 +4754,103 @@ static void SetActorPitch(AActor *activator, int tid, int angle, bool interpolat
|
|||
}
|
||||
}
|
||||
|
||||
static void SetActorRoll(AActor *activator, int tid, int angle, bool interpolate)
|
||||
{
|
||||
if (tid == 0)
|
||||
{
|
||||
if (activator != NULL)
|
||||
{
|
||||
activator->SetRoll(angle << 16, interpolate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FActorIterator iterator(tid);
|
||||
AActor *actor;
|
||||
|
||||
while ((actor = iterator.Next()))
|
||||
{
|
||||
actor->SetRoll(angle << 16, interpolate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SetActorTeleFog(AActor *activator, int tid, FName telefogsrc, FName telefogdest)
|
||||
{
|
||||
//Simply put, if it doesn't exist, it won't change. One can use "" in this scenario.
|
||||
const PClass *check;
|
||||
if (tid == 0)
|
||||
{
|
||||
if (activator != NULL)
|
||||
{
|
||||
check = PClass::FindClass(telefogsrc);
|
||||
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"))
|
||||
activator->TeleFogDestType = NULL;
|
||||
else
|
||||
activator->TeleFogDestType = check;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FActorIterator iterator(tid);
|
||||
AActor *actor;
|
||||
|
||||
while ((actor = iterator.Next()))
|
||||
{
|
||||
check = PClass::FindClass(telefogsrc);
|
||||
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"))
|
||||
actor->TeleFogDestType = NULL;
|
||||
else
|
||||
actor->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 (actor->TeleFogSourceType == actor->TeleFogDestType)
|
||||
continue; //They're the same. Save the effort.
|
||||
const PClass *temp = actor->TeleFogSourceType;
|
||||
actor->TeleFogSourceType = actor->TeleFogDestType;
|
||||
actor->TeleFogDestType = temp;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const SDWORD *stack, int stackdepth)
|
||||
|
@ -5593,7 +5764,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)
|
||||
{
|
||||
|
@ -5613,19 +5795,87 @@ 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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
// [Nash] Actor roll functions. Let's roll!
|
||||
case ACSF_SetActorRoll:
|
||||
actor = SingleActorFromTID(args[0], activator);
|
||||
if (actor != NULL)
|
||||
{
|
||||
actor->SetRoll(args[1] << 16, false);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case ACSF_ChangeActorRoll:
|
||||
if (argCount >= 2)
|
||||
{
|
||||
SetActorRoll(activator, args[0], args[1], argCount > 2 ? !!args[2] : false);
|
||||
}
|
||||
break;
|
||||
|
||||
case ACSF_GetActorRoll:
|
||||
actor = SingleActorFromTID(args[0], activator);
|
||||
return actor != NULL? actor->roll >> 16 : 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -7242,7 +7492,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]));
|
||||
|
@ -7290,22 +7540,9 @@ scriptwait:
|
|||
break;
|
||||
|
||||
case PCD_PRINTBINARY:
|
||||
#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)))) || defined(__clang__)
|
||||
#define HAS_DIAGNOSTIC_PRAGMA
|
||||
#endif
|
||||
#ifdef HAS_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic push
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic ignored "-Wformat-invalid-specifier"
|
||||
#else
|
||||
#pragma GCC diagnostic ignored "-Wformat="
|
||||
#endif
|
||||
#pragma GCC diagnostic ignored "-Wformat-extra-args"
|
||||
#endif
|
||||
IGNORE_FORMAT_PRE
|
||||
work.AppendFormat ("%B", STACK(1));
|
||||
#ifdef HAS_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
IGNORE_FORMAT_POST
|
||||
--sp;
|
||||
break;
|
||||
|
||||
|
@ -7608,13 +7845,6 @@ scriptwait:
|
|||
AddToConsole (-1, consolecolor);
|
||||
AddToConsole (-1, work);
|
||||
AddToConsole (-1, bar);
|
||||
if (Logfile)
|
||||
{
|
||||
fputs (logbar, Logfile);
|
||||
fputs (work, Logfile);
|
||||
fputs (logbar, Logfile);
|
||||
fflush (Logfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8590,7 +8820,7 @@ scriptwait:
|
|||
}
|
||||
else
|
||||
{
|
||||
STACK(1) = players[STACK(1)].isbot;
|
||||
STACK(1) = (players[STACK(1)].Bot != NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -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)); }
|
||||
|
|
|
@ -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 != NULL)
|
||||
return false; // JDC: bad guys never close doors
|
||||
//Added by MC: Neither do bots.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -3275,13 +3278,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 +3293,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:
|
||||
|
|
|
@ -72,7 +72,8 @@ DECLARE_ACTION(A_FreezeDeathChunks)
|
|||
DECLARE_ACTION(A_BossDeath)
|
||||
|
||||
void A_Chase(AActor *self);
|
||||
void A_FaceTarget (AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270);
|
||||
void A_FaceTarget(AActor *actor, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270);
|
||||
void A_Face(AActor *self, AActor *other, angle_t max_turn = 0, angle_t max_pitch = ANGLE_270);
|
||||
|
||||
bool A_RaiseMobj (AActor *, fixed_t speed);
|
||||
bool A_SinkMobj (AActor *, fixed_t speed);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 != NULL && 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
|
||||
|
@ -606,19 +606,19 @@ 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->isbot)
|
||||
player->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].isbot && this == players[i].enemy)
|
||||
if (players[i].Bot != NULL && 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -938,12 +943,22 @@ 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;
|
||||
bool fakedPain = false;
|
||||
bool forcedPain = false;
|
||||
int fakeDamage = 0;
|
||||
int holdDamage = 0;
|
||||
|
||||
if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
|
||||
{ // Shouldn't happen
|
||||
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)
|
||||
{
|
||||
|
@ -966,13 +981,20 @@ 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)))
|
||||
{
|
||||
return -1;
|
||||
if (fakedPain)
|
||||
{
|
||||
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 +1002,21 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
|
|||
// Players are optionally excluded from getting thrust by damage.
|
||||
if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
|
||||
{
|
||||
return -1;
|
||||
if (fakedPain)
|
||||
plrDontThrust = 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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.
|
||||
fakeDamage = damage;
|
||||
}
|
||||
|
||||
if (inflictor != NULL)
|
||||
{
|
||||
if (inflictor->flags5 & MF5_PIERCEARMOR)
|
||||
|
@ -1010,6 +1042,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,38 +1065,49 @@ 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 ((damage == -1) && (target->player == NULL)) //This isn't meant for the player.
|
||||
{
|
||||
if (fakedPain) //Hold off ending the function before we can deal the pain chances.
|
||||
goto fakepain;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
if (olddam != damage && damage <= 0)
|
||||
|
||||
if (source->Inventory != NULL)
|
||||
{
|
||||
source->Inventory->ModifyDamage(olddam, mod, damage, false);
|
||||
}
|
||||
damage = FixedMul(damage, source->DamageMultiply);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
if (olddam != damage && damage <= 0)
|
||||
{ // Still allow FORCEPAIN
|
||||
if (MustForcePain(target, inflictor))
|
||||
{
|
||||
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 (forcedPain)
|
||||
goto dopain;
|
||||
}
|
||||
else if (fakedPain)
|
||||
goto fakepain;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1071,32 +1115,37 @@ 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);
|
||||
}
|
||||
if (damage <= 0)
|
||||
if (damage <= 0 && target->player == NULL)
|
||||
{ // Still allow FORCEPAIN
|
||||
if (MustForcePain(target, inflictor))
|
||||
{
|
||||
if (forcedPain)
|
||||
goto dopain;
|
||||
}
|
||||
else if (fakedPain)
|
||||
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 (damage == -1 && target->player == NULL) //Make sure it's not a player, the pain has yet to be processed with cheats.
|
||||
{
|
||||
if (fakedPain)
|
||||
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)
|
||||
&& !(target->flags7 & MF7_DONTTHRUST)
|
||||
&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
|
||||
{
|
||||
int kickback;
|
||||
|
@ -1133,11 +1182,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)
|
||||
|
@ -1146,8 +1194,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;
|
||||
|
@ -1193,11 +1240,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 != NULL)
|
||||
{
|
||||
player->allround = true;
|
||||
player->Bot->allround = true;
|
||||
}
|
||||
|
||||
// end of game hell hack
|
||||
|
@ -1210,22 +1256,41 @@ 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;
|
||||
//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;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
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.
|
||||
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)) && (!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2)))
|
||||
{
|
||||
goto dopain;
|
||||
}
|
||||
|
@ -1233,7 +1298,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
|
||||
|
@ -1255,9 +1320,9 @@ 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.
|
||||
// [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.
|
||||
player->mo->health = target->health = player->health = 1;
|
||||
|
@ -1290,7 +1355,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
|
|||
damage = newdam;
|
||||
if (damage <= 0)
|
||||
{
|
||||
return damage;
|
||||
if (fakedPain)
|
||||
goto fakepain;
|
||||
else
|
||||
return damage;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1317,43 +1385,53 @@ 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)
|
||||
{
|
||||
//[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;
|
||||
}
|
||||
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);
|
||||
|
@ -1368,6 +1446,16 @@ 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.
|
||||
|
||||
//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))))
|
||||
{
|
||||
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)) &&
|
||||
(target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY))
|
||||
|
@ -1383,8 +1471,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -1421,6 +1509,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)
|
||||
{
|
||||
|
@ -1459,6 +1548,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 (fakedPain)
|
||||
{
|
||||
return holdDamage; //This is the calculated damage after all is said and done.
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
@ -1577,7 +1674,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;
|
||||
}
|
||||
|
@ -1628,8 +1725,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;
|
||||
}
|
||||
|
@ -1673,7 +1770,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) || (player->mo->flags7 & MF7_BUDDHA)) && damage < TELEFRAG_DAMAGE) || (player->cheats & CF_BUDDHA2))
|
||||
{ // [SP] Save the player...
|
||||
player->health = target->health = 1;
|
||||
}
|
||||
|
@ -1711,7 +1808,6 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage,
|
|||
P_SetMobjState(target, target->info->painstate);
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -519,19 +519,19 @@ FUNC(LS_Generic_Stairs)
|
|||
FUNC(LS_Pillar_Build)
|
||||
// Pillar_Build (tag, speed, height)
|
||||
{
|
||||
return EV_DoPillar (DPillar::pillarBuild, arg0, SPEED(arg1), arg2*FRACUNIT, 0, -1, false);
|
||||
return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, -1, false);
|
||||
}
|
||||
|
||||
FUNC(LS_Pillar_BuildAndCrush)
|
||||
// Pillar_BuildAndCrush (tag, speed, height, crush, crushtype)
|
||||
{
|
||||
return EV_DoPillar (DPillar::pillarBuild, arg0, SPEED(arg1), arg2*FRACUNIT, 0, arg3, CRUSHTYPE(arg4));
|
||||
return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, arg3, CRUSHTYPE(arg4));
|
||||
}
|
||||
|
||||
FUNC(LS_Pillar_Open)
|
||||
// Pillar_Open (tag, speed, f_height, c_height)
|
||||
{
|
||||
return EV_DoPillar (DPillar::pillarOpen, arg0, SPEED(arg1), arg2*FRACUNIT, arg3*FRACUNIT, -1, false);
|
||||
return EV_DoPillar (DPillar::pillarOpen, ln, arg0, SPEED(arg1), arg2*FRACUNIT, arg3*FRACUNIT, -1, false);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerByValue)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -132,7 +133,7 @@ enum EPuffFlags
|
|||
PF_NORANDOMZ = 16
|
||||
};
|
||||
|
||||
AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0);
|
||||
AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL);
|
||||
void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator);
|
||||
void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator);
|
||||
void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator);
|
||||
|
@ -170,7 +171,8 @@ 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);
|
||||
|
||||
//
|
||||
|
@ -557,6 +559,8 @@ enum EDmgFlags
|
|||
DMG_NO_FACTOR = 16,
|
||||
DMG_PLAYERATTACK = 32,
|
||||
DMG_FOILINVUL = 64,
|
||||
DMG_FOILBUDDHA = 128,
|
||||
DMG_NO_PROTECT = 256,
|
||||
};
|
||||
|
||||
|
||||
|
|
252
src/p_map.cpp
252
src/p_map.cpp
|
@ -74,6 +74,69 @@ TArray<line_t *> 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 left shifting
|
||||
// the numerator by 24 bits without overflow.
|
||||
return (fixed_t)((r_num << 24)/r_den);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PIT_FindFloorCeiling
|
||||
|
@ -198,7 +261,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;
|
||||
|
@ -380,7 +443,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;
|
||||
|
@ -734,11 +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
|
||||
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);
|
||||
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,
|
||||
|
@ -825,6 +887,20 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Isolated to keep the code readable and fix the logic
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool CheckRipLevel(AActor *victim, AActor *projectile)
|
||||
{
|
||||
if (victim->RipLevelMin > 0 && projectile->RipperLevel < victim->RipLevelMin) return false;
|
||||
if (victim->RipLevelMax > 0 && projectile->RipperLevel > victim->RipLevelMax) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PIT_CheckThing
|
||||
|
@ -1109,24 +1185,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1142,7 +1221,8 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm)
|
|||
{
|
||||
return true;
|
||||
}
|
||||
if (tm.DoRipping && !(thing->flags5 & MF5_DONTRIP))
|
||||
|
||||
if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && CheckRipLevel(thing, tm.thing))
|
||||
{
|
||||
if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS))
|
||||
{
|
||||
|
@ -1218,6 +1298,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))
|
||||
|
@ -1578,7 +1668,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;
|
||||
}
|
||||
|
@ -1917,13 +2007,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 != NULL && 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;
|
||||
|
@ -1981,13 +2071,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 +2081,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 +2111,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)
|
||||
|
@ -2151,7 +2245,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;
|
||||
}
|
||||
|
@ -2914,18 +3008,24 @@ 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 (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;
|
||||
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;
|
||||
|
@ -3665,6 +3765,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) ||
|
||||
|
@ -3674,7 +3776,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance,
|
|||
puffFlags |= PF_HITTHINGBLEED;
|
||||
|
||||
// We must pass the unreplaced puff type here
|
||||
puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING);
|
||||
puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING, trace.Actor);
|
||||
}
|
||||
|
||||
// Allow puffs to inflict poison damage, so that hitscans can poison, too.
|
||||
|
@ -3686,7 +3788,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.
|
||||
|
@ -3695,7 +3797,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
|
||||
|
@ -4069,7 +4171,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;
|
||||
|
@ -4102,7 +4204,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);
|
||||
}
|
||||
|
@ -4117,13 +4219,18 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i
|
|||
}
|
||||
if (spawnpuff)
|
||||
{
|
||||
P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags);
|
||||
P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags, hitactor);
|
||||
}
|
||||
|
||||
if (puffDefaults && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN)
|
||||
{
|
||||
P_PoisonMobj(hitactor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod, puffDefaults->PoisonDamageType);
|
||||
}
|
||||
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);
|
||||
|
@ -4175,7 +4282,7 @@ CVAR(Float, chase_dist, 90.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|||
|
||||
void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &CameraZ, sector_t *&CameraSector)
|
||||
{
|
||||
fixed_t distance = (fixed_t)(chase_dist * FRACUNIT);
|
||||
fixed_t distance = (fixed_t)(clamp<double>(chase_dist, 0, 30000) * FRACUNIT);
|
||||
angle_t angle = (t1->angle - ANG180) >> ANGLETOFINESHIFT;
|
||||
angle_t pitch = (angle_t)(t1->pitch) >> ANGLETOFINESHIFT;
|
||||
FTraceResults trace;
|
||||
|
@ -4185,7 +4292,7 @@ void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &Camera
|
|||
vy = FixedMul(finecosine[pitch], finesine[angle]);
|
||||
vz = finesine[pitch];
|
||||
|
||||
sz = t1->z - t1->floorclip + t1->height + (fixed_t)(chase_height * FRACUNIT);
|
||||
sz = t1->z - t1->floorclip + t1->height + (fixed_t)(clamp<double>(chase_height, -1000, 1000) * FRACUNIT);
|
||||
|
||||
if (Trace(t1->x, t1->y, sz, t1->Sector,
|
||||
vx, vy, vz, distance, 0, 0, NULL, trace) &&
|
||||
|
@ -4661,7 +4768,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
|
|||
points *= thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT) / (double)FRACUNIT;
|
||||
|
||||
// points and bombdamage should be the same sign
|
||||
if ((points * bombdamage) > 0 && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
|
||||
if (((bombspot->flags7 & MF7_CAUSEPAIN) || (points * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
|
||||
{ // OK to damage; target is in direct path
|
||||
double velz;
|
||||
double thrust;
|
||||
|
@ -4670,7 +4777,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_DONTTHRUST)))
|
||||
thing->flags2 |= MF2_BLASTED;
|
||||
|
||||
if (!(thing->flags & MF_ICECORPSE))
|
||||
|
@ -4682,25 +4789,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_DONTTHRUST))
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5014,6 +5125,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)
|
||||
|
@ -5022,7 +5135,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))
|
||||
|
|
264
src/p_mobj.cpp
264
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)
|
||||
|
||||
|
@ -252,9 +251,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
|
||||
|
@ -309,8 +312,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)
|
||||
|
@ -322,6 +333,17 @@ void AActor::Serialize (FArchive &arc)
|
|||
{
|
||||
arc << FriendPlayer;
|
||||
}
|
||||
if (SaveVersion >= 4517)
|
||||
{
|
||||
arc << TeleFogSourceType
|
||||
<< TeleFogDestType;
|
||||
}
|
||||
if (SaveVersion >= 4518)
|
||||
{
|
||||
arc << RipperLevel
|
||||
<< RipLevelMin
|
||||
<< RipLevelMax;
|
||||
}
|
||||
|
||||
{
|
||||
FString tagstr;
|
||||
|
@ -1163,7 +1185,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;
|
||||
}
|
||||
|
@ -1191,17 +1213,14 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target)
|
|||
|
||||
if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) )
|
||||
{
|
||||
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);
|
||||
}
|
||||
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.
|
||||
|
@ -1276,8 +1295,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)
|
||||
{
|
||||
|
@ -1310,11 +1338,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1651,6 +1674,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;
|
||||
|
@ -1940,20 +1964,55 @@ 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;
|
||||
bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle));
|
||||
// Change angle for deflection/reflection
|
||||
|
||||
// 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;
|
||||
if (!dontReflect)
|
||||
{
|
||||
bool tg = (mo->target != NULL);
|
||||
bool blockingtg = (BlockingMobj->target != NULL);
|
||||
if (BlockingMobj->flags7 & MF7_AIMREFLECT && (tg || blockingtg))
|
||||
{
|
||||
AActor *origin;
|
||||
if (tg)
|
||||
origin = mo->target;
|
||||
else if (blockingtg)
|
||||
origin = BlockingMobj->target;
|
||||
|
||||
float speed = (float)(mo->Speed);
|
||||
//dest->x - source->x
|
||||
FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z);
|
||||
velocity.Resize(speed);
|
||||
angle = mo->angle >> ANGLETOFINESHIFT;
|
||||
mo->velx = (fixed_t)(velocity.X);
|
||||
mo->vely = (fixed_t)(velocity.Y);
|
||||
mo->velz = (fixed_t)(velocity.Z);
|
||||
/*
|
||||
mo->velx = FixedMul(mo->Speed, finecosine[angle]);
|
||||
mo->vely = FixedMul(mo->Speed, finesine[angle]);
|
||||
mo->velz = -mo->velz;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto explode;
|
||||
}
|
||||
}
|
||||
if (mo->flags2 & MF2_SEEKERMISSILE)
|
||||
{
|
||||
mo->tracer = mo->target;
|
||||
|
@ -2634,18 +2693,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 ();
|
||||
|
@ -2884,9 +2935,12 @@ 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;
|
||||
|
||||
if (thing->flags7 & MF7_MIRRORREFLECT)
|
||||
angle += ANGLE_180;
|
||||
// Change angle for reflection
|
||||
if (thing->flags4&MF4_SHIELDREFLECT)
|
||||
else if (thing->flags4&MF4_SHIELDREFLECT)
|
||||
{
|
||||
// Shield reflection (from the Centaur
|
||||
if (abs (angle - thing->angle)>>24 > 45)
|
||||
|
@ -2909,6 +2963,13 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
|
|||
else
|
||||
angle -= ANG45;
|
||||
}
|
||||
else if (thing->flags7 & MF7_AIMREFLECT)
|
||||
{
|
||||
if (this->target != NULL)
|
||||
A_Face(this, this->target);
|
||||
else if (thing->target != NULL)
|
||||
A_Face(this, thing->target);
|
||||
}
|
||||
else
|
||||
angle += ANGLE_1 * ((pr_reflect()%16)-8);
|
||||
return false;
|
||||
|
@ -3016,6 +3077,18 @@ void AActor::SetAngle(angle_t ang, bool interpolate)
|
|||
}
|
||||
}
|
||||
|
||||
void AActor::SetRoll(angle_t r, bool interpolate)
|
||||
{
|
||||
if (r != roll)
|
||||
{
|
||||
roll = r;
|
||||
if (player != NULL && interpolate)
|
||||
{
|
||||
player->cheats |= CF_INTERPVIEW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// P_MobjThinker
|
||||
//
|
||||
|
@ -3112,7 +3185,7 @@ void AActor::Tick ()
|
|||
special2++;
|
||||
}
|
||||
//Added by MC: Freeze mode.
|
||||
if (bglobal.freeze && !(player && !player->isbot))
|
||||
if (bglobal.freeze && !(player && player->Bot == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -3226,40 +3299,40 @@ void AActor::Tick ()
|
|||
}
|
||||
}
|
||||
|
||||
if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback &&
|
||||
if (bglobal.botnum && !demoplayback &&
|
||||
((flags & (MF_SPECIAL|MF_MISSILE)) || (flags3 & MF3_ISMONSTER)))
|
||||
{
|
||||
BotSupportCycles.Clock();
|
||||
bglobal.m_Thinking = true;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || !players[i].isbot)
|
||||
if (!playeringame[i] || players[i].Bot == NULL)
|
||||
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)
|
||||
{ //Item pickup time
|
||||
//clock (BotWTG);
|
||||
bglobal.WhatToGet (players[i].mo, this);
|
||||
players[i].Bot->WhatToGet (this);
|
||||
//unclock (BotWTG);
|
||||
BotWTG++;
|
||||
}
|
||||
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;
|
||||
if (target != players[i].mo && players[i].Bot->Check_LOS (this, ANGLE_90))
|
||||
players[i].Bot->missile = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3866,6 +3939,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
|
||||
|
@ -4246,6 +4320,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)
|
||||
|
@ -4286,12 +4366,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)
|
||||
{
|
||||
|
@ -4305,14 +4388,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<APlayerPawn *>
|
||||
(Spawn (p->cls, spawn_x, spawn_y, spawn_z, NO_REPLACE));
|
||||
|
@ -4358,9 +4441,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))
|
||||
{
|
||||
|
@ -4432,7 +4512,8 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
|
|||
{
|
||||
APowerup *invul = static_cast<APowerup*>(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
|
||||
}
|
||||
|
||||
|
@ -4868,7 +4949,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
|
|||
// P_SpawnPuff
|
||||
//
|
||||
|
||||
AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags)
|
||||
AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict)
|
||||
{
|
||||
AActor *puff;
|
||||
|
||||
|
@ -4878,9 +4959,24 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t
|
|||
puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE);
|
||||
if (puff == NULL) return NULL;
|
||||
|
||||
if ((puff->flags4 & MF4_RANDOMIZE) && puff->tics > 0)
|
||||
{
|
||||
puff->tics -= pr_spawnpuff() & 3;
|
||||
if (puff->tics < 1)
|
||||
puff->tics = 1;
|
||||
}
|
||||
|
||||
//Moved puff creation and target/master/tracer setting to here.
|
||||
if (puff && vict)
|
||||
{
|
||||
if (puff->flags7 & MF7_HITTARGET) puff->target = vict;
|
||||
if (puff->flags7 & MF7_HITMASTER) puff->master = vict;
|
||||
if (puff->flags7 & MF7_HITTRACER) puff->tracer = vict;
|
||||
}
|
||||
// [BB] If the puff came from a player, set the target of the puff to this player.
|
||||
if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER))
|
||||
puff->target = source;
|
||||
|
||||
|
||||
if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y);
|
||||
|
||||
|
@ -5011,10 +5107,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);
|
||||
}
|
||||
|
@ -5850,22 +5947,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;
|
||||
}
|
||||
|
||||
|
@ -5961,7 +6077,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;
|
||||
}
|
||||
|
|
|
@ -212,16 +212,28 @@ DPillar::DPillar (sector_t *sector, EPillar type, fixed_t speed,
|
|||
}
|
||||
}
|
||||
|
||||
bool EV_DoPillar (DPillar::EPillar type, int tag, fixed_t speed, fixed_t height,
|
||||
fixed_t height2, int crush, bool hexencrush)
|
||||
bool EV_DoPillar (DPillar::EPillar type, line_t *line, int tag,
|
||||
fixed_t speed, fixed_t height, fixed_t height2, int crush, bool hexencrush)
|
||||
{
|
||||
int secnum;
|
||||
sector_t *sec;
|
||||
bool rtn = false;
|
||||
int secnum = -1;
|
||||
|
||||
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
|
||||
// check if a manual trigger; if so do just the sector on the backside
|
||||
if (tag == 0)
|
||||
{
|
||||
sector_t *sec = §ors[secnum];
|
||||
if (!line || !(sec = line->backsector))
|
||||
return rtn;
|
||||
secnum = (int)(sec-sectors);
|
||||
goto manual_pillar;
|
||||
}
|
||||
|
||||
secnum = -1;
|
||||
while (tag && (secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
|
||||
{
|
||||
sec = §ors[secnum];
|
||||
|
||||
manual_pillar:
|
||||
if (sec->PlaneMoving(sector_t::floor) || sec->PlaneMoving(sector_t::ceiling))
|
||||
continue;
|
||||
|
||||
|
|
|
@ -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 == 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->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->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->isbot && bot_observer)
|
||||
if (player->Bot == NULL && bot_observer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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 != NULL)
|
||||
{
|
||||
botinfo_t *thebot = bglobal.botinfo;
|
||||
while (thebot && stricmp (name, thebot->name))
|
||||
|
@ -280,10 +280,9 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name)
|
|||
}
|
||||
if (thebot)
|
||||
{
|
||||
thebot->inuse = true;
|
||||
thebot->inuse = BOTINUSE_Yes;
|
||||
}
|
||||
bglobal.botnum++;
|
||||
bglobal.botingame[dst - players] = true;
|
||||
dst->userinfo.TransferFrom(uibackup2);
|
||||
}
|
||||
else
|
||||
|
@ -567,7 +566,7 @@ void P_SerializePolyobjs (FArchive &arc)
|
|||
I_Error ("UnarchivePolyobjs: Invalid polyobj tag");
|
||||
}
|
||||
arc << angle;
|
||||
po->RotatePolyobj (angle);
|
||||
po->RotatePolyobj (angle, true);
|
||||
arc << deltaX << deltaY << po->interpolation;
|
||||
deltaX -= po->StartSpot.x;
|
||||
deltaY -= po->StartSpot.y;
|
||||
|
|
|
@ -965,7 +965,7 @@ int side_t::GetLightLevel (bool foggy, int baselight, bool noabsolute, int *pfak
|
|||
*pfakecontrast = 0;
|
||||
}
|
||||
|
||||
if (!foggy) // Don't do relative lighting in foggy sectors
|
||||
if (!foggy || level.flags3 & LEVEL3_FORCEFAKECONTRAST) // Don't do relative lighting in foggy sectors
|
||||
{
|
||||
if (!(Flags & WALLF_NOFAKECONTRAST) && r_fakecontrast != 0)
|
||||
{
|
||||
|
|
|
@ -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,48 @@ 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 &&
|
||||
line->special != Teleport)
|
||||
{
|
||||
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
|
||||
|
@ -671,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)
|
||||
{
|
||||
|
@ -680,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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
@ -514,8 +515,8 @@ private:
|
|||
DPillar ();
|
||||
};
|
||||
|
||||
bool EV_DoPillar (DPillar::EPillar type, int tag, fixed_t speed, fixed_t height,
|
||||
fixed_t height2, int crush, bool hexencrush);
|
||||
bool EV_DoPillar (DPillar::EPillar type, line_t *line, int tag,
|
||||
fixed_t speed, fixed_t height, fixed_t height2, int crush, bool hexencrush);
|
||||
|
||||
//
|
||||
// P_DOORS
|
||||
|
@ -902,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 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -74,19 +74,22 @@ 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 setTarget)
|
||||
{
|
||||
const PClass *fog = P_GetSpawnableType(spawnid);
|
||||
|
||||
if (fog == NULL)
|
||||
AActor *mo;
|
||||
if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL)
|
||||
{
|
||||
AActor *mo = Spawn ("TeleportFog", x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE);
|
||||
//Do nothing.
|
||||
mo = NULL;
|
||||
}
|
||||
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((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType), x, y, z, ALLOW_REPLACE);
|
||||
}
|
||||
|
||||
if (mo != NULL && setTarget)
|
||||
mo->target = mobj;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -96,6 +99,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,19 +186,20 @@ 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<ATeleportFog> (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE);
|
||||
fog->target = thing;
|
||||
P_SpawnTeleportFog(thing, oldx, oldy, oldz, true, true); //Passes the actor through which then pulls the TeleFog metadate types based on properties.
|
||||
}
|
||||
if (useFog)
|
||||
{
|
||||
fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
|
||||
an = angle >> ANGLETOFINESHIFT;
|
||||
AActor *fog = Spawn<ATeleportFog> (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;
|
||||
P_SpawnTeleportFog(thing, x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, false, true);
|
||||
|
||||
}
|
||||
if (thing->player)
|
||||
{
|
||||
// [RH] Zoom player's field of vision
|
||||
|
@ -226,7 +232,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 +282,7 @@ static AActor *SelectTeleDest (int tid, int tag)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (count != 1)
|
||||
if (count != 1 && !norandom)
|
||||
{
|
||||
count = 1 + (pr_teleport() % count);
|
||||
}
|
||||
|
@ -323,6 +329,7 @@ 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)
|
||||
{
|
||||
|
||||
AActor *searcher;
|
||||
fixed_t z;
|
||||
angle_t angle = 0;
|
||||
|
@ -334,6 +341,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;
|
||||
|
@ -342,7 +350,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 +398,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 ();
|
||||
}
|
||||
|
|
|
@ -49,11 +49,13 @@ public:
|
|||
|
||||
WORD operator [](FTextureID tex) const
|
||||
{
|
||||
if ((unsigned)tex.GetIndex() >= Types.Size()) return DefaultTerrainType;
|
||||
WORD type = Types[tex.GetIndex()];
|
||||
return type == 0xffff? DefaultTerrainType : type;
|
||||
}
|
||||
WORD operator [](int texnum) const
|
||||
{
|
||||
if ((unsigned)texnum >= Types.Size()) return DefaultTerrainType;
|
||||
WORD type = Types[texnum];
|
||||
return type == 0xffff? DefaultTerrainType : type;
|
||||
}
|
||||
|
|
|
@ -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<ATeleportFog> (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<ATeleportFog> (x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE);
|
||||
Spawn<ATeleportFog> (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;
|
||||
|
@ -402,13 +402,17 @@ 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<AInventory*>(actor)->Owner != NULL) return;
|
||||
|
||||
// be friendly to the level statistics. ;)
|
||||
actor->ClearCounters();
|
||||
actor->Destroy ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool P_Thing_Raise(AActor *thing)
|
||||
bool P_Thing_Raise(AActor *thing, AActor *raiser)
|
||||
{
|
||||
FState * RaiseState = thing->GetRaiseState();
|
||||
if (RaiseState == NULL)
|
||||
|
@ -441,10 +445,50 @@ 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;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue