Merge remote-tracking branch 'zdoom/master' into openal

Conflicts:
	output_sdl/CMakeLists.txt
	src/namedef.h
This commit is contained in:
Chris Robinson 2015-01-15 13:08:05 -08:00
commit a8348b13de
192 changed files with 19866 additions and 10872 deletions

180
FindSDL2.cmake Normal file
View 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)

View file

@ -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 )

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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|

View file

@ -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];

View file

@ -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++;
}

View file

@ -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__

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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");
}
//-----------------------------------------------------------------------------

View file

@ -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'));

View file

@ -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;

View file

@ -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);

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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;
}
}
}
}

View file

@ -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] == '/' &&

View file

@ -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());
}

View file

@ -61,6 +61,7 @@ typedef enum
ga_loadlevel,
ga_newgame,
ga_newgame2,
ga_recordgame,
ga_loadgame,
ga_loadgamehidecon,
ga_loadgameplaydemo,

View file

@ -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.

View file

@ -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())

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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
};

View file

@ -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

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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 )

View file

@ -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()

View file

@ -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;
}
}

View file

@ -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
};

View file

@ -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");
}

View file

@ -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);

View file

@ -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
{

View file

@ -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;
}

View file

@ -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:

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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;
}

View file

@ -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)

View file

@ -169,6 +169,7 @@ struct gameinfo_t
int TextScreenY;
FName DefaultEndSequence;
FString mMapArrow, mCheatMapArrow;
FString mEasyKey, mCheatKey;
FGIFont mStatscreenMapNameFont;
FGIFont mStatscreenFinishedFont;
FGIFont mStatscreenEnteringFont;

View file

@ -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;
}

View file

@ -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);

View file

@ -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)
{

View file

@ -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());

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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
View 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
View 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);
};

View file

@ -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 */

View file

@ -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 = &sectors[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
}
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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)); }

View file

@ -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.

View file

@ -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:

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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,
};

View file

@ -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))

View file

@ -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;
}

View file

@ -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 = &sectors[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 = &sectors[secnum];
manual_pillar:
if (sec->PlaneMoving(sector_t::floor) || sec->PlaneMoving(sector_t::ceiling))
continue;

View file

@ -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;
}

View file

@ -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;

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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);

View file

@ -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;
}

View file

@ -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 ();
}

View file

@ -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;
}

View file

@ -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