Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb9b7b8862 | ||
|
|
182bd93648 | ||
|
|
cef716e5cc | ||
|
|
52126feb40 | ||
|
|
73262ae207 | ||
|
|
75d2d98a46 | ||
|
|
6a30ccbef1 | ||
|
|
350651dae6 | ||
|
|
6ab7b3e772 | ||
|
|
e2f3ae66f8 | ||
|
|
18c80a0ff8 | ||
|
|
62e20b1cf9 | ||
|
|
93d8e12782 | ||
|
|
8c774722eb | ||
|
|
a937ff5c80 | ||
|
|
e466bbacb8 | ||
|
|
34a1e32843 | ||
|
|
43e2ab896b | ||
|
|
e0424bed65 | ||
|
|
c5acdcd524 | ||
|
|
f521a03322 | ||
|
|
4b86fe2aa7 | ||
|
|
ba470e8727 | ||
|
|
466c875f8a | ||
|
|
1749a2ba09 | ||
|
|
831c3f49bf | ||
|
|
9454e11fd9 | ||
|
|
215599684c | ||
|
|
8df996f452 | ||
|
|
d99fbb092e | ||
|
|
4192b12c29 | ||
|
|
10ff1143cc | ||
|
|
2d6f2c14e5 | ||
|
|
17f11bd428 | ||
|
|
6aa6472667 | ||
|
|
ab3f3bd12b | ||
|
|
3109a8ea75 | ||
|
|
2162cac977 | ||
|
|
8e43d06e84 | ||
|
|
31530bef18 | ||
|
|
3be26282b3 | ||
|
|
f561cadf63 | ||
|
|
1391eeba81 | ||
|
|
ea32c08c4f | ||
|
|
80947888a0 | ||
|
|
6486589c4a | ||
|
|
3c6e1c9d47 | ||
|
|
cfaab257ed | ||
|
|
4ec30cf472 | ||
|
|
e7ddebd16c | ||
|
|
7003b01e5d | ||
|
|
9f0ae0434e | ||
|
|
1e43bdd5fa | ||
|
|
40672845e4 | ||
|
|
22603aa126 | ||
|
|
dfe1665ba1 | ||
|
|
e80010e3c6 | ||
|
|
803ca14ef2 | ||
|
|
44d5fd5097 | ||
|
|
69fd91f003 | ||
|
|
42226a14c9 | ||
|
|
88f835d068 | ||
|
|
66a868083a | ||
|
|
c1c74878df | ||
|
|
14a8d64b67 | ||
|
|
acd1ad34b2 | ||
|
|
7feba1e947 | ||
|
|
e9a4791322 | ||
|
|
a2567c1fea | ||
|
|
367f4538a3 | ||
|
|
54a217c27b | ||
|
|
6f00b57eb9 | ||
|
|
1a610ba831 | ||
|
|
eed3662592 | ||
|
|
5e42f37fba | ||
|
|
8017734de4 | ||
|
|
c93e11ee6b | ||
|
|
5d7d7c0822 | ||
|
|
a4c6165094 | ||
|
|
cfe2691892 | ||
|
|
4183e7f0bf | ||
|
|
e283a643b3 | ||
|
|
97aea20586 | ||
|
|
5461483bb5 | ||
|
|
0cb75ecf7f | ||
|
|
2d2ca0ab2a | ||
|
|
fdf1f6c9f1 | ||
|
|
2d0da712e3 | ||
|
|
d23444b983 | ||
|
|
3f7526ba12 | ||
|
|
cdf0216136 | ||
|
|
cc06d35bc7 | ||
|
|
0f88e43ba2 | ||
|
|
b20e13ee97 | ||
|
|
a626572da3 | ||
|
|
29c84c37c8 | ||
|
|
6039f843a7 | ||
|
|
8f34829b1e | ||
|
|
0a2d6847ba | ||
|
|
43af97127b | ||
|
|
d8ee1b9bfe | ||
|
|
583262d221 | ||
|
|
9acd3dbc9b | ||
|
|
5ffbab8208 | ||
|
|
a6e2a433c4 | ||
|
|
ad4a17c2f2 | ||
|
|
4db4e5fbcb | ||
|
|
0dc6006937 | ||
|
|
e81c6c9d37 | ||
|
|
b25cf3f0d0 | ||
|
|
3400ea4576 | ||
|
|
389122182e | ||
|
|
de13d4e326 | ||
|
|
4e8dbd0b86 | ||
|
|
919b537e28 | ||
|
|
95007c9253 | ||
|
|
37198f1b99 | ||
|
|
64c3f2031b | ||
|
|
3b7dc0dae2 | ||
|
|
2229f9b70e | ||
|
|
545af17b3b | ||
|
|
200a7dbf79 | ||
|
|
8ab50ea7b7 | ||
|
|
f3e4211226 | ||
|
|
16b527e3cf | ||
|
|
683204519c | ||
|
|
ecdf802d68 | ||
|
|
dc00dbde0d | ||
|
|
862fe13dcd | ||
|
|
6c299ed103 | ||
|
|
fc1975a607 | ||
|
|
e61bbd634c | ||
|
|
917b68d630 | ||
|
|
46d3ae324c | ||
|
|
d27740bd38 | ||
|
|
cfd30419c2 | ||
|
|
3ec96b84ad | ||
|
|
34cb964ea5 | ||
|
|
5789492021 | ||
|
|
38cf08e298 | ||
|
|
5cd01807b2 | ||
|
|
57af3af800 | ||
|
|
8e07b7fc3f | ||
|
|
0076f8947c | ||
|
|
43ce86571c | ||
|
|
4188cd3455 | ||
|
|
685dfe78c2 | ||
|
|
348d79ef38 | ||
|
|
9088c44b3e | ||
|
|
06b760e8dd | ||
|
|
d06aa1c736 | ||
|
|
787c623cfe | ||
|
|
b7bf1563e5 | ||
|
|
f56abf0596 | ||
|
|
a7e3503e2c | ||
|
|
2ee40560ee | ||
|
|
5682640e36 | ||
|
|
7ec2ab9a76 | ||
|
|
82d4d8719f | ||
|
|
f302687c7f | ||
|
|
fd973f049c | ||
|
|
5947727f80 | ||
|
|
69ecce88df | ||
|
|
43593b168d | ||
|
|
d80074b9b6 | ||
|
|
69027eca53 | ||
|
|
ac289c7f48 | ||
|
|
87e44b700b | ||
|
|
48721e5811 | ||
|
|
7ee508118c | ||
|
|
8c4f38c0af | ||
|
|
167a2c2bd1 | ||
|
|
f1c6c48b36 | ||
|
|
8d2745fc33 | ||
|
|
5e03978cd7 | ||
|
|
261457a959 | ||
|
|
93de90b680 | ||
|
|
dc5915b4f8 | ||
|
|
c3b6daefc9 | ||
|
|
81c2034a16 | ||
|
|
8a421a2623 | ||
|
|
a281000308 | ||
|
|
03deda2f9d | ||
|
|
ba5a0f3044 | ||
|
|
b37f5d6d76 | ||
|
|
593b4d161c | ||
|
|
7c29c05d64 | ||
|
|
89f4b6d535 | ||
|
|
373351c2ba | ||
|
|
65037e0e69 | ||
|
|
e2a2037a99 | ||
|
|
b843e9a6cb | ||
|
|
b4cb827d73 | ||
|
|
22ce8ac538 | ||
|
|
625a6e7498 | ||
|
|
fd9de493e6 | ||
|
|
4c196a9290 | ||
|
|
c63c6701ac | ||
|
|
8bae7a5b05 | ||
|
|
af5a70785e | ||
|
|
c5b7c0ad16 | ||
|
|
28e2417ef9 | ||
|
|
2fe6d6d33a | ||
|
|
e0638c598d | ||
|
|
9a10d72e1f | ||
|
|
a09ea75d80 | ||
|
|
10c83e8bf5 | ||
|
|
d5b44e44e1 | ||
|
|
dcd488c48c | ||
|
|
db08631ab9 | ||
|
|
bad55d49cf | ||
|
|
98f234fce3 | ||
|
|
55984fbb08 | ||
|
|
5c3e9fea4c | ||
|
|
debe52c1e0 | ||
|
|
1c5256a4c6 | ||
|
|
d594f5fdb7 | ||
|
|
49f6132d23 | ||
|
|
6ff457eb68 | ||
|
|
5b9a1ff95d | ||
|
|
232f24a2a2 | ||
|
|
efc56e82d9 |
No files matched your search
@@ -0,0 +1,23 @@
|
||||
name: Make Release Builds
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
runs-on: macos-12
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 13
|
||||
|
||||
- run: bash build-mac-app.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mac-build
|
||||
path: SpaceCadetPinball-*-mac.dmg
|
||||
@@ -21,6 +21,9 @@ bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
ARM/
|
||||
ARM64/
|
||||
win32/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
@@ -263,3 +266,37 @@ __pycache__/
|
||||
/Export
|
||||
/DrMem
|
||||
/Doc private
|
||||
# Windows and macOS local libraries
|
||||
/Libs
|
||||
|
||||
#CMake generated
|
||||
out/
|
||||
/cmake-build-debug
|
||||
|
||||
# Windows XP stuff
|
||||
DebugWinXp/
|
||||
ReleaseWinXp/
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
### Ninja ###
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
build.ninja
|
||||
# Build directory
|
||||
/build
|
||||
@@ -0,0 +1,20 @@
|
||||
#Run this script from Developer Command Prompt for VS *
|
||||
$artefacts = ".\bin\Release\SpaceCadetPinball.exe", ".\bin\Release\SDL2.dll", ".\bin\Release\SDL2_mixer.dll"
|
||||
|
||||
#X86 build
|
||||
Remove-Item -Path .\build\CMakeCache.txt -ErrorAction SilentlyContinue
|
||||
cmake -S . -B build -A Win32 -DCMAKE_WIN32_EXECUTABLE:BOOL=1
|
||||
cmake --build build --config Release
|
||||
Compress-Archive -Path $artefacts -DestinationPath ".\bin\SpaceCadetPinballx86Win.zip" -Force
|
||||
|
||||
#X64 build
|
||||
Remove-Item -Path .\build\CMakeCache.txt
|
||||
cmake -S . -B build -A x64 -DCMAKE_WIN32_EXECUTABLE:BOOL=1
|
||||
cmake --build build --config Release
|
||||
Compress-Archive -Path $artefacts -DestinationPath ".\bin\SpaceCadetPinballx64Win.zip" -Force
|
||||
|
||||
#86 XP build, requires special XP MSVC toolset
|
||||
Remove-Item -Path .\build\CMakeCache.txt
|
||||
cmake -S . -B build -A Win32 -DCMAKE_WIN32_EXECUTABLE:BOOL=1 -T v141_xp
|
||||
cmake --build build --config Release
|
||||
Compress-Archive -Path $artefacts -DestinationPath ".\bin\SpaceCadetPinballx86WinXP.zip" -Force
|
||||
@@ -0,0 +1,247 @@
|
||||
cmake_minimum_required(VERSION 3.0...3.5)
|
||||
project(SpaceCadetPinball)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/bin)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMakeModules")
|
||||
|
||||
# On Windows, set paths to SDL-devel packages here
|
||||
if(WIN32)
|
||||
list(APPEND SDL2_PATH "${CMAKE_CURRENT_LIST_DIR}/Libs/SDL2")
|
||||
list(APPEND SDL2_MIXER_PATH "${CMAKE_CURRENT_LIST_DIR}/Libs/SDL2_mixer")
|
||||
endif()
|
||||
|
||||
# Link mingw libs static
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(MACOSX_RPATH)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH true)
|
||||
set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||
list(APPEND SDL2_PATH "${CMAKE_CURRENT_LIST_DIR}/Libs")
|
||||
list(APPEND SDL2_MIXER_PATH "${CMAKE_CURRENT_LIST_DIR}/Libs")
|
||||
endif()
|
||||
|
||||
# SDL2main is not needed
|
||||
set(SDL2_BUILDING_LIBRARY ON)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
FIND_PACKAGE(SDL2_mixer REQUIRED)
|
||||
|
||||
include_directories(${SDL2_INCLUDE_DIR} ${SDL2_MIXER_INCLUDE_DIR})
|
||||
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
|
||||
foreach(dir ${dirs})
|
||||
message(STATUS "Include dir='${dir}'")
|
||||
endforeach()
|
||||
|
||||
|
||||
|
||||
set(SOURCE_FILES
|
||||
SpaceCadetPinball/control.cpp
|
||||
SpaceCadetPinball/control.h
|
||||
SpaceCadetPinball/EmbeddedData.cpp
|
||||
SpaceCadetPinball/EmbeddedData.h
|
||||
SpaceCadetPinball/font_selection.cpp
|
||||
SpaceCadetPinball/font_selection.h
|
||||
SpaceCadetPinball/fullscrn.cpp
|
||||
SpaceCadetPinball/fullscrn.h
|
||||
SpaceCadetPinball/gdrv.cpp
|
||||
SpaceCadetPinball/gdrv.h
|
||||
SpaceCadetPinball/GroupData.cpp
|
||||
SpaceCadetPinball/GroupData.h
|
||||
SpaceCadetPinball/high_score.cpp
|
||||
SpaceCadetPinball/high_score.h
|
||||
SpaceCadetPinball/loader.cpp
|
||||
SpaceCadetPinball/loader.h
|
||||
SpaceCadetPinball/maths.cpp
|
||||
SpaceCadetPinball/maths.h
|
||||
SpaceCadetPinball/midi.cpp
|
||||
SpaceCadetPinball/midi.h
|
||||
SpaceCadetPinball/nudge.cpp
|
||||
SpaceCadetPinball/nudge.h
|
||||
SpaceCadetPinball/options.cpp
|
||||
SpaceCadetPinball/options.h
|
||||
SpaceCadetPinball/partman.cpp
|
||||
SpaceCadetPinball/partman.h
|
||||
SpaceCadetPinball/pb.cpp
|
||||
SpaceCadetPinball/pb.h
|
||||
SpaceCadetPinball/pch.h
|
||||
SpaceCadetPinball/proj.cpp
|
||||
SpaceCadetPinball/proj.h
|
||||
SpaceCadetPinball/render.cpp
|
||||
SpaceCadetPinball/render.h
|
||||
SpaceCadetPinball/score.cpp
|
||||
SpaceCadetPinball/score.h
|
||||
SpaceCadetPinball/Sound.cpp
|
||||
SpaceCadetPinball/Sound.h
|
||||
SpaceCadetPinball/SpaceCadetPinball.cpp
|
||||
SpaceCadetPinball/TBall.cpp
|
||||
SpaceCadetPinball/TBall.h
|
||||
SpaceCadetPinball/TBlocker.cpp
|
||||
SpaceCadetPinball/TBlocker.h
|
||||
SpaceCadetPinball/TBumper.cpp
|
||||
SpaceCadetPinball/TBumper.h
|
||||
SpaceCadetPinball/TCircle.cpp
|
||||
SpaceCadetPinball/TCircle.h
|
||||
SpaceCadetPinball/TCollisionComponent.cpp
|
||||
SpaceCadetPinball/TCollisionComponent.h
|
||||
SpaceCadetPinball/TComponentGroup.cpp
|
||||
SpaceCadetPinball/TComponentGroup.h
|
||||
SpaceCadetPinball/TDemo.cpp
|
||||
SpaceCadetPinball/TDemo.h
|
||||
SpaceCadetPinball/TDrain.cpp
|
||||
SpaceCadetPinball/TDrain.h
|
||||
SpaceCadetPinball/TEdgeBox.h
|
||||
SpaceCadetPinball/TEdgeManager.cpp
|
||||
SpaceCadetPinball/TEdgeManager.h
|
||||
SpaceCadetPinball/TEdgeSegment.cpp
|
||||
SpaceCadetPinball/TEdgeSegment.h
|
||||
SpaceCadetPinball/TFlagSpinner.cpp
|
||||
SpaceCadetPinball/TFlagSpinner.h
|
||||
SpaceCadetPinball/TFlipper.cpp
|
||||
SpaceCadetPinball/TFlipper.h
|
||||
SpaceCadetPinball/TFlipperEdge.cpp
|
||||
SpaceCadetPinball/TFlipperEdge.h
|
||||
SpaceCadetPinball/TGate.cpp
|
||||
SpaceCadetPinball/TGate.h
|
||||
SpaceCadetPinball/THole.cpp
|
||||
SpaceCadetPinball/THole.h
|
||||
SpaceCadetPinball/timer.cpp
|
||||
SpaceCadetPinball/timer.h
|
||||
SpaceCadetPinball/TKickback.cpp
|
||||
SpaceCadetPinball/TKickback.h
|
||||
SpaceCadetPinball/TKickout.cpp
|
||||
SpaceCadetPinball/TKickout.h
|
||||
SpaceCadetPinball/TLight.cpp
|
||||
SpaceCadetPinball/TLight.h
|
||||
SpaceCadetPinball/TLightBargraph.cpp
|
||||
SpaceCadetPinball/TLightBargraph.h
|
||||
SpaceCadetPinball/TLightGroup.cpp
|
||||
SpaceCadetPinball/TLightGroup.h
|
||||
SpaceCadetPinball/TLightRollover.cpp
|
||||
SpaceCadetPinball/TLightRollover.h
|
||||
SpaceCadetPinball/TLine.cpp
|
||||
SpaceCadetPinball/TLine.h
|
||||
SpaceCadetPinball/TOneway.cpp
|
||||
SpaceCadetPinball/TOneway.h
|
||||
SpaceCadetPinball/TPinballComponent.cpp
|
||||
SpaceCadetPinball/TPinballComponent.h
|
||||
SpaceCadetPinball/TPinballTable.cpp
|
||||
SpaceCadetPinball/TPinballTable.h
|
||||
SpaceCadetPinball/TPlunger.cpp
|
||||
SpaceCadetPinball/TPlunger.h
|
||||
SpaceCadetPinball/TPopupTarget.cpp
|
||||
SpaceCadetPinball/TPopupTarget.h
|
||||
SpaceCadetPinball/TRamp.cpp
|
||||
SpaceCadetPinball/TRamp.h
|
||||
SpaceCadetPinball/translations.cpp
|
||||
SpaceCadetPinball/translations.h
|
||||
SpaceCadetPinball/TRollover.cpp
|
||||
SpaceCadetPinball/TRollover.h
|
||||
SpaceCadetPinball/TSink.cpp
|
||||
SpaceCadetPinball/TSink.h
|
||||
SpaceCadetPinball/TSoloTarget.cpp
|
||||
SpaceCadetPinball/TSoloTarget.h
|
||||
SpaceCadetPinball/TSound.cpp
|
||||
SpaceCadetPinball/TSound.h
|
||||
SpaceCadetPinball/TTableLayer.cpp
|
||||
SpaceCadetPinball/TTableLayer.h
|
||||
SpaceCadetPinball/TTextBox.cpp
|
||||
SpaceCadetPinball/TTextBox.h
|
||||
SpaceCadetPinball/TTextBoxMessage.cpp
|
||||
SpaceCadetPinball/TTextBoxMessage.h
|
||||
SpaceCadetPinball/TTimer.cpp
|
||||
SpaceCadetPinball/TTimer.h
|
||||
SpaceCadetPinball/TTripwire.cpp
|
||||
SpaceCadetPinball/TTripwire.h
|
||||
SpaceCadetPinball/TWall.cpp
|
||||
SpaceCadetPinball/TWall.h
|
||||
SpaceCadetPinball/winmain.cpp
|
||||
SpaceCadetPinball/winmain.h
|
||||
SpaceCadetPinball/zdrv.cpp
|
||||
SpaceCadetPinball/zdrv.h
|
||||
SpaceCadetPinball/imconfig.h
|
||||
SpaceCadetPinball/imgui_internal.h
|
||||
SpaceCadetPinball/imgui.cpp
|
||||
SpaceCadetPinball/imgui.h
|
||||
SpaceCadetPinball/imgui_sdl.cpp
|
||||
SpaceCadetPinball/imgui_sdl.h
|
||||
SpaceCadetPinball/imgui_draw.cpp
|
||||
SpaceCadetPinball/imgui_widgets.cpp
|
||||
SpaceCadetPinball/imgui_tables.cpp
|
||||
SpaceCadetPinball/imgui_demo.cpp
|
||||
SpaceCadetPinball/imgui_impl_sdl.cpp
|
||||
SpaceCadetPinball/imgui_impl_sdl.h
|
||||
SpaceCadetPinball/imgui_impl_sdlrenderer.h
|
||||
SpaceCadetPinball/imgui_impl_sdlrenderer.cpp
|
||||
SpaceCadetPinball/imstb_textedit.h
|
||||
SpaceCadetPinball/imstb_rectpack.h
|
||||
SpaceCadetPinball/imstb_truetype.h
|
||||
SpaceCadetPinball/DebugOverlay.cpp
|
||||
SpaceCadetPinball/DebugOverlay.h
|
||||
)
|
||||
|
||||
# On Windows, include resource file with the icon
|
||||
if(WIN32)
|
||||
set_source_files_properties(SpaceCadetPinball/SpaceCadetPinball.rc LANGUAGE RC)
|
||||
list(APPEND SOURCE_FILES SpaceCadetPinball/SpaceCadetPinball.rc)
|
||||
endif(WIN32)
|
||||
|
||||
add_executable(SpaceCadetPinball ${SOURCE_FILES})
|
||||
|
||||
# Skip pch on foreign code
|
||||
set_source_files_properties(
|
||||
SpaceCadetPinball/imgui.cpp
|
||||
SpaceCadetPinball/imgui_sdl.cpp
|
||||
SpaceCadetPinball/imgui_draw.cpp
|
||||
SpaceCadetPinball/imgui_widgets.cpp
|
||||
SpaceCadetPinball/imgui_tables.cpp
|
||||
SpaceCadetPinball/imgui_demo.cpp
|
||||
SpaceCadetPinball/imgui_impl_sdl.cpp
|
||||
SpaceCadetPinball/imgui_impl_sdlrenderer.cpp
|
||||
PROPERTIES SKIP_PRECOMPILE_HEADERS 1
|
||||
)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER "3.16.0" OR ${CMAKE_VERSION} VERSION_EQUAL "3.16.0")
|
||||
target_precompile_headers(SpaceCadetPinball
|
||||
PUBLIC
|
||||
SpaceCadetPinball/pch.h
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(SpaceCadetPinball ${SDL2_LIBRARY} ${SDL2_MIXER_LIBRARY})
|
||||
|
||||
# On Windows, copy DLL to output
|
||||
if(WIN32)
|
||||
list(GET SDL2_LIBRARY -1 SDL2_DLL_PATH)
|
||||
list(GET SDL2_MIXER_LIBRARY -1 SDL2_MIXER_DLL_PATH)
|
||||
get_filename_component(SDL2_DLL_PATH ${SDL2_DLL_PATH} DIRECTORY)
|
||||
get_filename_component(SDL2_MIXER_DLL_PATH ${SDL2_MIXER_DLL_PATH} DIRECTORY)
|
||||
if(MINGW)
|
||||
string(REGEX REPLACE "lib$" "bin" SDL2_DLL_PATH ${SDL2_DLL_PATH})
|
||||
string(REGEX REPLACE "lib$" "bin" SDL2_MIXER_DLL_PATH ${SDL2_MIXER_DLL_PATH})
|
||||
endif()
|
||||
message(STATUS "copy paths='${SDL2_DLL_PATH}' '${SDL2_MIXER_DLL_PATH}'")
|
||||
add_custom_command(TARGET SpaceCadetPinball POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL2_DLL_PATH}/SDL2.dll" $<TARGET_FILE_DIR:SpaceCadetPinball>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL2_MIXER_DLL_PATH}/SDL2_mixer.dll" $<TARGET_FILE_DIR:SpaceCadetPinball>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/Platform/Linux/${PROJECT_NAME}.desktop" DESTINATION "share/applications")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/Platform/Linux/${PROJECT_NAME}.metainfo.xml" DESTINATION "share/metainfo")
|
||||
foreach(S 16 32 48 128 192)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Icon_${S}x${S}.png" DESTINATION
|
||||
"share/icons/hicolor/${S}x${S}/apps" RENAME "${PROJECT_NAME}.png")
|
||||
endforeach(S)
|
||||
endif()
|
||||
@@ -0,0 +1,173 @@
|
||||
|
||||
# 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 SDL2main 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 SDLmain.h and SDLmain.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" SDL 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" SDL 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).
|
||||
|
||||
#=============================================================================
|
||||
# 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.)
|
||||
|
||||
# message("<FindSDL2.cmake>")
|
||||
|
||||
SET(SDL2_SEARCH_PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
${SDL2_PATH}
|
||||
)
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES include/SDL2 include SDL2
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(PATH_SUFFIXES lib64 lib/x64 lib)
|
||||
else()
|
||||
set(PATH_SUFFIXES lib/x86 lib)
|
||||
endif()
|
||||
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
||||
NAMES SDL2
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES ${PATH_SUFFIXES}
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
|
||||
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 ${PATH_SUFFIXES}
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
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 link flag, -mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
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 ${CMAKE_THREAD_LIBS_INIT} ${SDL2_LIBRARY_TEMP})
|
||||
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 "")
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
# message("</FindSDL2.cmake>")
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
||||
@@ -0,0 +1,100 @@
|
||||
# Locate SDL_MIXER library
|
||||
#
|
||||
# This module defines:
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# SDL2_MIXER_LIBRARIES, the name of the library to link against
|
||||
# SDL2_MIXER_INCLUDE_DIRS, where to find the headers
|
||||
# SDL2_MIXER_FOUND, if false, do not try to link against
|
||||
# SDL2_MIXER_VERSION_STRING - human-readable string containing the version of SDL_MIXER
|
||||
#
|
||||
#
|
||||
#
|
||||
# For backward compatibility the following variables are also set:
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# SDLMIXER_LIBRARY (same value as SDL2_MIXER_LIBRARIES)
|
||||
# SDLMIXER_INCLUDE_DIR (same value as SDL2_MIXER_INCLUDE_DIRS)
|
||||
# SDLMIXER_FOUND (same value as SDL2_MIXER_FOUND)
|
||||
#
|
||||
#
|
||||
#
|
||||
# $SDLDIR is an environment variable that would correspond to the
|
||||
# ./configure --prefix=$SDLDIR used in building SDL.
|
||||
#
|
||||
# Created by Eric Wing. This was influenced by the FindSDL.cmake
|
||||
# module, but with modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2005-2009 Kitware, Inc.
|
||||
# Copyright 2012 Benjamin Eikel
|
||||
#
|
||||
# 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_MIXER_INCLUDE_DIR SDL_mixer.h
|
||||
HINTS
|
||||
ENV SDL2MIXERDIR
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES SDL2
|
||||
# path suffixes to search inside ENV{SDLDIR}
|
||||
include/SDL2 include
|
||||
PATHS ${SDL2_MIXER_PATH}
|
||||
)
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(VC_LIB_PATH_SUFFIX lib/x64)
|
||||
else()
|
||||
set(VC_LIB_PATH_SUFFIX lib/x86)
|
||||
endif()
|
||||
|
||||
find_library(SDL2_MIXER_LIBRARY
|
||||
NAMES SDL2_mixer
|
||||
HINTS
|
||||
ENV SDL2MIXERDIR
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES lib bin ${VC_LIB_PATH_SUFFIX}
|
||||
PATHS ${SDL2_MIXER_PATH}
|
||||
)
|
||||
|
||||
if(SDL2_MIXER_INCLUDE_DIR AND EXISTS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+[0-9]+$")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MAJOR "${SDL2_MIXER_VERSION_MAJOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MINOR "${SDL2_MIXER_VERSION_MINOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_PATCH "${SDL2_MIXER_VERSION_PATCH_LINE}")
|
||||
set(SDL2_MIXER_VERSION_STRING ${SDL2_MIXER_VERSION_MAJOR}.${SDL2_MIXER_VERSION_MINOR}.${SDL2_MIXER_VERSION_PATCH})
|
||||
unset(SDL2_MIXER_VERSION_MAJOR_LINE)
|
||||
unset(SDL2_MIXER_VERSION_MINOR_LINE)
|
||||
unset(SDL2_MIXER_VERSION_PATCH_LINE)
|
||||
unset(SDL2_MIXER_VERSION_MAJOR)
|
||||
unset(SDL2_MIXER_VERSION_MINOR)
|
||||
unset(SDL2_MIXER_VERSION_PATCH)
|
||||
endif()
|
||||
|
||||
set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY})
|
||||
set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_mixer
|
||||
REQUIRED_VARS SDL2_MIXER_LIBRARIES SDL2_MIXER_INCLUDE_DIRS
|
||||
VERSION_VAR SDL2_MIXER_VERSION_STRING)
|
||||
|
||||
# for backward compatibility
|
||||
set(SDLMIXER_LIBRARY ${SDL2_MIXER_LIBRARIES})
|
||||
set(SDLMIXER_INCLUDE_DIR ${SDL2_MIXER_INCLUDE_DIRS})
|
||||
set(SDLMIXER_FOUND ${SDL2_MIXER_FOUND})
|
||||
|
||||
mark_as_advanced(SDL2_MIXER_LIBRARY SDL2_MIXER_INCLUDE_DIR)
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"addressSanitizerEnabled": false
|
||||
},
|
||||
{
|
||||
"name": "x86-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x86" ],
|
||||
"addressSanitizerEnabled": false
|
||||
},
|
||||
{
|
||||
"name": "x86-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Release",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DCMAKE_WIN32_EXECUTABLE:BOOL=1",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x86" ]
|
||||
},
|
||||
{
|
||||
"name": "x64-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Release",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DCMAKE_WIN32_EXECUTABLE:BOOL=1",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ]
|
||||
},
|
||||
{
|
||||
"name": "x86-Release-WinXP",
|
||||
"generator": "Visual Studio 16 2019",
|
||||
"configurationType": "Release",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DCMAKE_WIN32_EXECUTABLE:BOOL=1 -T v141_xp",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x86" ]
|
||||
},
|
||||
{
|
||||
"name": "WSL-Clang-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeExecutable": "cmake",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "linux_clang_x64" ],
|
||||
"wslPath": "${defaultWSLPath}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Issues
|
||||
Don’t forget to mention game version, which release or branch it came from.\
|
||||
Source port issues are handled in their respective repositories.
|
||||
|
||||
# Pull request
|
||||
No source ports in main repository.\
|
||||
I have no way to test and maintain most of them.\
|
||||
The best I can do is to add a link.
|
||||
|
||||
There is no guarantee that any particular PR will be accepted.\
|
||||
If you are unsure, ask first, make PR second.
|
||||
@@ -1,3 +0,0 @@
|
||||
rc /Fo.\DrMem\SpaceCadetPinball.res ".\SpaceCadetPinball\SpaceCadetPinball.rc"
|
||||
|
||||
cl /Zi /MT /EHsc /O /Ob0 /cgthreads4 /Fo.\DrMem\ /Fe.\DrMem\myapp.exe ".\SpaceCadetPinball\*.cpp" Comctl32.lib Winmm.lib Htmlhelp.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ".\DrMem\SpaceCadetPinball.res"
|
||||
@@ -39,21 +39,21 @@ Type Meaning/comments
|
||||
9 String (content)
|
||||
10 Array of 16bits integer values
|
||||
11 Array of 32bits floating point values (collision box, ...)
|
||||
12 16 bpp bitmap (Heightmap?)
|
||||
12 16 bpp bitmap (zMap)
|
||||
|
||||
|
||||
//-- 8bpp bitmap data header --//
|
||||
+0: Unknown (0) BYTE
|
||||
+0: Resolution BYTE 0=640x480, 1=800x600, 2=1024x768, -1=Load in all resolutions
|
||||
+1: Width WORD
|
||||
+3: Height WORD
|
||||
+5: X position WORD
|
||||
+7 Y position WORD
|
||||
+9: Size of bitmap DWORD
|
||||
+13: Unknown (1) BYTE
|
||||
+13: Flags BYTE bit0=Raw bmp align; bit1=DibBitmap, raw when 0; bit2=Spliced bitmap (aka skipline), combines bmp and zMap in RLE-like way
|
||||
+14: Bitmap data BYTE*(DWORD@+9)
|
||||
|
||||
|
||||
//-- 16bpp bitmap data header --//
|
||||
//-- 16bpp zMap data header --//
|
||||
+0: Width WORD
|
||||
+2: Height WORD
|
||||
+4: Pitch/2 WORD
|
||||
@@ -62,6 +62,16 @@ Type Meaning/comments
|
||||
+12: Unknown (80) WORD
|
||||
+14: Bitmap data BYTE*(DWORD@+9)
|
||||
|
||||
//-- 16bpp zMap data header full tilt --//
|
||||
+0: Resolution BYTE 0=640x480, 1=800x600, 2=1024x768, -1=Load in all resolutions
|
||||
+1: Width WORD
|
||||
+3: Height WORD
|
||||
+5: Pitch/2 WORD
|
||||
+7: Unknown (0) DWORD
|
||||
+11: Unknown (0) WORD
|
||||
+13: Unknown (80) WORD
|
||||
+15: Bitmap data BYTE*(DWORD@+9)
|
||||
|
||||
|
||||
//-- Pinball 3D remarkable groups --//
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2021 Andrey Muzychenko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Building
|
||||
|
||||
Install devel packages for `SDL2` and `SDL2_mixer`.\
|
||||
|
||||
Compile with CMake; tested with GCC 10, Clang 11.\
|
||||
To cross-compile for Windows, install a 64-bit version of mingw and its `SDL2` and `SDL2_mixer` distributions, then use the `mingwcc.cmake` toolchain.
|
||||
|
||||
```bash
|
||||
# On Debian and Ubuntu
|
||||
sudo apt install cmake build-essential ninja-build libsdl2-dev libsdl2-mixer-dev libsdl2-mixer-2.0-0 libsdl2-2.0-0 fluidsynth
|
||||
|
||||
# On Fedora
|
||||
sudo dnf install cmake ninja-build SDL2 SDL2-devel SDL2_mixer SDL2_mixer-devel fluidsynth fluidsynth-libs mscore-fonts g++
|
||||
|
||||
# Build
|
||||
cmake -GNinja .
|
||||
ninja
|
||||
```
|
||||
|
||||
**If you can't hear the background music**: You need to export the following variable before launching the game like this:
|
||||
`export SDL_SOUNDFONT=/path/to/your/soundfont`
|
||||
|
||||
Fluidsynth will install a default soundfont in the following path:
|
||||
On Fedora: `/usr/share/soundfonts/FluidR3_GM.sf2`
|
||||
On Ubuntu/Debian: `/usr/share/sounds/sf2/FluidR3_GM.sf2`
|
||||
@@ -0,0 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Space Cadet Pinball
|
||||
Comment=Decompilation of 3D Pinball for Windows - Space Cadet
|
||||
Exec=SpaceCadetPinball
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Icon=SpaceCadetPinball
|
||||
Type=Application
|
||||
Categories=Game;ArcadeGame;
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.github.k4zmu2a.spacecadetpinball</id>
|
||||
<name>Space Cadet Pinball</name>
|
||||
<display_name>Space Cadet Pinball</display_name>
|
||||
<summary>Game engine for Space Cadet Pinball</summary>
|
||||
<url type="homepage">https://github.com/k4zmu2a/SpaceCadetPinball</url>
|
||||
<url type="bugtracker">https://github.com/k4zmu2a/SpaceCadetPinball/issues</url>
|
||||
<description>
|
||||
<p>
|
||||
Reverse engineering of '3D Pinball for Windows - Space Cadet', a game bundled with Windows.
|
||||
</p>
|
||||
</description>
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
<category>ArcadeGame</category>
|
||||
</categories>
|
||||
<launchable type="desktop-id">SpaceCadetPinball.desktop</launchable>
|
||||
<provides>
|
||||
<binary>SpaceCadetPinball</binary>
|
||||
</provides>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/k4zmu2a/SpaceCadetPinball/master/Screenshots/screenshot-1.jpg</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/k4zmu2a/SpaceCadetPinball/master/Screenshots/screenshot-2.jpg</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="2.1.0" date="2023-10-16">
|
||||
<description>
|
||||
<p>Feature release focusing on FT compatibility.</p>
|
||||
<p>Main highlights:</p>
|
||||
<ul>
|
||||
<li>FT: collision system changes, table control changes, multiball, three MIDI tracks.</li>
|
||||
<li>Work in progress: localization support. Depends on external font selection for most languages.</li>
|
||||
<li>HW accelerated ImGui renderer.</li>
|
||||
<li>Experimental stereo sound support.</li>
|
||||
<li>UX improvements.</li>
|
||||
<li>Bug fixes.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.0.1" date="2021-12-29">
|
||||
<description>
|
||||
<p>First bug fix release of cross-platform port.</p>
|
||||
<p>Main highlights:</p>
|
||||
<ul>
|
||||
<li>Improved Linux support: install target, icons, desktop shortcut, alternative data paths.</li>
|
||||
<li>Improved game controller support.</li>
|
||||
<li>High precision sleep mode for ~0 frame time jitter.</li>
|
||||
<li>Integer image scaling mode.</li>
|
||||
<li>3DPB <-> FT data switch option, for easier data-set changes when both are available.</li>
|
||||
<li>FT demo data support.</li>
|
||||
<li>Bug fixes.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.0.0" date="2021-10-30">
|
||||
<description>
|
||||
<p>First release of cross-platform port.</p>
|
||||
<p>Main highlights:</p>
|
||||
<ul>
|
||||
<li>Windows dependencies removed.</li>
|
||||
<li>Graphics and sounds rendered with SDL.</li>
|
||||
<li>Dear ImGui GUI.</li>
|
||||
<li>CMake build script, compiles on a lot of platforms.</li>
|
||||
<li>A number of 3rd party source ports.</li>
|
||||
</ul>
|
||||
<p>All features of v1.x are present, except for text translations.</p>
|
||||
<p>
|
||||
Development and releases of v1.x will continue, don’t confuse the two.
|
||||
All source ports are technically v2.0RC, not v1.x.
|
||||
</p>
|
||||
|
||||
<p>There are no Linux or macOS binaries attached, compile them yourself.</p>
|
||||
|
||||
<p>
|
||||
This release was made better with the help of contributors.
|
||||
I thank them for their interest in the project.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
</releases>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<developer_name>Andrey Muzychenko</developer_name>
|
||||
<project_license>MIT</project_license>
|
||||
<keywords>
|
||||
<keyword>pinball</keyword>
|
||||
<keyword>space</keyword>
|
||||
<keyword>cadet</keyword>
|
||||
</keywords>
|
||||
<content_rating type="oars-1.1" />
|
||||
</component>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SpaceCadetPinball</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>SpaceCadetPinball.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.github.k4zmu2a.spacecadetpinball</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string></string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SpaceCadetPinball</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>CHANGEME_SW_VERSION</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.11</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,123 @@
|
||||
<!-- markdownlint-disable-file MD033 -->
|
||||
|
||||
# SpaceCadetPinball
|
||||
|
||||
## Summary
|
||||
|
||||
Reverse engineering of `3D Pinball for Windows - Space Cadet`, a game bundled with Windows.
|
||||
|
||||
## How to play
|
||||
|
||||
Place compiled executable into a folder containing original game resources (not included).\
|
||||
Supports data files from Windows and Full Tilt versions of the game.
|
||||
|
||||
## Known source ports
|
||||
|
||||
| Platform | Author | URL |
|
||||
| ------------------ | --------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| PS Vita | Axiom | <https://github.com/suicvne/SpaceCadetPinball_Vita> |
|
||||
| Emscripten | alula | <https://github.com/alula/SpaceCadetPinball> <br> Play online: <https://alula.github.io/SpaceCadetPinball> |
|
||||
| Nintendo Switch | averne | <https://github.com/averne/SpaceCadetPinball-NX> |
|
||||
| webOS TV | mariotaku | <https://github.com/webosbrew/SpaceCadetPinball> |
|
||||
| Android (WIP) | Iscle | https://github.com/Iscle/SpaceCadetPinball |
|
||||
| Nintendo Wii | MaikelChan | https://github.com/MaikelChan/SpaceCadetPinball |
|
||||
| Nintendo 3DS | MaikelChan | https://github.com/MaikelChan/SpaceCadetPinball/tree/3ds |
|
||||
| Nintendo DS | Headshotnoby | https://github.com/headshot2017/3dpinball-nds |
|
||||
| Nintendo Wii U | IntriguingTiles | https://github.com/IntriguingTiles/SpaceCadetPinball-WiiU |
|
||||
| PlayStation 2 | Headshotnoby | https://github.com/headshot2017/3dpinball-ps2 |
|
||||
| Sega Dreamcast | Headshotnoby | https://github.com/headshot2017/3dpinball-dc |
|
||||
| MorphOS | BeWorld | https://www.morphos-storage.net/?id=1688897 |
|
||||
| AmigaOS 4 | rjd324 | http://aminet.net/package/game/actio/spacecadetpinball-aos4 |
|
||||
| Android (WIP) | fexed | https://github.com/fexed/Pinball-on-Android |
|
||||
|
||||
Platforms covered by this project: desktop Windows, Linux and macOS.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Source
|
||||
|
||||
* `pinball.exe` from `Windows XP` (SHA-1 `2A5B525E0F631BB6107639E2A69DF15986FB0D05`) and its public PDB
|
||||
* `CADET.EXE` 32bit version from `Full Tilt! Pinball` (SHA-1 `3F7B5699074B83FD713657CD94671F2156DBEDC4`)
|
||||
|
||||
## Tools used
|
||||
|
||||
`Ghidra`, `Ida`, `Visual Studio`
|
||||
|
||||
## What was done
|
||||
|
||||
* All structures were populated, globals and locals named.
|
||||
* All subs were decompiled, C pseudo code was converted to compilable C++. Loose (namespace?) subs were assigned to classes.
|
||||
|
||||
## Compiling
|
||||
|
||||
Project uses `C++11` and depends on `SDL2` libs.
|
||||
|
||||
### On Windows
|
||||
|
||||
Download and unpack devel packages for `SDL2` and `SDL2_mixer`.\
|
||||
Set paths to them in `CMakeLists.txt`, see suggested placement in `/Libs`.\
|
||||
Compile with Visual Studio; tested with 2019.
|
||||
|
||||
### On Linux
|
||||
|
||||
Install devel packages for `SDL2` and `SDL2_mixer`.\
|
||||
Compile with CMake; tested with GCC 10, Clang 11.\
|
||||
To cross-compile for Windows, install a 64-bit version of mingw and its `SDL2` and `SDL2_mixer` distributions, then use the `mingwcc.cmake` toolchain.
|
||||
|
||||
[](https://repology.org/project/spacecadetpinball/versions)
|
||||
|
||||
Some distributions provide a package in their repository. You can use those for easier dependency management and updates.
|
||||
|
||||
This project is available as Flatpak on [Flathub](https://flathub.org/apps/details/com.github.k4zmu2a.spacecadetpinball).
|
||||
|
||||
### On macOS
|
||||
|
||||
Install XCode (or at least Xcode Command Line Tools with `xcode-select --install`) and CMake.
|
||||
|
||||
**HomeBrew**
|
||||
|
||||
You can easily install the build artifact by using `brew`.
|
||||
|
||||
```sh
|
||||
brew tap draftbrew/tap
|
||||
brew install --no-quarantine space-cadet-pinball
|
||||
```
|
||||
|
||||
Be aware that the flag `--no-quarantime` will disable macOS's Gatekeeper during installation.
|
||||
|
||||
**Manual compilation:**
|
||||
|
||||
* **Homebrew**: Install the `SDL2`, `SDL2_mixer` homebrew packages.
|
||||
* **MacPorts**: Install the `libSDL2`, `libSDL2_mixer` macports packages.
|
||||
|
||||
Compile with CMake. Ensure that `CMAKE_OSX_ARCHITECTURES` variable is set for either `x86_64` Apple Intel or `arm64` for Apple Silicon.
|
||||
|
||||
Tested with: macOS Big Sur (Intel) with Xcode 13 & macOS Montery Beta (Apple Silicon) with Xcode 13.
|
||||
|
||||
**Automated compilation:**
|
||||
|
||||
Run the `build-mac-app.sh` script from the root of the repository. The app will be available in a DMG file named `SpaceCadetPinball-<version>-mac.dmg`.
|
||||
|
||||
Tested with: macOS Ventura (Apple Silicon) with Xcode Command Line Tools 14 & macOS Big Sur on GitHub Runner (Intel) with XCode 13.
|
||||
|
||||
## Plans
|
||||
|
||||
* ~~Decompile original game~~
|
||||
* ~~Resizable window, scaled graphics~~
|
||||
* ~~Loader for high-res sprites from CADET.DAT~~
|
||||
* ~~Cross-platform port using SDL2, SDL2_mixer, ImGui~~
|
||||
* Full Tilt Cadet features
|
||||
* Localization support
|
||||
* Maybe: Support for the other two tables - Dragon and Pirate
|
||||
* Maybe: Game data editor
|
||||
|
||||
## On 64-bit bug that killed the game
|
||||
|
||||
I did not find it, decompiled game worked in x64 mode on the first try.\
|
||||
It was either lost in decompilation or introduced in x64 port/not present in x86 build.\
|
||||
Based on public description of the bug (no ball collision), I guess that the bug was in `TEdgeManager::TestGridBox`
|
||||
|
After Width: | Height: | Size: 612 KiB |
|
After Width: | Height: | Size: 278 KiB |
@@ -1,31 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.705
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpaceCadetPinball", "SpaceCadetPinball\SpaceCadetPinball.vcxproj", "{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Debug|x64.Build.0 = Debug|x64
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Release|x64.ActiveCfg = Release|x64
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Release|x64.Build.0 = Release|x64
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4B620E0B-FD42-4AEC-BC4A-261C1669A1B5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,358 @@
|
||||
#include "pch.h"
|
||||
#include "DebugOverlay.h"
|
||||
|
||||
#include "loader.h"
|
||||
#include "maths.h"
|
||||
#include "proj.h"
|
||||
#include "winmain.h"
|
||||
#include "TFlipperEdge.h"
|
||||
#include "TFlipper.h"
|
||||
#include "pb.h"
|
||||
#include "TLine.h"
|
||||
#include "TCircle.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TEdgeBox.h"
|
||||
#include "TTableLayer.h"
|
||||
#include "TBall.h"
|
||||
#include "render.h"
|
||||
#include "options.h"
|
||||
#include "Sound.h"
|
||||
|
||||
|
||||
gdrv_bitmap8* DebugOverlay::dbScreen = nullptr;
|
||||
|
||||
static int SDL_RenderDrawCircle(SDL_Renderer* renderer, int x, int y, int radius)
|
||||
{
|
||||
SDL_Point points[256];
|
||||
int pointCount = 0;
|
||||
int offsetx, offsety, d;
|
||||
int status;
|
||||
|
||||
offsetx = 0;
|
||||
offsety = radius;
|
||||
d = radius - 1;
|
||||
status = 0;
|
||||
|
||||
while (offsety >= offsetx)
|
||||
{
|
||||
if (pointCount + 8 > 256)
|
||||
{
|
||||
status = SDL_RenderDrawPoints(renderer, points, pointCount);
|
||||
pointCount = 0;
|
||||
|
||||
if (status < 0) {
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
points[pointCount++] = { x + offsetx, y + offsety };
|
||||
points[pointCount++] = { x + offsety, y + offsetx };
|
||||
points[pointCount++] = { x - offsetx, y + offsety };
|
||||
points[pointCount++] = { x - offsety, y + offsetx };
|
||||
points[pointCount++] = { x + offsetx, y - offsety };
|
||||
points[pointCount++] = { x + offsety, y - offsetx };
|
||||
points[pointCount++] = { x - offsetx, y - offsety };
|
||||
points[pointCount++] = { x - offsety, y - offsetx };
|
||||
|
||||
if (d >= 2 * offsetx) {
|
||||
d -= 2 * offsetx + 1;
|
||||
offsetx += 1;
|
||||
}
|
||||
else if (d < 2 * (radius - offsety)) {
|
||||
d += 2 * offsety - 1;
|
||||
offsety -= 1;
|
||||
}
|
||||
else {
|
||||
d += 2 * (offsety - offsetx - 1);
|
||||
offsety -= 1;
|
||||
offsetx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pointCount > 0)
|
||||
status = SDL_RenderDrawPoints(renderer, points, pointCount);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void DebugOverlay::UnInit()
|
||||
{
|
||||
delete dbScreen;
|
||||
dbScreen = nullptr;
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawOverlay()
|
||||
{
|
||||
if (dbScreen == nullptr)
|
||||
{
|
||||
dbScreen = new gdrv_bitmap8(render::vscreen->Width, render::vscreen->Height, false, false);
|
||||
dbScreen->CreateTexture("nearest", SDL_TEXTUREACCESS_TARGET);
|
||||
SDL_SetTextureBlendMode(dbScreen->Texture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
// Setup overlay rendering
|
||||
Uint8 initialR, initialG, initialB, initialA;
|
||||
auto initialRenderTarget = SDL_GetRenderTarget(winmain::Renderer);
|
||||
SDL_GetRenderDrawColor(winmain::Renderer, &initialR, &initialG, &initialB, &initialA);
|
||||
SDL_SetRenderTarget(winmain::Renderer, dbScreen->Texture);
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(winmain::Renderer);
|
||||
|
||||
// Draw EdgeManager box grid
|
||||
if (options::Options.DebugOverlayGrid)
|
||||
DrawBoxGrid();
|
||||
|
||||
// Draw bounding boxes around sprites
|
||||
if (options::Options.DebugOverlaySprites)
|
||||
DrawAllSprites();
|
||||
|
||||
// Draw all edges registered in TCollisionComponent.EdgeList + flippers
|
||||
if (options::Options.DebugOverlayAllEdges)
|
||||
DrawAllEdges();
|
||||
|
||||
// Draw ball collision info
|
||||
if (options::Options.DebugOverlayBallPosition || options::Options.DebugOverlayBallEdges)
|
||||
DrawBallInfo();
|
||||
|
||||
// Draw positions associated with currently playing sound channels
|
||||
if (options::Options.DebugOverlaySounds)
|
||||
DrawSoundPositions();
|
||||
|
||||
// Draw ball depth cutoff steps that determine sprite size.
|
||||
if (options::Options.DebugOverlayBallDepthGrid)
|
||||
DrawBallDepthSteps();
|
||||
|
||||
// Draw AABB of collision components
|
||||
if (options::Options.DebugOverlayAabb)
|
||||
DrawComponentAabb();
|
||||
|
||||
// Restore render target
|
||||
SDL_SetRenderTarget(winmain::Renderer, initialRenderTarget);
|
||||
SDL_SetRenderDrawColor(winmain::Renderer,
|
||||
initialR, initialG, initialB, initialA);
|
||||
|
||||
// Copy overlay with alpha blending
|
||||
SDL_BlendMode blendMode;
|
||||
SDL_GetRenderDrawBlendMode(winmain::Renderer, &blendMode);
|
||||
SDL_SetRenderDrawBlendMode(winmain::Renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_RenderCopy(winmain::Renderer, dbScreen->Texture, nullptr, &render::DestinationRect);
|
||||
SDL_SetRenderDrawBlendMode(winmain::Renderer, blendMode);
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawBoxGrid()
|
||||
{
|
||||
auto& edgeMan = *TTableLayer::edge_manager;
|
||||
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 0, 255, 0, 255);
|
||||
for (int x = 0; x <= edgeMan.MaxBoxX; x++)
|
||||
{
|
||||
vector2 boxPt{ x * edgeMan.AdvanceX + edgeMan.MinX , edgeMan.MinY };
|
||||
auto pt1 = proj::xform_to_2d(boxPt);
|
||||
boxPt.Y = edgeMan.MaxBoxY * edgeMan.AdvanceY + edgeMan.MinY;
|
||||
auto pt2 = proj::xform_to_2d(boxPt);
|
||||
|
||||
SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
|
||||
}
|
||||
for (int y = 0; y <= edgeMan.MaxBoxY; y++)
|
||||
{
|
||||
vector2 boxPt{ edgeMan.MinX, y * edgeMan.AdvanceY + edgeMan.MinY };
|
||||
auto pt1 = proj::xform_to_2d(boxPt);
|
||||
boxPt.X = edgeMan.MaxBoxX * edgeMan.AdvanceX + edgeMan.MinX;
|
||||
auto pt2 = proj::xform_to_2d(boxPt);
|
||||
|
||||
SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawAllEdges()
|
||||
{
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 0, 200, 200, 255);
|
||||
for (auto cmp : pb::MainTable->ComponentList)
|
||||
{
|
||||
auto collCmp = dynamic_cast<TCollisionComponent*>(cmp);
|
||||
if (collCmp)
|
||||
{
|
||||
for (auto edge : collCmp->EdgeList)
|
||||
{
|
||||
DrawEdge(edge);
|
||||
}
|
||||
}
|
||||
auto flip = dynamic_cast<TFlipper*>(cmp);
|
||||
if (flip)
|
||||
{
|
||||
DrawEdge(flip->FlipperEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawBallInfo()
|
||||
{
|
||||
auto& edgeMan = *TTableLayer::edge_manager;
|
||||
for (auto ball : pb::MainTable->BallList)
|
||||
{
|
||||
if (ball->ActiveFlag)
|
||||
{
|
||||
vector2 ballPosition = { ball->Position.X, ball->Position.Y };
|
||||
|
||||
if (options::Options.DebugOverlayBallEdges)
|
||||
{
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 255, 0, 0, 255);
|
||||
auto x = edgeMan.box_x(ballPosition.X), y = edgeMan.box_y(ballPosition.Y);
|
||||
auto& box = edgeMan.BoxArray[x + y * edgeMan.MaxBoxX];
|
||||
for (auto edge : box.EdgeList)
|
||||
{
|
||||
DrawEdge(edge);
|
||||
}
|
||||
}
|
||||
|
||||
if (options::Options.DebugOverlayBallPosition)
|
||||
{
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 255, 255);
|
||||
|
||||
auto pt1 = proj::xform_to_2d(ballPosition);
|
||||
vector2 radVec1 = { 0, ballPosition.Y }, radVec2 = { ball->Radius, ballPosition.Y };
|
||||
auto radVec1I = proj::xform_to_2d(radVec1), radVec2I = proj::xform_to_2d(radVec2);
|
||||
auto radI = std::sqrt(maths::magnitudeSq(vector2i{ radVec1I.X - radVec2I.X ,radVec1I.Y - radVec2I.Y }));
|
||||
SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, static_cast<int>(std::round(radI)));
|
||||
|
||||
auto nextPos = ballPosition;
|
||||
maths::vector_add(nextPos, maths::vector_mul(ball->Direction, ball->Speed / 10.0f));
|
||||
auto pt2 = proj::xform_to_2d(nextPos);
|
||||
SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawAllSprites()
|
||||
{
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 200, 200, 0, 255);
|
||||
for (auto cmp : pb::MainTable->ComponentList)
|
||||
{
|
||||
if (cmp->RenderSprite)
|
||||
{
|
||||
auto& bmpR = cmp->RenderSprite->BmpRect;
|
||||
if (bmpR.Width != 0 && bmpR.Height != 0)
|
||||
{
|
||||
SDL_Rect rect{ bmpR.XPosition, bmpR.YPosition, bmpR.Width, bmpR.Height };
|
||||
SDL_RenderDrawRect(winmain::Renderer, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawSoundPositions()
|
||||
{
|
||||
auto& edgeMan = *TTableLayer::edge_manager;
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 200, 0, 200, 255);
|
||||
|
||||
for (auto& posNorm : Sound::Channels)
|
||||
{
|
||||
auto pos3D = edgeMan.DeNormalizeBox(posNorm.Position);
|
||||
auto pos2D = proj::xform_to_2d(pos3D);
|
||||
SDL_RenderDrawCircle(winmain::Renderer, pos2D.X, pos2D.Y, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawBallDepthSteps()
|
||||
{
|
||||
auto& edgeMan = *TTableLayer::edge_manager;
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 200, 100, 0, 255);
|
||||
|
||||
for (auto ball : pb::MainTable->BallList)
|
||||
{
|
||||
auto visualCount = loader::query_visual_states(ball->GroupIndex);
|
||||
for (auto index = 0; index < visualCount; ++index)
|
||||
{
|
||||
auto depthPt = reinterpret_cast<vector3*>(loader::query_float_attribute(ball->GroupIndex, index, 501));
|
||||
auto pt = proj::xform_to_2d(*depthPt);
|
||||
|
||||
// Snap X coordinate to edge box sides
|
||||
auto x1 = proj::xform_to_2d(vector2{edgeMan.MinX, depthPt->Y}).X;
|
||||
auto x2 = proj::xform_to_2d(vector2{edgeMan.MaxBoxX * edgeMan.AdvanceX + edgeMan.MinX, depthPt->Y}).X;
|
||||
auto ff = proj::xform_to_2d(vector2{ edgeMan.MaxBoxX * edgeMan.AdvanceX + edgeMan.MinX, depthPt->Y });
|
||||
SDL_RenderDrawLine(winmain::Renderer, x1, pt.Y, x2, pt.Y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawComponentAabb()
|
||||
{
|
||||
SDL_SetRenderDrawColor(winmain::Renderer, 0, 50, 200, 255);
|
||||
for (auto cmp : pb::MainTable->ComponentList)
|
||||
{
|
||||
auto collCmp = dynamic_cast<TCollisionComponent*>(cmp);
|
||||
if (collCmp)
|
||||
{
|
||||
const auto& aabb = collCmp->AABB;
|
||||
auto pt1 = proj::xform_to_2d(vector2{ aabb.XMax, aabb.YMax });
|
||||
auto pt2 = proj::xform_to_2d(vector2{ aabb.XMin, aabb.YMin });
|
||||
SDL_Rect rect{ pt2.X,pt2.Y, pt1.X - pt2.X , pt1.Y - pt2.Y };
|
||||
SDL_RenderDrawRect(winmain::Renderer, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawCicleType(circle_type& circle)
|
||||
{
|
||||
vector2 linePt{ circle.Center.X + sqrt(circle.RadiusSq), circle.Center.Y };
|
||||
auto pt1 = proj::xform_to_2d(circle.Center);
|
||||
auto pt2 = proj::xform_to_2d(linePt);
|
||||
auto radius = abs(pt2.X - pt1.X);
|
||||
|
||||
SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, radius);
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawLineType(line_type& line)
|
||||
{
|
||||
auto pt1 = proj::xform_to_2d(line.Origin);
|
||||
auto pt2 = proj::xform_to_2d(line.End);
|
||||
|
||||
SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
|
||||
}
|
||||
|
||||
void DebugOverlay::DrawEdge(TEdgeSegment* edge)
|
||||
{
|
||||
if (options::Options.DebugOverlayCollisionMask)
|
||||
{
|
||||
TBall* refBall = nullptr;
|
||||
for (auto ball : pb::MainTable->BallList)
|
||||
{
|
||||
if (ball->ActiveFlag)
|
||||
{
|
||||
refBall = ball;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (refBall != nullptr && (refBall->CollisionMask & edge->CollisionGroup) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
auto line = dynamic_cast<TLine*>(edge);
|
||||
if (line)
|
||||
{
|
||||
DrawLineType(line->Line);
|
||||
return;
|
||||
}
|
||||
|
||||
auto circle = dynamic_cast<TCircle*>(edge);
|
||||
if (circle)
|
||||
{
|
||||
DrawCicleType(circle->Circle);
|
||||
return;
|
||||
}
|
||||
|
||||
auto flip = dynamic_cast<TFlipperEdge*>(edge);
|
||||
if (flip)
|
||||
{
|
||||
if (flip->ControlPointDirtyFlag)
|
||||
flip->set_control_points(flip->CurrentAngle);
|
||||
|
||||
DrawLineType(flip->LineA);
|
||||
DrawLineType(flip->LineB);
|
||||
DrawCicleType(flip->circlebase);
|
||||
DrawCicleType(flip->circleT1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
struct gdrv_bitmap8;
|
||||
struct circle_type;
|
||||
struct line_type;
|
||||
class TEdgeSegment;
|
||||
|
||||
class DebugOverlay
|
||||
{
|
||||
public:
|
||||
static void UnInit();
|
||||
static void DrawOverlay();
|
||||
private:
|
||||
static gdrv_bitmap8* dbScreen;
|
||||
|
||||
static void DrawCicleType(circle_type& circle);
|
||||
static void DrawLineType(line_type& line);
|
||||
static void DrawEdge(TEdgeSegment* edge);
|
||||
static void DrawBoxGrid();
|
||||
static void DrawAllEdges();
|
||||
static void DrawBallInfo();
|
||||
static void DrawAllSprites();
|
||||
static void DrawSoundPositions();
|
||||
static void DrawBallDepthSteps();
|
||||
static void DrawComponentAabb();
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
class EmbeddedData
|
||||
{
|
||||
public:
|
||||
static const char PB_MSGFT_bin_compressed_data_base85[5380 + 1];
|
||||
static const unsigned int SDL_GameControllerDB_compressed_data[63392 / 4];
|
||||
static const unsigned int SDL_GameControllerDB_compressed_size;
|
||||
};
|
||||
@@ -0,0 +1,361 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "GroupData.h"
|
||||
|
||||
#include "EmbeddedData.h"
|
||||
#include "fullscrn.h"
|
||||
#include "gdrv.h"
|
||||
#include "pb.h"
|
||||
#include "zdrv.h"
|
||||
|
||||
|
||||
EntryData::~EntryData()
|
||||
{
|
||||
if (Buffer)
|
||||
{
|
||||
if (EntryType == FieldTypes::Bitmap8bit)
|
||||
{
|
||||
delete reinterpret_cast<gdrv_bitmap8*>(Buffer);
|
||||
}
|
||||
else if (EntryType == FieldTypes::Bitmap16bit)
|
||||
{
|
||||
delete reinterpret_cast<zmap_header_type*>(Buffer);
|
||||
}
|
||||
else
|
||||
delete[] Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GroupData::GroupData(int groupId)
|
||||
{
|
||||
GroupId = groupId;
|
||||
}
|
||||
|
||||
void GroupData::AddEntry(EntryData* entry)
|
||||
{
|
||||
auto addEntry = true;
|
||||
switch (entry->EntryType)
|
||||
{
|
||||
case FieldTypes::GroupName:
|
||||
GroupName = entry->Buffer;
|
||||
break;
|
||||
case FieldTypes::Bitmap8bit:
|
||||
{
|
||||
auto srcBmp = reinterpret_cast<gdrv_bitmap8*>(entry->Buffer);
|
||||
if (srcBmp->BitmapType == BitmapTypes::Spliced)
|
||||
{
|
||||
// Get rid of spliced bitmap early on, to simplify render pipeline
|
||||
auto bmp = new gdrv_bitmap8(srcBmp->Width, srcBmp->Height, true);
|
||||
auto zMap = new zmap_header_type(srcBmp->Width, srcBmp->Height, srcBmp->Width);
|
||||
SplitSplicedBitmap(*srcBmp, *bmp, *zMap);
|
||||
|
||||
NeedsSort = true;
|
||||
addEntry = false;
|
||||
AddEntry(new EntryData(FieldTypes::Bitmap8bit, reinterpret_cast<char*>(bmp)));
|
||||
AddEntry(new EntryData(FieldTypes::Bitmap16bit, reinterpret_cast<char*>(zMap)));
|
||||
delete entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBitmap(srcBmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldTypes::Bitmap16bit:
|
||||
{
|
||||
SetZMap(reinterpret_cast<zmap_header_type*>(entry->Buffer));
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (addEntry)
|
||||
Entries.push_back(entry);
|
||||
}
|
||||
|
||||
void GroupData::FinalizeGroup()
|
||||
{
|
||||
if (NeedsSort)
|
||||
{
|
||||
// Entries within a group are sorted by EntryType, in ascending order.
|
||||
// Dat files follow this rule, zMaps inserted in the middle break it.
|
||||
NeedsSort = false;
|
||||
std::sort(Entries.begin(), Entries.end(), [](const EntryData* lhs, const EntryData* rhs)
|
||||
{
|
||||
return lhs->EntryType < rhs->EntryType;
|
||||
});
|
||||
Entries.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
gdrv_bitmap8* GroupData::GetBitmap(int resolution) const
|
||||
{
|
||||
return Bitmaps[resolution];
|
||||
}
|
||||
|
||||
zmap_header_type* GroupData::GetZMap(int resolution) const
|
||||
{
|
||||
return ZMaps[resolution];
|
||||
}
|
||||
|
||||
void GroupData::SplitSplicedBitmap(const gdrv_bitmap8& srcBmp, gdrv_bitmap8& bmp, zmap_header_type& zMap)
|
||||
{
|
||||
assertm(srcBmp.BitmapType == BitmapTypes::Spliced, "GroupData: wrong bitmap type");
|
||||
|
||||
std::memset(bmp.IndexedBmpPtr, 0xff, bmp.Stride * bmp.Height);
|
||||
bmp.XPosition = srcBmp.XPosition;
|
||||
bmp.YPosition = srcBmp.YPosition;
|
||||
bmp.Resolution = srcBmp.Resolution;
|
||||
|
||||
zdrv::fill(&zMap, zMap.Width, zMap.Height, 0, 0, 0xFFFF);
|
||||
zMap.Resolution = srcBmp.Resolution;
|
||||
|
||||
auto tableWidth = fullscrn::resolution_array[srcBmp.Resolution].TableWidth;
|
||||
auto src = reinterpret_cast<uint16_t*>(srcBmp.IndexedBmpPtr);
|
||||
auto srcChar = reinterpret_cast<char**>(&src);
|
||||
for (int dstInd = 0;;)
|
||||
{
|
||||
auto stride = static_cast<int16_t>(*src++);
|
||||
if (stride < 0)
|
||||
break;
|
||||
|
||||
// Stride is in terms of dst stride, hardcoded to match vScreen width in current resolution
|
||||
if (stride > bmp.Width)
|
||||
{
|
||||
stride += bmp.Width - tableWidth;
|
||||
assertm(stride >= 0, "Spliced bitmap: negative computed stride");
|
||||
}
|
||||
|
||||
dstInd += stride;
|
||||
for (auto count = *src++; count; count--)
|
||||
{
|
||||
auto depth = *src++;
|
||||
bmp.IndexedBmpPtr[dstInd] = **srcChar;
|
||||
zMap.ZPtr1[dstInd] = depth;
|
||||
|
||||
(*srcChar)++;
|
||||
dstInd++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupData::SetBitmap(gdrv_bitmap8* bmp)
|
||||
{
|
||||
assertm(Bitmaps[bmp->Resolution] == nullptr, "GroupData: bitmap override");
|
||||
Bitmaps[bmp->Resolution] = bmp;
|
||||
|
||||
auto zMap = ZMaps[bmp->Resolution];
|
||||
if (zMap)
|
||||
{
|
||||
assertm(bmp->Width == zMap->Width && bmp->Height == zMap->Height,
|
||||
"GroupData: mismatched bitmap/zMap dimensions");
|
||||
}
|
||||
}
|
||||
|
||||
void GroupData::SetZMap(zmap_header_type* zMap)
|
||||
{
|
||||
// Flip zMap to match with flipped non-indexed bitmaps
|
||||
zdrv::FlipZMapHorizontally(*zMap);
|
||||
|
||||
assertm(ZMaps[zMap->Resolution] == nullptr, "GroupData: zMap override");
|
||||
ZMaps[zMap->Resolution] = zMap;
|
||||
|
||||
auto bmp = Bitmaps[zMap->Resolution];
|
||||
if (bmp)
|
||||
{
|
||||
assertm(bmp->Width == zMap->Width && bmp->Height == zMap->Height,
|
||||
"GroupData: mismatched bitmap/zMap dimensions");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DatFile::~DatFile()
|
||||
{
|
||||
for (auto group : Groups)
|
||||
{
|
||||
if (!group)
|
||||
continue;
|
||||
|
||||
for (const auto entry : group->GetEntries())
|
||||
{
|
||||
delete entry;
|
||||
}
|
||||
delete group;
|
||||
}
|
||||
}
|
||||
|
||||
char* DatFile::field(int groupIndex, FieldTypes targetEntryType)
|
||||
{
|
||||
assertm(targetEntryType != FieldTypes::Bitmap8bit && targetEntryType != FieldTypes::Bitmap16bit,
|
||||
"partman: Use specific get for bitmaps");
|
||||
|
||||
auto group = Groups[groupIndex];
|
||||
for (const auto entry : group->GetEntries())
|
||||
{
|
||||
if (entry->EntryType == targetEntryType)
|
||||
{
|
||||
return entry->Buffer;
|
||||
}
|
||||
if (entry->EntryType > targetEntryType)
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
char* DatFile::field_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN)
|
||||
{
|
||||
assertm(targetEntryType != FieldTypes::Bitmap8bit && targetEntryType != FieldTypes::Bitmap16bit,
|
||||
"partman: Use specific get for bitmaps");
|
||||
|
||||
auto group = Groups[groupIndex];
|
||||
auto skipCount = 0;
|
||||
for (const auto entry : group->GetEntries())
|
||||
{
|
||||
if (entry->EntryType > targetEntryType)
|
||||
break;
|
||||
if (entry->EntryType == targetEntryType)
|
||||
if (skipCount++ == skipFirstN)
|
||||
return entry->Buffer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int DatFile::field_size_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN)
|
||||
{
|
||||
auto group = Groups[groupIndex];
|
||||
auto skipCount = 0;
|
||||
for (const auto entry : group->GetEntries())
|
||||
{
|
||||
if (entry->EntryType > targetEntryType)
|
||||
return 0;
|
||||
if (entry->EntryType == targetEntryType)
|
||||
if (skipCount++ == skipFirstN)
|
||||
return entry->FieldSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DatFile::field_size(int groupIndex, FieldTypes targetEntryType)
|
||||
{
|
||||
return field_size_nth(groupIndex, targetEntryType, 0);
|
||||
}
|
||||
|
||||
int DatFile::record_labeled(LPCSTR targetGroupName)
|
||||
{
|
||||
auto targetLength = strlen(targetGroupName);
|
||||
for (int groupIndex = static_cast<int>(Groups.size()) - 1; groupIndex >= 0; --groupIndex)
|
||||
{
|
||||
auto groupName = field(groupIndex, FieldTypes::GroupName);
|
||||
if (!groupName)
|
||||
continue;
|
||||
|
||||
auto index = 0u;
|
||||
for (; index < targetLength; index++)
|
||||
if (targetGroupName[index] != groupName[index])
|
||||
break;
|
||||
if (index == targetLength && !targetGroupName[index] && !groupName[index])
|
||||
return groupIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* DatFile::field_labeled(LPCSTR lpString, FieldTypes fieldType)
|
||||
{
|
||||
auto groupIndex = record_labeled(lpString);
|
||||
return groupIndex < 0 ? nullptr : field(groupIndex, fieldType);
|
||||
}
|
||||
|
||||
gdrv_bitmap8* DatFile::GetBitmap(int groupIndex)
|
||||
{
|
||||
auto group = Groups[groupIndex];
|
||||
return group->GetBitmap(fullscrn::GetResolution());
|
||||
}
|
||||
|
||||
zmap_header_type* DatFile::GetZMap(int groupIndex)
|
||||
{
|
||||
auto group = Groups[groupIndex];
|
||||
return group->GetZMap(fullscrn::GetResolution());
|
||||
}
|
||||
|
||||
void DatFile::Finalize()
|
||||
{
|
||||
if (!pb::FullTiltMode)
|
||||
{
|
||||
int groupIndex = record_labeled("pbmsg_ft");
|
||||
assertm(groupIndex < 0, "DatFile: pbmsg_ft is already in .dat");
|
||||
|
||||
// Load 3DPB font into dat to simplify pipeline
|
||||
auto rcData = reinterpret_cast<MsgFont*>(ImFontAtlas::DecompressCompressedBase85Data(
|
||||
EmbeddedData::PB_MSGFT_bin_compressed_data_base85));
|
||||
AddMsgFont(rcData, "pbmsg_ft");
|
||||
IM_FREE(rcData);
|
||||
|
||||
// PINBALL2.MID is an alternative font provided in 3DPB data
|
||||
// Scaled down because it is too large for top text box
|
||||
/*auto file = pb::make_path_name("PINBALL2.MID");
|
||||
auto fileHandle = fopenu(file.c_str(), "rb");
|
||||
fseek(fileHandle, 0, SEEK_END);
|
||||
auto fileSize = static_cast<uint32_t>(ftell(fileHandle));
|
||||
auto rcData = reinterpret_cast<MsgFont*>(new uint8_t[fileSize]);
|
||||
fseek(fileHandle, 0, SEEK_SET);
|
||||
fread(rcData, 1, fileSize, fileHandle);
|
||||
fclose(fileHandle);
|
||||
auto groupId = Groups.back()->GroupId + 1u;
|
||||
AddMsgFont(rcData, "pbmsg_ft");
|
||||
delete[] rcData;
|
||||
for (auto i = groupId; i < Groups.size(); i++)
|
||||
Groups[i]->GetBitmap(0)->ScaleIndexed(0.84f, 0.84f);*/
|
||||
}
|
||||
|
||||
for (auto group : Groups)
|
||||
{
|
||||
group->FinalizeGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void DatFile::AddMsgFont(MsgFont* font, const std::string& fontName)
|
||||
{
|
||||
auto groupId = Groups.back()->GroupId + 1;
|
||||
auto ptrToData = reinterpret_cast<char*>(font->Data);
|
||||
for (auto charInd = 32; charInd < 128; charInd++, groupId++)
|
||||
{
|
||||
auto curChar = reinterpret_cast<MsgFontChar*>(ptrToData);
|
||||
assertm(curChar->Width == font->CharWidths[charInd], "Score: mismatched font width");
|
||||
ptrToData += curChar->Width * font->Height + 1;
|
||||
|
||||
auto bmp = new gdrv_bitmap8(curChar->Width, font->Height, true);
|
||||
auto srcPtr = curChar->Data;
|
||||
auto dstPtr = &bmp->IndexedBmpPtr[bmp->Stride * (bmp->Height - 1)];
|
||||
for (auto y = 0; y < font->Height; ++y)
|
||||
{
|
||||
memcpy(dstPtr, srcPtr, curChar->Width);
|
||||
srcPtr += curChar->Width;
|
||||
dstPtr -= bmp->Stride;
|
||||
}
|
||||
|
||||
auto group = new GroupData(groupId);
|
||||
group->AddEntry(new EntryData(FieldTypes::Bitmap8bit, reinterpret_cast<char*>(bmp)));
|
||||
if (charInd == 32)
|
||||
{
|
||||
// First font group holds font name and gap width
|
||||
auto groupName = new char[fontName.length() + 1];
|
||||
strcpy(groupName, fontName.c_str());
|
||||
group->AddEntry(new EntryData(FieldTypes::GroupName, groupName));
|
||||
|
||||
auto gaps = new char[2];
|
||||
*reinterpret_cast<int16_t*>(gaps) = font->GapWidth;
|
||||
group->AddEntry(new EntryData(FieldTypes::ShortArray, gaps));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto groupName = new char[30];
|
||||
sprintf(groupName, "char %d='%c'", charInd, charInd);
|
||||
group->AddEntry(new EntryData(FieldTypes::GroupName, groupName));
|
||||
}
|
||||
|
||||
Groups.push_back(group);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
struct zmap_header_type;
|
||||
struct gdrv_bitmap8;
|
||||
|
||||
|
||||
enum class FieldTypes : int16_t
|
||||
{
|
||||
// One 16 bit signed integer
|
||||
ShortValue = 0,
|
||||
|
||||
// Sprite bitmap, 8bpp, indexed color
|
||||
Bitmap8bit = 1,
|
||||
|
||||
Unknown2 = 2,
|
||||
|
||||
// Group name, char[]. Not all groups have names.
|
||||
GroupName = 3,
|
||||
|
||||
Unknown4 = 4,
|
||||
|
||||
// Palette, contains 256 RBGA 4-byte colors.
|
||||
Palette = 5,
|
||||
|
||||
Unknown6 = 6,
|
||||
|
||||
Unknown7 = 7,
|
||||
|
||||
Unknown8 = 8,
|
||||
|
||||
// String, char[]
|
||||
String = 9,
|
||||
|
||||
// Array of 16 bit signed integers
|
||||
ShortArray = 10,
|
||||
|
||||
// Array of 32 bit floats
|
||||
FloatArray = 11,
|
||||
|
||||
// Sprite depth map, 16bpp, unsigned
|
||||
Bitmap16bit = 12,
|
||||
};
|
||||
|
||||
struct EntryData
|
||||
{
|
||||
EntryData() = default;
|
||||
|
||||
EntryData(FieldTypes entryType, char* buffer): EntryType(entryType), FieldSize(-1), Buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
~EntryData();
|
||||
FieldTypes EntryType{};
|
||||
int FieldSize{};
|
||||
char* Buffer{};
|
||||
};
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MsgFontChar
|
||||
{
|
||||
uint8_t Width;
|
||||
char Data[1];
|
||||
};
|
||||
|
||||
struct MsgFont
|
||||
{
|
||||
int16_t GapWidth;
|
||||
int16_t Unknown1;
|
||||
int16_t Height;
|
||||
uint8_t CharWidths[128];
|
||||
MsgFontChar Data[1];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(MsgFont) == 136, "Wrong size of MsgFont");
|
||||
|
||||
|
||||
class GroupData
|
||||
{
|
||||
public:
|
||||
int GroupId;
|
||||
std::string GroupName;
|
||||
|
||||
GroupData(int groupId);
|
||||
void AddEntry(EntryData* entry);
|
||||
void FinalizeGroup();
|
||||
const std::vector<EntryData*>& GetEntries() const { return Entries; }
|
||||
const EntryData* GetEntry(size_t index) const { return Entries[index]; }
|
||||
size_t EntryCount() const { return Entries.size(); }
|
||||
void ReserveEntries(size_t count) { Entries.reserve(count); }
|
||||
gdrv_bitmap8* GetBitmap(int resolution) const;
|
||||
zmap_header_type* GetZMap(int resolution) const;
|
||||
|
||||
private:
|
||||
std::vector<EntryData*> Entries;
|
||||
gdrv_bitmap8* Bitmaps[3]{};
|
||||
zmap_header_type* ZMaps[3]{};
|
||||
bool NeedsSort = false;
|
||||
|
||||
static void SplitSplicedBitmap(const gdrv_bitmap8& srcBmp, gdrv_bitmap8& bmp, zmap_header_type& zMap);
|
||||
|
||||
void SetBitmap(gdrv_bitmap8* bmp);
|
||||
void SetZMap(zmap_header_type* zMap);
|
||||
};
|
||||
|
||||
|
||||
class DatFile
|
||||
{
|
||||
public:
|
||||
std::string AppName;
|
||||
std::string Description;
|
||||
std::vector<GroupData*> Groups;
|
||||
|
||||
~DatFile();
|
||||
char* field_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN);
|
||||
char* field(int groupIndex, FieldTypes entryType);
|
||||
int field_size_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN);
|
||||
int field_size(int groupIndex, FieldTypes targetEntryType);
|
||||
int record_labeled(LPCSTR targetGroupName);
|
||||
char* field_labeled(LPCSTR lpString, FieldTypes fieldType);
|
||||
gdrv_bitmap8* GetBitmap(int groupIndex);
|
||||
zmap_header_type* GetZMap(int groupIndex);
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
void AddMsgFont(MsgFont* font, const std::string& fontName);
|
||||
};
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 972 B |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -1,50 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="objlist_class">
|
||||
<DisplayString>{{ size={ListPtr->Count} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]" ExcludeView="simple">ListPtr->Count</Item>
|
||||
<Item Name="[capacity]" ExcludeView="simple">ListPtr->Size</Item>
|
||||
<ArrayItems>
|
||||
<Size>ListPtr->Size</Size>
|
||||
<ValuePointer>ListPtr->Array</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="objlist_struct1">
|
||||
<DisplayString>{{ size={Count} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]" ExcludeView="simple">Count</Item>
|
||||
<Item Name="[capacity]" ExcludeView="simple">Size</Item>
|
||||
<ArrayItems>
|
||||
<Size>Size</Size>
|
||||
<ValuePointer>Array</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="datFileStruct">
|
||||
<DisplayString>{{ NumberOfGroups={NumberOfGroups} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[NumberOfGroups]" ExcludeView="simple">NumberOfGroups</Item>
|
||||
<Item Name="[Description]" ExcludeView="simple">Description</Item>
|
||||
<ArrayItems>
|
||||
<Size>NumberOfGroups</Size>
|
||||
<ValuePointer>GroupData</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="datGroupData">
|
||||
<DisplayString>{{ EntryCount={EntryCount} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[EntryCount]" ExcludeView="simple">EntryCount</Item>
|
||||
<ArrayItems>
|
||||
<Size>EntryCount</Size>
|
||||
<ValuePointer>Entries</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
|
||||
<Type Name="ColorRgba">
|
||||
<DisplayString>{{ Color:{Color} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[Alpha]" ExcludeView="simple">Color>>alphaOffset & 255</Item>
|
||||
<Item Name="[Red]" ExcludeView="simple">Color>>redOffset & 255</Item>
|
||||
<Item Name="[Green]" ExcludeView="simple">Color>>greenOffset & 255</Item>
|
||||
<Item Name="[Blue]" ExcludeView="simple">Color>>blueOffset & 255</Item>
|
||||
<Item Name="[Color]" ExcludeView="simple">Color</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="vector3">
|
||||
<DisplayString>{{ X={X} Y={Y} Z={Z} }}</DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -1,266 +1,154 @@
|
||||
#include "pch.h"
|
||||
#include "options.h"
|
||||
#include "Sound.h"
|
||||
|
||||
#include "pinball.h"
|
||||
#include "WaveMix.h"
|
||||
#include "winmain.h"
|
||||
#include "maths.h"
|
||||
|
||||
int Sound::num_channels;
|
||||
HWND Sound::wavemix_window;
|
||||
HANDLE Sound::pMem;
|
||||
unsigned int Sound::enabled_flag;
|
||||
int Sound::channel_time[8];
|
||||
MIXWAVE* Sound::channel_wavePtr[8];
|
||||
void (*Sound::callback_ptr)(int, MIXWAVE*, int);
|
||||
HMODULE Sound::HInstance;
|
||||
bool Sound::enabled_flag = false;
|
||||
std::vector<ChannelInfo> Sound::Channels{};
|
||||
int Sound::Volume = MIX_MAX_VOLUME;
|
||||
bool Sound::MixOpen = false;
|
||||
|
||||
|
||||
int Sound::Init(HINSTANCE hInstance, int voices, void (* someFuncPtr)(int, MIXWAVE*, int))
|
||||
void Sound::Init(bool mixOpen, int channels, bool enableFlag, int volume)
|
||||
{
|
||||
WNDCLASSA WndClass;
|
||||
char FileName[300];
|
||||
|
||||
int channelCount = voices;
|
||||
if (voices > 8)
|
||||
channelCount = 8;
|
||||
num_channels = channelCount;
|
||||
if (wavemix_window || pMem)
|
||||
return 0;
|
||||
enabled_flag = -1;
|
||||
for (int i = 0; i < channelCount; ++i)
|
||||
{
|
||||
channel_time[i] = 0;
|
||||
channel_wavePtr[i] = nullptr;
|
||||
}
|
||||
callback_ptr = someFuncPtr;
|
||||
if (!someFuncPtr)
|
||||
callback_ptr = NullCallback;
|
||||
|
||||
pinball::make_path_name(FileName, "wavemix.inf", 300);
|
||||
FILE* wavemixIniFile = nullptr;
|
||||
fopen_s(&wavemixIniFile, FileName, "r");
|
||||
if (wavemixIniFile)
|
||||
{
|
||||
fclose(wavemixIniFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxA(winmain::hwnd_frame, pinball::get_rc_string(42, 0), pinball::WindowName, 0x2000u);
|
||||
}
|
||||
|
||||
WndClass.style = 0;
|
||||
WndClass.lpfnWndProc = SoundCallBackWndProc;
|
||||
WndClass.cbClsExtra = 0;
|
||||
WndClass.cbWndExtra = 0;
|
||||
WndClass.hInstance = hInstance;
|
||||
WndClass.hIcon = nullptr;
|
||||
WndClass.hCursor = nullptr;
|
||||
WndClass.hbrBackground = nullptr;
|
||||
WndClass.lpszMenuName = nullptr;
|
||||
WndClass.lpszClassName = "WaveMixSoundGuy";
|
||||
RegisterClassA(&WndClass);
|
||||
wavemix_window = CreateWindowExA(
|
||||
0,
|
||||
"WaveMixSoundGuy",
|
||||
nullptr,
|
||||
0x80000000,
|
||||
0x80000000,
|
||||
0,
|
||||
0x80000000,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
hInstance,
|
||||
nullptr);
|
||||
if (!wavemix_window)
|
||||
return 0;
|
||||
|
||||
HInstance = hInstance;
|
||||
HANDLE hMixSession = WaveMix::Init();
|
||||
pMem = hMixSession;
|
||||
if (!hMixSession)
|
||||
return 0;
|
||||
WaveMix::OpenChannel(hMixSession, num_channels, 2u);
|
||||
return 1;
|
||||
MixOpen = mixOpen;
|
||||
Volume = volume;
|
||||
SetChannels(channels);
|
||||
Enable(enableFlag);
|
||||
}
|
||||
|
||||
void Sound::Enable(int channelFrom, int channelTo, int enableFlag)
|
||||
void Sound::Enable(bool enableFlag)
|
||||
{
|
||||
if (pMem)
|
||||
{
|
||||
if (channelTo >= num_channels)
|
||||
channelTo = num_channels - 1;
|
||||
|
||||
if (channelFrom >= 0 && channelTo < num_channels)
|
||||
{
|
||||
for (int index = channelFrom; index <= channelTo; ++index)
|
||||
{
|
||||
int channelFlag = 1 << index;
|
||||
if (enableFlag)
|
||||
{
|
||||
enabled_flag |= channelFlag;
|
||||
}
|
||||
else
|
||||
{
|
||||
enabled_flag &= ~channelFlag;
|
||||
Flush(index, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::Idle()
|
||||
{
|
||||
if (pMem)
|
||||
WaveMix::Pump();
|
||||
enabled_flag = enableFlag;
|
||||
if (MixOpen && !enableFlag)
|
||||
Mix_HaltChannel(-1);
|
||||
}
|
||||
|
||||
void Sound::Activate()
|
||||
{
|
||||
if (pMem)
|
||||
WaveMix::Activate(pMem, true);
|
||||
if (MixOpen)
|
||||
Mix_Resume(-1);
|
||||
}
|
||||
|
||||
void Sound::Deactivate()
|
||||
{
|
||||
if (pMem)
|
||||
WaveMix::Activate(pMem, false);
|
||||
if (MixOpen)
|
||||
Mix_Pause(-1);
|
||||
}
|
||||
|
||||
void Sound::Close()
|
||||
{
|
||||
if (wavemix_window)
|
||||
{
|
||||
DestroyWindow(wavemix_window);
|
||||
wavemix_window = nullptr;
|
||||
}
|
||||
if (pMem)
|
||||
{
|
||||
WaveMix::CloseChannel(pMem, 0, 1);
|
||||
WaveMix::CloseSession(pMem);
|
||||
pMem = nullptr;
|
||||
}
|
||||
Enable(false);
|
||||
Channels.clear();
|
||||
}
|
||||
|
||||
void Sound::PlaySound(MIXWAVE* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, __int16 loops)
|
||||
void Sound::PlaySound(Mix_Chunk* wavePtr, int time, TPinballComponent* soundSource, const char* info)
|
||||
{
|
||||
MIXPLAYPARAMS mixParams{};
|
||||
|
||||
if (!pMem)
|
||||
return;
|
||||
|
||||
if (maxChannel >= num_channels)
|
||||
maxChannel = num_channels - 1;
|
||||
if (!wavePtr || minChannel < 0 || maxChannel >= num_channels)
|
||||
return;
|
||||
|
||||
if ((dwFlags & 0x8000) != 0 && num_channels > 0)
|
||||
if (MixOpen && wavePtr && enabled_flag)
|
||||
{
|
||||
int index2 = 0;
|
||||
bool ok = false;
|
||||
while (channel_wavePtr[index2] != wavePtr)
|
||||
if (Mix_Playing(-1) == num_channels)
|
||||
{
|
||||
if (++index2 >= num_channels)
|
||||
auto cmp = [](const ChannelInfo& a, const ChannelInfo& b)
|
||||
{
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
return a.TimeStamp < b.TimeStamp;
|
||||
};
|
||||
auto min = std::min_element(Channels.begin(), Channels.end(), cmp);
|
||||
auto oldestChannel = static_cast<int>(std::distance(Channels.begin(), min));
|
||||
Mix_HaltChannel(oldestChannel);
|
||||
}
|
||||
if (!ok)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int playChannel = minChannel;
|
||||
if (minChannel < maxChannel)
|
||||
{
|
||||
int curChannel = minChannel;
|
||||
do
|
||||
auto channel = Mix_PlayChannel(-1, wavePtr, 0);
|
||||
if (channel != -1)
|
||||
{
|
||||
++curChannel;
|
||||
if ((1 << curChannel) & enabled_flag &&
|
||||
channel_time[curChannel] < channel_time[playChannel])
|
||||
Channels[channel].TimeStamp = time;
|
||||
if (options::Options.SoundStereo)
|
||||
{
|
||||
playChannel = curChannel;
|
||||
}
|
||||
}
|
||||
while (curChannel < maxChannel);
|
||||
}
|
||||
// Positional audio uses collision grid 2D coordinates normalized to [0, 1]
|
||||
// Point (0, 0) is bottom left table corner; point (1, 1) is top right table corner.
|
||||
// Z is defined as: 0 at table level, positive axis goes up from table surface.
|
||||
|
||||
if ((1 << playChannel) & enabled_flag)
|
||||
{
|
||||
mixParams.hMixSession = pMem;
|
||||
mixParams.hWndNotify = wavemix_window;
|
||||
mixParams.dwFlags = dwFlags;
|
||||
mixParams.wSize = 28;
|
||||
mixParams.wLoops = loops;
|
||||
mixParams.iChannel = playChannel;
|
||||
mixParams.lpMixWave = wavePtr;
|
||||
// Get the source sound position.
|
||||
// Sound without position are assumed to be at the center top of the table.
|
||||
vector3 soundPos{};
|
||||
if (soundSource)
|
||||
{
|
||||
auto soundPos2D = soundSource->get_coordinates();
|
||||
soundPos = {soundPos2D.X, soundPos2D.Y, 0.0f};
|
||||
}
|
||||
else
|
||||
{
|
||||
soundPos = {0.5f, 1.0f, 0.0f};
|
||||
}
|
||||
Channels[channel].Position = soundPos;
|
||||
|
||||
callback_ptr(1, wavePtr, playChannel);
|
||||
channel_time[playChannel] = timeGetTime();
|
||||
channel_wavePtr[playChannel] = wavePtr;
|
||||
WaveMix::Play(&mixParams);
|
||||
}
|
||||
}
|
||||
// Listener is positioned at the bottom center of the table,
|
||||
// at 0.5 height, so roughly a table half - length.
|
||||
vector3 playerPos = {0.5f, 0.0f, 0.5f};
|
||||
auto soundDir = maths::vector_sub(soundPos, playerPos);
|
||||
|
||||
MIXWAVE* Sound::LoadWaveFile(LPCSTR lpName)
|
||||
{
|
||||
return pMem ? WaveMix::OpenWave(pMem, lpName, HInstance, 1u) : nullptr;
|
||||
}
|
||||
// Find sound angle from positive Y axis in clockwise direction with atan2
|
||||
// Remap atan2 output from (-Pi, Pi] to [0, 2 * Pi)
|
||||
auto angle = fmodf(atan2(soundDir.X, soundDir.Y) + Pi * 2, Pi * 2);
|
||||
auto angleDeg = angle * 180.0f / Pi;
|
||||
auto angleSdl = static_cast<Sint16>(angleDeg);
|
||||
|
||||
void Sound::FreeSound(MIXWAVE* wave)
|
||||
{
|
||||
if (wave && pMem)
|
||||
WaveMix::FreeWave(pMem, wave);
|
||||
}
|
||||
// Distance from listener to the sound position is roughly in the [0, ~1.22] range.
|
||||
// Remap to [0, 122] by multiplying by 100 and cast to an integer.
|
||||
auto distance = static_cast<Uint8>(100.0f * maths::magnitude(soundDir));
|
||||
|
||||
void Sound::Flush(int channelFrom, int channelTo)
|
||||
{
|
||||
if (pMem)
|
||||
{
|
||||
if (channelTo >= num_channels)
|
||||
channelTo = num_channels - 1;
|
||||
// Mix_SetPosition expects an angle in (Sint16)degrees, where
|
||||
// angle 0 is due north, and rotates clockwise as the value increases.
|
||||
// Mix_SetPosition expects a (Uint8)distance from 0 (near) to 255 (far).
|
||||
Mix_SetPosition(channel, angleSdl, distance);
|
||||
|
||||
if (channelFrom >= 0 && channelTo < num_channels)
|
||||
{
|
||||
for (auto index = channelFrom; index <= channelTo; index++)
|
||||
{
|
||||
WaveMix::FlushChannel(pMem, index, 0);
|
||||
channel_time[index] = 0;
|
||||
channel_wavePtr[index] = nullptr;
|
||||
// Output position of each sound emitted so we can verify
|
||||
// the sanity of the implementation.
|
||||
/*printf("X: %3.3f Y: %3.3f Angle: %3.3f Distance: %3d, Object: %s\n",
|
||||
soundPos.X,
|
||||
soundPos.Y,
|
||||
angleDeg,
|
||||
distance,
|
||||
info
|
||||
);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::NullCallback(int a1, MIXWAVE* a2, int a3)
|
||||
Mix_Chunk* Sound::LoadWaveFile(const std::string& lpName)
|
||||
{
|
||||
if (!MixOpen)
|
||||
return nullptr;
|
||||
|
||||
auto wavFile = fopenu(lpName.c_str(), "r");
|
||||
if (!wavFile)
|
||||
return nullptr;
|
||||
fclose(wavFile);
|
||||
|
||||
return Mix_LoadWAV(lpName.c_str());
|
||||
}
|
||||
|
||||
LRESULT Sound::SoundCallBackWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
void Sound::FreeSound(Mix_Chunk* wave)
|
||||
{
|
||||
if (Msg != MM_WOM_DONE)
|
||||
return DefWindowProcA(hWnd, Msg, wParam, lParam);
|
||||
if (MixOpen && wave)
|
||||
Mix_FreeChunk(wave);
|
||||
}
|
||||
|
||||
auto wavePtr = reinterpret_cast<MIXWAVE*>(lParam);
|
||||
int channel = -1;
|
||||
for (auto index = 0; index < num_channels; ++index)
|
||||
{
|
||||
if (channel_wavePtr[index] == wavePtr &&
|
||||
(channel < 0 || channel_time[index] < channel_time[channel]))
|
||||
{
|
||||
channel = index;
|
||||
}
|
||||
}
|
||||
void Sound::SetChannels(int channels)
|
||||
{
|
||||
if (channels <= 0)
|
||||
channels = 8;
|
||||
|
||||
if (channel >= 0)
|
||||
{
|
||||
channel_time[channel] = 0;
|
||||
channel_wavePtr[channel] = nullptr;
|
||||
}
|
||||
num_channels = channels;
|
||||
Channels.resize(num_channels);
|
||||
if (MixOpen)
|
||||
Mix_AllocateChannels(num_channels);
|
||||
SetVolume(Volume);
|
||||
}
|
||||
|
||||
callback_ptr(2, wavePtr, channel);
|
||||
return 0;
|
||||
void Sound::SetVolume(int volume)
|
||||
{
|
||||
Volume = volume;
|
||||
if (MixOpen)
|
||||
Mix_Volume(-1, volume);
|
||||
}
|
||||
@@ -1,28 +1,31 @@
|
||||
#pragma once
|
||||
#include "WaveMix.h"
|
||||
#include "maths.h"
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
struct ChannelInfo
|
||||
{
|
||||
int TimeStamp;
|
||||
vector2 Position;
|
||||
};
|
||||
|
||||
class Sound
|
||||
{
|
||||
public:
|
||||
static int Init(HINSTANCE hInstance, int voices, void (* someFuncPtr)(int, MIXWAVE*, int));
|
||||
static void Enable(int channelFrom, int channelTo, int enableFlag);
|
||||
static void Idle();
|
||||
static std::vector<ChannelInfo> Channels;
|
||||
|
||||
static void Init(bool mixOpen, int channels, bool enableFlag, int volume);
|
||||
static void Enable(bool enableFlag);
|
||||
static void Activate();
|
||||
static void Deactivate();
|
||||
static void Close();
|
||||
static void PlaySound(MIXWAVE* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, __int16 loops);
|
||||
static MIXWAVE* LoadWaveFile(LPCSTR lpName);
|
||||
static void FreeSound(MIXWAVE* wave);
|
||||
static void Flush(int channelFrom, int channelTo);
|
||||
static void NullCallback(int a1, MIXWAVE* a2, int a3);
|
||||
static LRESULT __stdcall SoundCallBackWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
||||
static void PlaySound(Mix_Chunk* wavePtr, int time, TPinballComponent *soundSource, const char* info);
|
||||
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
|
||||
static void FreeSound(Mix_Chunk* wave);
|
||||
static void SetChannels(int channels);
|
||||
static void SetVolume(int volume);
|
||||
private:
|
||||
static int num_channels;
|
||||
static HWND wavemix_window;
|
||||
static HANDLE pMem;
|
||||
static unsigned int enabled_flag;
|
||||
static int channel_time[8];
|
||||
static MIXWAVE* channel_wavePtr[8];
|
||||
static void (* callback_ptr)(int, MIXWAVE*, int);
|
||||
static HMODULE HInstance;
|
||||
static bool enabled_flag;
|
||||
static int Volume;
|
||||
static bool MixOpen;
|
||||
};
|
||||
@@ -1,83 +1,48 @@
|
||||
// SpaceCadetPinball.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "objlist_class.h"
|
||||
#include "partman.h"
|
||||
#include "gdrv.h"
|
||||
#include "loader.h"
|
||||
#include "pb.h"
|
||||
#include "pinball.h"
|
||||
#include "score.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TTextBox.h"
|
||||
#include "winmain.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::string cmdLine;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
// Testing with UI
|
||||
char cmdLine[1]{};
|
||||
WinMain(GetModuleHandleA(nullptr), 0, cmdLine, 10);
|
||||
return 0;
|
||||
if (i > 1)
|
||||
cmdLine += " ";
|
||||
cmdLine += argv[i];
|
||||
}
|
||||
|
||||
std::cout << "Hello World!\n";
|
||||
gdrv::init(0, 0);
|
||||
auto dib = gdrv::DibCreate(8, 1, 1);
|
||||
gdrv::DibSetUsage(dib, 0, 1);
|
||||
|
||||
objlist_class d = objlist_class(2, 4);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
d.Add((void*)i);
|
||||
}
|
||||
d.Delete((void*)3);
|
||||
|
||||
auto xx = sizeof(datFileHeader);
|
||||
|
||||
lstrcpyA(winmain::DatFileName, "PINBALL.DAT");
|
||||
pb::init();
|
||||
auto datFile = pb::record_table;
|
||||
|
||||
assert(partman::field_size_nth(datFile, 0, datFieldTypes::String, 0) == 43);
|
||||
assert(partman::field_size_nth(datFile, 2, datFieldTypes::Palette, 0) == 1024);
|
||||
assert(partman::field_size_nth(datFile, 101, datFieldTypes::FloatArray, 4) == 32);
|
||||
|
||||
assert(strcmp(partman::field(datFile, 0, datFieldTypes::String), "3D-Pinball: Copyright 1994, Cinematronics") == 0);
|
||||
assert(strcmp(partman::field(datFile, 540, datFieldTypes::GroupName), "table_objects") == 0);
|
||||
|
||||
assert(partman::record_labeled(datFile, "background") == 2);
|
||||
assert(partman::record_labeled(datFile, "a_bump1") == 372);
|
||||
|
||||
assert(memcmp(partman::field_labeled(datFile, "table_size", datFieldTypes::ShortArray), new short[2]{ 600, 416 }, 2 * 2) == 0);
|
||||
|
||||
//loader::error(25, 26);
|
||||
loader::get_sound_id(18);
|
||||
visualStruct visual1{};
|
||||
loader::material(96, &visual1);
|
||||
loader::query_visual(283, 0, &visual1);
|
||||
visualKickerStruct kicker1{};
|
||||
loader::kicker(509, &kicker1);
|
||||
|
||||
auto score1 = score::create("score1", nullptr);
|
||||
|
||||
auto pinballTable = pb::MainTable;
|
||||
//pinballTable->find_component(1);
|
||||
|
||||
for (int i = 0; i < 190; i++)
|
||||
{
|
||||
auto rsc = pinball::get_rc_string(i, 0);
|
||||
if (rsc)
|
||||
printf_s("%d:\t%s\n", i, rsc);
|
||||
}
|
||||
|
||||
//DatParser::Parse(dataFileName);
|
||||
std::cout << "Goodby World!\n";
|
||||
return winmain::WinMain(cmdLine.c_str());
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
// Windows subsystem main
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||
{
|
||||
return winmain::WinMain(lpCmdLine);
|
||||
}
|
||||
|
||||
// fopen to _wfopen adapter, for UTF-8 paths
|
||||
FILE* fopenu(const char* path, const char* opt)
|
||||
{
|
||||
wchar_t* wideArgs[2]{};
|
||||
for (auto& arg : wideArgs)
|
||||
{
|
||||
auto src = wideArgs[0] ? opt : path;
|
||||
auto length = MultiByteToWideChar(CP_UTF8, 0, src, -1, nullptr, 0);
|
||||
arg = new wchar_t[length];
|
||||
MultiByteToWideChar(CP_UTF8, 0, src, -1, arg, length);
|
||||
}
|
||||
|
||||
auto fileHandle = _wfopen(wideArgs[0], wideArgs[1]);
|
||||
for (auto arg : wideArgs)
|
||||
delete[] arg;
|
||||
|
||||
return fileHandle;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
|
||||
// Debug program: F5 or Debug > Start Debugging menu
|
||||
|
||||
|
||||
@@ -1,604 +1 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Russian (Russia) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
|
||||
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
|
||||
#pragma code_page(1251)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // Russian (Russia) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Menu
|
||||
//
|
||||
|
||||
MENU_1 MENU
|
||||
BEGIN
|
||||
POPUP "&Game"
|
||||
BEGIN
|
||||
MENUITEM "&New Game\tF2", Menu1_New_Game
|
||||
MENUITEM "&Launch Ball", Menu1_Launch_Ball
|
||||
MENUITEM "&Pause/Resume Game\tF3", Menu1_Pause_Resume_Game
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "&High Scores...", Menu1_High_Scores
|
||||
MENUITEM "&Demo", Menu1_Demo
|
||||
MENUITEM "E&xit", Menu1_Exit
|
||||
END
|
||||
POPUP "&Options"
|
||||
BEGIN
|
||||
MENUITEM "&Full Screen\tF4", Menu1_Full_Screen
|
||||
MENUITEM "Select &Table", Menu1_Select_Table
|
||||
POPUP "Select &Players"
|
||||
BEGIN
|
||||
MENUITEM "&1 Player", Menu1_1Player
|
||||
MENUITEM "&2 Players", Menu1_2Players
|
||||
MENUITEM "&3 Players", Menu1_3Players
|
||||
MENUITEM "&4 Players", Menu1_4Players
|
||||
END
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "&Sounds", Menu1_Sounds
|
||||
MENUITEM "&Music", Menu1_Music
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "P&layer Controls...\tF8", Menu1_Player_Controls
|
||||
END
|
||||
POPUP "&Help"
|
||||
BEGIN
|
||||
MENUITEM "&Help Topics\tF1", Menu1_Help_Topics
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "&About Pinball", Menu1_About_Pinball
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RCDATA
|
||||
//
|
||||
|
||||
PBMSG_FT RCDATA "PB_MSGFT.bin"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
ICON_1 ICON "icon_1.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
DIALOG_1 DIALOG 18, 18, 157, 78
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
|
||||
CAPTION "About"
|
||||
BEGIN
|
||||
CTEXT "3D-Pinball for Windows",-1,11,6,135,8
|
||||
CTEXT "Version 1.0",-1,40,37,76,8
|
||||
ICON "ICON_1",-1,69,18,16,16
|
||||
DEFPUSHBUTTON "Ok",1,66,60,24,12
|
||||
END
|
||||
|
||||
DLG_HIGHSCORES DIALOGEX 20, 22, 274, 141
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "High Scores"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",DLG_HIGHSCORES_Ok,109,120,50,14
|
||||
PUSHBUTTON "Cancel",DLG_HIGHSCORES_Cancel,163,120,50,14
|
||||
PUSHBUTTON "&Clear",DLG_HIGHSCORES_Clear,217,120,50,14
|
||||
LTEXT "Name",701,49,7,24,8
|
||||
LTEXT "Score",702,200,7,20,8
|
||||
LTEXT "Rank",703,13,7,20,8
|
||||
RTEXT "1",704,13,31,16,12
|
||||
RTEXT "2",705,13,47,16,12
|
||||
RTEXT "3",706,13,63,16,12
|
||||
RTEXT "4",707,13,79,16,12
|
||||
RTEXT "5",708,13,95,16,12
|
||||
LTEXT "",DLG_HIGHSCORES_StaticName1,49,31,120,12
|
||||
LTEXT "",DLG_HIGHSCORES_StaticName2,49,47,120,12
|
||||
LTEXT "",DLG_HIGHSCORES_StaticName3,49,63,120,12
|
||||
LTEXT "",DLG_HIGHSCORES_StaticName4,49,79,120,12
|
||||
LTEXT "",DLG_HIGHSCORES_StaticName5,49,95,120,12
|
||||
RTEXT "",DLG_HIGHSCORES_Score1,180,31,58,12
|
||||
RTEXT "",DLG_HIGHSCORES_Score2,180,47,58,12
|
||||
RTEXT "",DLG_HIGHSCORES_Score3,180,63,58,12
|
||||
RTEXT "",DLG_HIGHSCORES_Score4,180,79,58,12
|
||||
RTEXT "",DLG_HIGHSCORES_Score5,180,95,58,12
|
||||
EDITTEXT DLG_HIGHSCORES_EditName1,47,29,120,12,WS_DISABLED
|
||||
EDITTEXT DLG_HIGHSCORES_EditName2,47,45,120,12,WS_DISABLED
|
||||
EDITTEXT DLG_HIGHSCORES_EditName3,47,61,120,12,WS_DISABLED
|
||||
EDITTEXT DLG_HIGHSCORES_EditName4,47,77,120,12,WS_DISABLED
|
||||
EDITTEXT DLG_HIGHSCORES_EditName5,47,93,120,12,WS_DISABLED
|
||||
CONTROL "",606,"Static",SS_BLACKFRAME,7,21,259,92
|
||||
END
|
||||
|
||||
EDIT_COMPONENTS DIALOG 0, 0, 312, 94
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Edit Pinball Components FOR TESTING ONLY"
|
||||
BEGIN
|
||||
LISTBOX 100,1,12,68,79,LBS_SORT | WS_VSCROLL | WS_TABSTOP
|
||||
DEFPUSHBUTTON "OK",1,81,75,41,14
|
||||
GROUPBOX "",103,136,0,175,93
|
||||
EDITTEXT 130,141,10,34,12
|
||||
EDITTEXT 131,141,23,34,12
|
||||
EDITTEXT 132,141,36,34,12
|
||||
EDITTEXT 133,141,49,34,12
|
||||
EDITTEXT 134,141,62,34,12
|
||||
EDITTEXT 135,141,75,34,12
|
||||
LTEXT "Text",136,178,12,130,8
|
||||
LTEXT "Text",137,178,25,130,8
|
||||
LTEXT "Text",138,178,38,130,8
|
||||
LTEXT "Text",139,178,51,130,8
|
||||
LTEXT "Text",140,178,64,130,8
|
||||
LTEXT "Text",141,178,77,130,8
|
||||
EDITTEXT 149,142,10,38,12
|
||||
EDITTEXT 150,142,23,38,12
|
||||
EDITTEXT 151,142,36,38,12
|
||||
EDITTEXT 152,142,49,38,12
|
||||
EDITTEXT 153,142,62,38,12
|
||||
EDITTEXT 154,142,75,38,12
|
||||
LTEXT "1",156,184,12,120,8
|
||||
LTEXT "2",157,184,25,120,8
|
||||
LTEXT "3",158,184,38,120,8
|
||||
LTEXT "4",159,184,51,120,8
|
||||
LTEXT "5",160,184,64,120,8
|
||||
LTEXT "6",161,184,77,120,8
|
||||
PUSHBUTTON "Reset",163,81,56,41,12
|
||||
LTEXT "Text",164,146,26,162,8
|
||||
LTEXT "Text",165,146,54,162,8
|
||||
LTEXT "Text",166,146,81,162,8
|
||||
PUSHBUTTON "Change...",167,245,10,62,14
|
||||
PUSHBUTTON "Change...",168,245,38,62,14
|
||||
PUSHBUTTON "Change...",169,245,65,62,14
|
||||
CONTROL "Sound Effects",170,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,74,10,58,10
|
||||
CONTROL "Physics",171,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,73,24,59,10
|
||||
CONTROL "Scoring",172,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,73,38,40,10
|
||||
LTEXT "Text",173,146,13,97,8
|
||||
LTEXT "Text",174,146,41,97,8
|
||||
LTEXT "Text",175,146,68,97,8
|
||||
LTEXT "Components:",176,2,3,43,8
|
||||
END
|
||||
|
||||
KEYMAPPER DIALOGEX 2, 9, 284, 183
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "3D Pinball: Player Controls"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
PUSHBUTTON "OK",KEYMAPPER_Ok,119,163,50,14
|
||||
PUSHBUTTON "Cancel",KEYMAPPER_Cancel,173,163,50,14
|
||||
PUSHBUTTON "&Default",KEYMAPPER_Default,227,163,50,14
|
||||
LTEXT "&Left Flipper",901,20,87,39,10
|
||||
COMBOBOX KEYMAPPER_FlipperL,66,85,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Right &Flipper",902,146,87,44,10
|
||||
COMBOBOX KEYMAPPER_FlipperR,192,85,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Left &Table Bump",904,20,107,34,17
|
||||
COMBOBOX KEYMAPPER_BumpLeft,66,108,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "&Right Table Bump",905,146,107,38,19
|
||||
COMBOBOX KEYMAPPER_BumpRight,192,108,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "&BottomTable Bump",906,20,131,41,18
|
||||
COMBOBOX KEYMAPPER_BumpBottom,66,133,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "&Plunger",903,146,136,39,8
|
||||
COMBOBOX KEYMAPPER_Plunger,192,133,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "To change game controls, choose the control options you want, click the Arrow buttons to change specific keys, and then choose OK.",801,14,17,249,18
|
||||
LTEXT "To restore 3D Pinball to its original settings, choose Default, and then choose OK. To use the Arrow keys on the numeric keypad, make sure NUMLOCK is off.",802,14,38,256,25
|
||||
GROUPBOX "Control Options",908,7,71,269,87
|
||||
GROUPBOX "Instructions",909,7,5,269,61
|
||||
END
|
||||
|
||||
XXDLG_HIGHSCORES DIALOG 20, 22, 211, 116
|
||||
STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "High Scores"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",1,32,97,33,14,BS_LEFTTEXT
|
||||
PUSHBUTTON "Cancel",2,89,97,33,14
|
||||
PUSHBUTTON "&Clear",112,146,97,33,14
|
||||
LTEXT "Name",-1,51,8,24,8
|
||||
LTEXT "Score",-1,160,8,20,8
|
||||
LTEXT "Rank",-1,15,8,20,8
|
||||
RTEXT "1",-1,15,28,16,12
|
||||
RTEXT "2",-1,15,40,16,12
|
||||
RTEXT "3",-1,15,52,16,12
|
||||
RTEXT "4",-1,15,64,16,12
|
||||
RTEXT "5",-1,15,76,16,12
|
||||
LTEXT "",401,51,28,88,12
|
||||
LTEXT "",402,51,40,88,12
|
||||
LTEXT "",403,51,52,88,12
|
||||
LTEXT "",404,51,64,88,12
|
||||
LTEXT "",405,51,76,88,12
|
||||
RTEXT "",501,140,28,58,12
|
||||
RTEXT "",502,140,40,58,12
|
||||
RTEXT "",503,140,52,58,12
|
||||
RTEXT "",504,140,64,58,12
|
||||
RTEXT "",505,140,76,58,12
|
||||
EDITTEXT 601,49,26,88,12,WS_DISABLED
|
||||
EDITTEXT 602,49,38,88,12,WS_DISABLED
|
||||
EDITTEXT 603,49,50,88,12,WS_DISABLED
|
||||
EDITTEXT 604,49,62,88,12,WS_DISABLED
|
||||
EDITTEXT 605,49,74,88,12,WS_DISABLED
|
||||
CONTROL "",-1,"Static",SS_BLACKFRAME,9,22,195,69
|
||||
END
|
||||
|
||||
XXKEYMAPPER DIALOG 2, 9, 277, 158
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "3D Pinball: Player Controls"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
PUSHBUTTON "OK",1,221,84,50,14
|
||||
PUSHBUTTON "Cancel",2,221,105,50,14
|
||||
PUSHBUTTON "Default",501,221,127,50,14
|
||||
COMBOBOX 401,50,82,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX 402,157,82,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX 404,50,105,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX 405,157,105,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX 406,50,130,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX 403,157,130,53,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
RTEXT "Plunger",811,114,133,39,8
|
||||
RTEXT "Right Flipper",807,107,84,46,10
|
||||
CTEXT "Left Table Bump",912,12,102,34,19
|
||||
CTEXT "Right Table Bump",914,115,102,38,19
|
||||
LTEXT "To change game controls, choose the control options you want, click the Arrow buttons to change specific keys, and then choose OK.",801,12,13,249,18
|
||||
LTEXT "To restore 3D Pinball to its original settings, choose Default, and then choose OK. To use the Arrow keys on the numeric keypad, make sure NUMLOCK is off.",802,12,32,256,25
|
||||
GROUPBOX "Control Options",908,5,67,214,87
|
||||
RTEXT "Left Flipper",803,9,84,39,10
|
||||
GROUPBOX "Instructions",909,5,1,265,61
|
||||
CTEXT "BottomTable Bump",916,7,127,41,19
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
"DIALOG_1", DIALOG
|
||||
BEGIN
|
||||
END
|
||||
|
||||
"DLG_HIGHSCORES", DIALOG
|
||||
BEGIN
|
||||
END
|
||||
|
||||
"KEYMAPPER", DIALOG
|
||||
BEGIN
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AFX_DIALOG_LAYOUT
|
||||
//
|
||||
|
||||
DLG_HIGHSCORES AFX_DIALOG_LAYOUT
|
||||
BEGIN
|
||||
0
|
||||
END
|
||||
|
||||
KEYMAPPER AFX_DIALOG_LAYOUT
|
||||
BEGIN
|
||||
0
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Bitmap
|
||||
//
|
||||
|
||||
splash_bitmap BITMAP "splash_bitmap.bmp"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING101 "Replay Awarded"
|
||||
IDS_STRING102 "Ball Locked"
|
||||
IDS_STRING103 "Center Post\n%ld"
|
||||
IDS_STRING104 "Bonus Awarded\n%ld"
|
||||
IDS_STRING105 "Bonus Activated"
|
||||
IDS_STRING106 "Weapons Upgraded"
|
||||
IDS_STRING107 "Engine Upgraded"
|
||||
IDS_STRING108 "Bonus up 1 Million"
|
||||
IDS_STRING109 "Extra Ball Available\n%ld"
|
||||
IDS_STRING110 "Extra Ball"
|
||||
IDS_STRING111 "Reflex Shot Award\n%ld"
|
||||
IDS_STRING112 "Final Battle Won"
|
||||
IDS_STRING113 "Hyperspace Bonus\n%ld"
|
||||
IDS_STRING114 "Hyperspace Bonus Available"
|
||||
IDS_STRING115 "Jackpot Awarded\n%ld"
|
||||
IDS_STRING116 "Jackpot Activated"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING117 "Multiball"
|
||||
IDS_STRING118 "Ramp Bonus Awarded"
|
||||
IDS_STRING119 "Light Added"
|
||||
IDS_STRING120 "Ramp Bonus On"
|
||||
IDS_STRING121 "Light Reset Off"
|
||||
IDS_STRING122 "Skill Shot\n%ld"
|
||||
IDS_STRING123 "Game Paused\nF3 to Resume"
|
||||
IDS_STRING124 "Continue Play"
|
||||
IDS_STRING125 "F2 Starts New Game"
|
||||
IDS_STRING126 "Careful..."
|
||||
IDS_STRING127 "Player 1"
|
||||
IDS_STRING128 "Player 2"
|
||||
IDS_STRING129 "Player 3"
|
||||
IDS_STRING130 "Player 4"
|
||||
IDS_STRING131 "Demo\nPlayer 1"
|
||||
IDS_STRING132 "Demo\nPlayer 2"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING133 "Demo\nPlayer 3"
|
||||
IDS_STRING134 "Demo\nPlayer 4"
|
||||
IDS_STRING135 "Game Over"
|
||||
IDS_STRING136 "TILT!"
|
||||
IDS_STRING137 "This program requires an 80386 or later CPU."
|
||||
IDS_STRING138 "80386 Required"
|
||||
IDS_STRING139 "3D Pinball for Windows - Space Cadet"
|
||||
IDS_STRING140 "One or more of the player controls is set to the same key,\nfor best performance use unique keys for each control."
|
||||
IDS_STRING141 "Clear High Scores?"
|
||||
IDS_STRING142 "Confirm"
|
||||
IDS_STRING143 "WAVEMIX.INF is missing - it must be in the pinball directory!"
|
||||
IDS_STRING144 "Warning:"
|
||||
IDS_STRING145 "Ship Re-Fueled"
|
||||
IDS_STRING146 "Gravity Well"
|
||||
IDS_STRING147 "Time Warp Forward"
|
||||
IDS_STRING148 "Time Warp Backward"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING149 "Maelstrom!"
|
||||
IDS_STRING150 "Wormhole"
|
||||
IDS_STRING151 "Awaiting Deployment"
|
||||
IDS_STRING152 "Flags Upgraded"
|
||||
IDS_STRING153 "Bonus Hold"
|
||||
IDS_STRING154 "Level One Commendation"
|
||||
IDS_STRING155 "Level Two Commendation"
|
||||
IDS_STRING156 "Level Three Commendation"
|
||||
IDS_STRING157 "Field Multiplier 2x"
|
||||
IDS_STRING158 "Field Multiplier 3x"
|
||||
IDS_STRING159 "Field Multiplier 5x"
|
||||
IDS_STRING160 "Field Multiplier 10x"
|
||||
IDS_STRING161 "Target Practice"
|
||||
IDS_STRING162 "Launch Training"
|
||||
IDS_STRING163 "Re-Entry Training"
|
||||
IDS_STRING164 "Science"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING165 "Stray Comet"
|
||||
IDS_STRING166 "Black Hole"
|
||||
IDS_STRING167 "Space Radiation"
|
||||
IDS_STRING168 "Bug Hunt"
|
||||
IDS_STRING169 "Alien Menace"
|
||||
IDS_STRING170 "Rescue"
|
||||
IDS_STRING171 "Satellite Retrieval"
|
||||
IDS_STRING172 "Recon"
|
||||
IDS_STRING173 "Doomsday Machine"
|
||||
IDS_STRING174 "Cosmic Plague"
|
||||
IDS_STRING175 "Secret"
|
||||
IDS_STRING176 "Time Warp"
|
||||
IDS_STRING177 "Maelstrom"
|
||||
IDS_STRING178 "Mission Accepted\n%ld"
|
||||
IDS_STRING179 "Mission Completed\n%ld"
|
||||
IDS_STRING180 "%s Mission Selected"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING181 "Black Hole\n%ld"
|
||||
IDS_STRING182 "Gravity Normalized\n%ld"
|
||||
IDS_STRING183 "Gravity Well\n%ld"
|
||||
IDS_STRING184 "Promotion to %s"
|
||||
IDS_STRING185 "Cadet"
|
||||
IDS_STRING186 "Ensign"
|
||||
IDS_STRING187 "Lieutenant"
|
||||
IDS_STRING188 "Captain"
|
||||
IDS_STRING189 "Lt Commander"
|
||||
IDS_STRING190 "Commander"
|
||||
IDS_STRING191 "Commodore"
|
||||
IDS_STRING192 "Admiral"
|
||||
IDS_STRING193 "Fleet Admiral"
|
||||
IDS_STRING194 "Wormhole Opened"
|
||||
IDS_STRING195 "Crash Bonus\n%ld"
|
||||
IDS_STRING196 "Replay Ball"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING197 "Re-Deploy"
|
||||
IDS_STRING198 "Player 1 Shoot Again"
|
||||
IDS_STRING199 "Player 2 Shoot Again"
|
||||
IDS_STRING200 "Player 3 Shoot Again"
|
||||
IDS_STRING201 "Player 4 Shoot Again"
|
||||
IDS_STRING202 "This 3D Pinball Table was created for Microsoft by Maxis.\nFor more information call (800)-336-2947\n(US and Canadian customers only).\nCopyright (c) 1995 Maxis."
|
||||
IDS_STRING203 "3D Pinball Table created for Microsoft by Maxis. Copyright (c) 1995 Maxis."
|
||||
IDS_STRING204 "About 3D Pinball"
|
||||
IDS_STRING205 "Hit Mission Targets To Select Mission"
|
||||
IDS_STRING206 "Re-Fuel Ship"
|
||||
IDS_STRING207 "Launch Ramp To Accept %s Mission"
|
||||
IDS_STRING208 "Attack Bumpers Hits\nLeft: %d"
|
||||
IDS_STRING209 "Target Training Passed"
|
||||
IDS_STRING210 "Mission Aborted"
|
||||
IDS_STRING211 "Launches Left: %d"
|
||||
IDS_STRING212 "Launch Training Passed"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING213 "Re-Entries Left: %d"
|
||||
IDS_STRING214 "Re-Entry Training Passed"
|
||||
IDS_STRING215 "Drop Targets\nLeft: %d"
|
||||
IDS_STRING216 "Science Mission Completed"
|
||||
IDS_STRING217 "Warning -- Low Fuel"
|
||||
IDS_STRING218 "Fill Right Hazard Banks"
|
||||
IDS_STRING219 "Hyperspace Launch"
|
||||
IDS_STRING220 "Comet Destroyed"
|
||||
IDS_STRING221 "Enter Wormhole"
|
||||
IDS_STRING222 "Radiation Eliminated"
|
||||
IDS_STRING223 "Upgrade Launch Bumpers"
|
||||
IDS_STRING224 "Enter Black Hole"
|
||||
IDS_STRING225 "Black Hole Eliminated"
|
||||
IDS_STRING226 "Targets\nLeft: %d"
|
||||
IDS_STRING227 "Xenomorphs Destroyed"
|
||||
IDS_STRING228 "Upgrade Flags"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING229 "Hyperspace Launch"
|
||||
IDS_STRING230 "Survivors Rescued"
|
||||
IDS_STRING231 "Aliens Repelled"
|
||||
IDS_STRING232 "Hit Fuel Targets"
|
||||
IDS_STRING233 "Remote Attack Bumper Hits\nLeft: %d"
|
||||
IDS_STRING234 "Satellite Repaired"
|
||||
IDS_STRING235 "Lane Passes\nLeft: %d"
|
||||
IDS_STRING236 "Shoot Ball Up Fuel Chute"
|
||||
IDS_STRING237 "Survey Complete"
|
||||
IDS_STRING238 "Out Lane Passes\nLeft: %d"
|
||||
IDS_STRING239 "Doomsday Machine Destroyed"
|
||||
IDS_STRING240 "Roll Flags: %d"
|
||||
IDS_STRING241 "Hit Space Warp Rollover"
|
||||
IDS_STRING242 "Plague Eliminated"
|
||||
IDS_STRING243 "Hit Yellow Wormhole"
|
||||
IDS_STRING244 "Hit Red Wormhole"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING245 "Hit Green Wormhole"
|
||||
IDS_STRING246 "Plans Recovered"
|
||||
IDS_STRING247 "Rebound Hits\nLeft: %d"
|
||||
IDS_STRING248 "Hit Hyperspace Chute or Launch Ramp"
|
||||
IDS_STRING249 "Drop Target Hits\nLeft: %d"
|
||||
IDS_STRING250 "Spot Target Hits\nLeft: %d"
|
||||
IDS_STRING251 "Lanes Passes\nLeft: %d"
|
||||
IDS_STRING252 "Shoot Ball Up Fuel Chute"
|
||||
IDS_STRING253 "Hit Launch Ramp"
|
||||
IDS_STRING254 "Hit Flags"
|
||||
IDS_STRING255 "Hit Worm Hole"
|
||||
IDS_STRING256 "Hyperspace Chute to end Maelstrom"
|
||||
IDS_STRING257 "pinball.mid"
|
||||
IDS_STRING258 "1 UseBitmapFont"
|
||||
IDS_STRING259 "90 Left Flipper Key"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING260 "191 Right Flipper Key"
|
||||
IDS_STRING261 "32 Plunger Key"
|
||||
IDS_STRING262 "88 Bump Left Key"
|
||||
IDS_STRING263 "190 Bump Right Key"
|
||||
IDS_STRING264 "38 Bump Bottom Key"
|
||||
IDS_STRING265 "Software\\Microsoft\\Plus!\\Pinball"
|
||||
IDS_STRING266 "SpaceCadet"
|
||||
IDS_STRING267 "1c7c22a0-9576-11ce-bf80-444553540000"
|
||||
IDS_STRING268 "PINBALL.DAT"
|
||||
IDS_STRING269 "Space Cadet"
|
||||
IDS_STRING270 "Error:"
|
||||
IDS_STRING271 "Unable to find other tables."
|
||||
IDS_STRING272 "3D Pinball\nSpace Cadet"
|
||||
IDS_STRING273 "Promotion to %s"
|
||||
IDS_STRING274 "Demotion to %s"
|
||||
IDS_STRING275 "Upgrade Attack Bumpers"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING276 "Fill Left Hazard Banks"
|
||||
IDS_STRING277 "HIGH SCORE"
|
||||
IDS_STRING278 "pinball.chm"
|
||||
IDS_STRING279 "Not enough memory to run 3D Pinball."
|
||||
IDS_STRING280 "Player 1's Score\n%ld"
|
||||
IDS_STRING281 "Player 2's Score\n%ld"
|
||||
IDS_STRING282 "Player 3's Score\n%ld"
|
||||
IDS_STRING283 "Player 4's Score\n%ld"
|
||||
IDS_STRING284 "High Score 1\n%ld"
|
||||
IDS_STRING285 "High Score 2\n%ld"
|
||||
IDS_STRING286 "High Score 3\n%ld"
|
||||
IDS_STRING287 "High Score 4\n%ld"
|
||||
IDS_STRING288 "High Score 5\n%ld"
|
||||
IDS_STRING289 "255 255 255 (R G B default font color)"
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
ICON_1 ICON "Icon_1.ico"
|
||||
@@ -1,314 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{F7B78CC7-6984-4F79-9486-ABCF87DF9F06}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>SpaceCadetPinball</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Comctl32.lib;Winmm.lib;Htmlhelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Comctl32.lib;Winmm.lib;Htmlhelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Comctl32.lib;Winmm.lib;Htmlhelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Comctl32.lib;Winmm.lib;Htmlhelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="control.h" />
|
||||
<ClInclude Include="fullscrn.h" />
|
||||
<ClInclude Include="gdrv.h" />
|
||||
<ClInclude Include="high_score.h" />
|
||||
<ClInclude Include="loader.h" />
|
||||
<ClInclude Include="maths.h" />
|
||||
<ClInclude Include="memory.h" />
|
||||
<ClInclude Include="midi.h" />
|
||||
<ClInclude Include="nudge.h" />
|
||||
<ClInclude Include="objlist_class.h" />
|
||||
<ClInclude Include="options.h" />
|
||||
<ClInclude Include="partman.h" />
|
||||
<ClInclude Include="pb.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="pinball.h" />
|
||||
<ClInclude Include="proj.h" />
|
||||
<ClInclude Include="render.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="score.h" />
|
||||
<ClInclude Include="Sound.h" />
|
||||
<ClInclude Include="splash.h" />
|
||||
<ClInclude Include="TBall.h" />
|
||||
<ClInclude Include="TBlocker.h" />
|
||||
<ClInclude Include="TBumper.h" />
|
||||
<ClInclude Include="TCircle.h" />
|
||||
<ClInclude Include="TCollisionComponent.h" />
|
||||
<ClInclude Include="TComponentGroup.h" />
|
||||
<ClInclude Include="TDemo.h" />
|
||||
<ClInclude Include="TDrain.h" />
|
||||
<ClInclude Include="TEdgeBox.h" />
|
||||
<ClInclude Include="TEdgeManager.h" />
|
||||
<ClInclude Include="TEdgeSegment.h" />
|
||||
<ClInclude Include="TFlagSpinner.h" />
|
||||
<ClInclude Include="TFlipper.h" />
|
||||
<ClInclude Include="TFlipperEdge.h" />
|
||||
<ClInclude Include="TGate.h" />
|
||||
<ClInclude Include="THole.h" />
|
||||
<ClInclude Include="timer.h" />
|
||||
<ClInclude Include="TKickback.h" />
|
||||
<ClInclude Include="TKickout.h" />
|
||||
<ClInclude Include="TLight.h" />
|
||||
<ClInclude Include="TLightBargraph.h" />
|
||||
<ClInclude Include="TLightGroup.h" />
|
||||
<ClInclude Include="TLightRollover.h" />
|
||||
<ClInclude Include="TLine.h" />
|
||||
<ClInclude Include="TOneway.h" />
|
||||
<ClInclude Include="TPinballComponent.h" />
|
||||
<ClInclude Include="TPinballTable.h" />
|
||||
<ClInclude Include="TPlunger.h" />
|
||||
<ClInclude Include="TPopupTarget.h" />
|
||||
<ClInclude Include="TRamp.h" />
|
||||
<ClInclude Include="TRollover.h" />
|
||||
<ClInclude Include="TSink.h" />
|
||||
<ClInclude Include="TSoloTarget.h" />
|
||||
<ClInclude Include="TSound.h" />
|
||||
<ClInclude Include="TTableLayer.h" />
|
||||
<ClInclude Include="TTextBox.h" />
|
||||
<ClInclude Include="TTextBoxMessage.h" />
|
||||
<ClInclude Include="TTimer.h" />
|
||||
<ClInclude Include="TTripwire.h" />
|
||||
<ClInclude Include="TWall.h" />
|
||||
<ClInclude Include="TZmapList.h" />
|
||||
<ClInclude Include="WaveMix.h" />
|
||||
<ClInclude Include="winmain.h" />
|
||||
<ClInclude Include="zdrv.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="control.cpp" />
|
||||
<ClCompile Include="fullscrn.cpp" />
|
||||
<ClCompile Include="gdrv.cpp" />
|
||||
<ClCompile Include="high_score.cpp" />
|
||||
<ClCompile Include="loader.cpp" />
|
||||
<ClCompile Include="maths.cpp" />
|
||||
<ClCompile Include="memory.cpp" />
|
||||
<ClCompile Include="midi.cpp" />
|
||||
<ClCompile Include="nudge.cpp" />
|
||||
<ClCompile Include="objlist_class.cpp" />
|
||||
<ClCompile Include="options.cpp" />
|
||||
<ClCompile Include="partman.cpp" />
|
||||
<ClCompile Include="pb.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pinball.cpp" />
|
||||
<ClCompile Include="proj.cpp" />
|
||||
<ClCompile Include="render.cpp" />
|
||||
<ClCompile Include="score.cpp" />
|
||||
<ClCompile Include="Sound.cpp" />
|
||||
<ClCompile Include="SpaceCadetPinball.cpp" />
|
||||
<ClCompile Include="splash.cpp" />
|
||||
<ClCompile Include="TBall.cpp" />
|
||||
<ClCompile Include="TBlocker.cpp" />
|
||||
<ClCompile Include="TBumper.cpp" />
|
||||
<ClCompile Include="TCircle.cpp" />
|
||||
<ClCompile Include="TCollisionComponent.cpp" />
|
||||
<ClCompile Include="TComponentGroup.cpp" />
|
||||
<ClCompile Include="TDemo.cpp" />
|
||||
<ClCompile Include="TDrain.cpp" />
|
||||
<ClCompile Include="TEdgeBox.cpp" />
|
||||
<ClCompile Include="TEdgeManager.cpp" />
|
||||
<ClCompile Include="TEdgeSegment.cpp" />
|
||||
<ClCompile Include="TFlagSpinner.cpp" />
|
||||
<ClCompile Include="TFlipper.cpp" />
|
||||
<ClCompile Include="TFlipperEdge.cpp" />
|
||||
<ClCompile Include="TGate.cpp" />
|
||||
<ClCompile Include="THole.cpp" />
|
||||
<ClCompile Include="timer.cpp" />
|
||||
<ClCompile Include="TKickback.cpp" />
|
||||
<ClCompile Include="TKickout.cpp" />
|
||||
<ClCompile Include="TLight.cpp" />
|
||||
<ClCompile Include="TLightBargraph.cpp" />
|
||||
<ClCompile Include="TLightGroup.cpp" />
|
||||
<ClCompile Include="TLightRollover.cpp" />
|
||||
<ClCompile Include="TLine.cpp" />
|
||||
<ClCompile Include="TOneway.cpp" />
|
||||
<ClCompile Include="TPinballComponent.cpp" />
|
||||
<ClCompile Include="TPinballTable.cpp" />
|
||||
<ClCompile Include="TPlunger.cpp" />
|
||||
<ClCompile Include="TPopupTarget.cpp" />
|
||||
<ClCompile Include="TRamp.cpp" />
|
||||
<ClCompile Include="TRollover.cpp" />
|
||||
<ClCompile Include="TSink.cpp" />
|
||||
<ClCompile Include="TSoloTarget.cpp" />
|
||||
<ClCompile Include="TSound.cpp" />
|
||||
<ClCompile Include="TTableLayer.cpp" />
|
||||
<ClCompile Include="TTextBox.cpp" />
|
||||
<ClCompile Include="TTextBoxMessage.cpp" />
|
||||
<ClCompile Include="TTimer.cpp" />
|
||||
<ClCompile Include="TTripwire.cpp" />
|
||||
<ClCompile Include="TWall.cpp" />
|
||||
<ClCompile Include="WaveMix.cpp" />
|
||||
<ClCompile Include="winmain.cpp" />
|
||||
<ClCompile Include="zdrv.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="NatvisFile.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="SpaceCadetPinball.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PB_MSGFT.bin" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="Icon_1.ico" />
|
||||
<Image Include="splash_bitmap.bmp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,447 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\TEdgeSegment">
|
||||
<UniqueIdentifier>{0aa40751-a44a-400e-8809-ee817161e8e0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\TEdgeSegment">
|
||||
<UniqueIdentifier>{d70e7fca-2294-41a4-9cf8-78052bdb9aa4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\TCollisionComponent">
|
||||
<UniqueIdentifier>{01aed326-d2ec-457a-b99f-08ef32ed97fa}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\TCollisionComponent">
|
||||
<UniqueIdentifier>{7ed2796a-da4b-4edd-8783-53e45d8d1c88}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\TPinballComponent">
|
||||
<UniqueIdentifier>{9ee086c2-1a95-48fb-92d8-4b7e7f6682ff}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\TPinballComponent">
|
||||
<UniqueIdentifier>{33813da8-81ac-449c-b19a-9756272519b9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="objlist_class.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="partman.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="loader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pinball.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="score.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TPinballComponent.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TBall.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TComponentGroup.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TLight.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TLightBargraph.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TLightGroup.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TSound.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TTextBox.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TTimer.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TZmapList.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="memory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="winmain.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="options.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sound.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="render.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fullscrn.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gdrv.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="maths.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="zdrv.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TEdgeSegment.h">
|
||||
<Filter>Header Files\TEdgeSegment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TCollisionComponent.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TCircle.h">
|
||||
<Filter>Header Files\TEdgeSegment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TLine.h">
|
||||
<Filter>Header Files\TEdgeSegment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TWall.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TBumper.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TBlocker.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TDemo.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TDrain.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TFlagSpinner.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TFlipper.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TGate.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="THole.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TKickback.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TKickout.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TOneway.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TPlunger.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TPopupTarget.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TRamp.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TRollover.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TSink.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TSoloTarget.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TTableLayer.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TPinballTable.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TTripwire.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TLightRollover.h">
|
||||
<Filter>Header Files\TCollisionComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="proj.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="midi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="nudge.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TTextBoxMessage.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="high_score.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="control.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TEdgeManager.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TEdgeBox.h">
|
||||
<Filter>Header Files\TPinballComponent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TFlipperEdge.h">
|
||||
<Filter>Header Files\TEdgeSegment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WaveMix.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="splash.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SpaceCadetPinball.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="objlist_class.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="partman.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="loader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pinball.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="score.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TPinballComponent.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TPinballTable.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TBall.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TComponentGroup.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TLight.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TLightBargraph.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TLightGroup.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TSound.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TTextBox.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TTimer.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="memory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="winmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="options.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sound.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pb.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="render.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="fullscrn.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gdrv.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="maths.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="zdrv.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TEdgeSegment.cpp">
|
||||
<Filter>Source Files\TEdgeSegment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TCollisionComponent.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TCircle.cpp">
|
||||
<Filter>Source Files\TEdgeSegment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TLine.cpp">
|
||||
<Filter>Source Files\TEdgeSegment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TWall.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TBlocker.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TBumper.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TDemo.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TDrain.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TFlagSpinner.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TFlipper.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TGate.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="THole.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TKickback.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TKickout.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TOneway.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TPlunger.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TPopupTarget.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TRamp.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TRollover.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TSink.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TSoloTarget.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TTableLayer.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TTripwire.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TLightRollover.cpp">
|
||||
<Filter>Source Files\TCollisionComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="proj.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="midi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="nudge.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TTextBoxMessage.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="high_score.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="control.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TEdgeManager.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TEdgeBox.cpp">
|
||||
<Filter>Source Files\TPinballComponent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TFlipperEdge.cpp">
|
||||
<Filter>Source Files\TEdgeSegment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WaveMix.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="splash.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="NatvisFile.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="SpaceCadetPinball.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PB_MSGFT.bin">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="Icon_1.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="splash_bitmap.bmp">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,139 +2,196 @@
|
||||
#include "TBall.h"
|
||||
|
||||
|
||||
#include "fullscrn.h"
|
||||
#include "loader.h"
|
||||
#include "maths.h"
|
||||
#include "objlist_class.h"
|
||||
#include "pb.h"
|
||||
#include "proj.h"
|
||||
#include "render.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
#include "TTableLayer.h"
|
||||
|
||||
TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
|
||||
TBall::TBall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false),
|
||||
TEdgeSegment(this, &ActiveFlag, 0)
|
||||
{
|
||||
visualStruct visual{};
|
||||
char ballGroupName[10]{"ball"};
|
||||
|
||||
TimeNow = 0.0;
|
||||
RayMaxDistance = 0.0;
|
||||
ActiveFlag = 1;
|
||||
CollisionComp = nullptr;
|
||||
EdgeCollisionCount = 0;
|
||||
TimeDelta = 0.0;
|
||||
FieldFlag = 1;
|
||||
CollisionFlag = 0;
|
||||
Speed = 0.0;
|
||||
Acceleration.Y = 0.0;
|
||||
Acceleration.X = 0.0;
|
||||
InvAcceleration.Y = 1000000000.0;
|
||||
InvAcceleration.X = 1000000000.0;
|
||||
Position.X = 0.0;
|
||||
Position.Y = 0.0;
|
||||
Direction.Y = 0.0;
|
||||
Direction.X = 0.0;
|
||||
ListBitmap = new std::vector<SpriteData>();
|
||||
|
||||
ListBitmap = new TZmapList(0, 4);
|
||||
auto groupIndex = loader::query_handle("ball");
|
||||
Offset = *loader::query_float_attribute(groupIndex, 0, 500);
|
||||
auto visualCount = loader::query_visual_states(groupIndex);
|
||||
auto index = 0;
|
||||
if (visualCount > 0)
|
||||
if (groupIndex == -1)
|
||||
{
|
||||
auto visualZPtr = VisualZArray;
|
||||
do
|
||||
{
|
||||
loader::query_visual(groupIndex, index, &visual);
|
||||
if (ListBitmap)
|
||||
ListBitmap->Add(visual.Bitmap);
|
||||
auto visVec = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, index, 501));
|
||||
auto zDepth = proj::z_distance(visVec);
|
||||
++index;
|
||||
*visualZPtr = zDepth;
|
||||
++visualZPtr;
|
||||
}
|
||||
while (index < visualCount);
|
||||
HasGroupFlag = false;
|
||||
Position = {0, 0, 0};
|
||||
CollisionMask = 1;
|
||||
}
|
||||
RenderSprite = render::create_sprite(VisualType::Ball, nullptr, nullptr, 0, 0, nullptr);
|
||||
PinballTable->CollisionCompOffset = Offset;
|
||||
Position.Z = Offset;
|
||||
else
|
||||
{
|
||||
HasGroupFlag = true;
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
CollisionMask = visual.CollisionGroup;
|
||||
auto floatArr = loader::query_float_attribute(groupIndex, 0, 408);
|
||||
Position = {floatArr[0], floatArr[1], floatArr[3]};
|
||||
}
|
||||
|
||||
/*Full tilt: ball is ballN, where N[0,2] resolution*/
|
||||
groupIndex = loader::query_handle(ballGroupName);
|
||||
if (groupIndex < 0)
|
||||
{
|
||||
ballGroupName[4] = '0' + fullscrn::GetResolution();
|
||||
groupIndex = loader::query_handle(ballGroupName);
|
||||
}
|
||||
|
||||
Radius = *loader::query_float_attribute(groupIndex, 0, 500);
|
||||
|
||||
auto visualCount = loader::query_visual_states(groupIndex);
|
||||
for (auto index = 0; index < visualCount; ++index)
|
||||
{
|
||||
loader::query_visual(groupIndex, index, &visual);
|
||||
ListBitmap->push_back(visual.Bitmap);
|
||||
auto visVec = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, index, 501));
|
||||
auto zDepth = proj::z_distance(*visVec);
|
||||
VisualZArray[index] = zDepth;
|
||||
}
|
||||
RenderSprite = new render_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr);
|
||||
PinballTable->CollisionCompOffset = Radius;
|
||||
Position.Z = Radius;
|
||||
GroupIndex = groupIndex;
|
||||
}
|
||||
|
||||
void TBall::Repaint()
|
||||
{
|
||||
int pos2D[2];
|
||||
|
||||
if (CollisionFlag)
|
||||
{
|
||||
Position.Z =
|
||||
CollisionOffset.X * Position.X +
|
||||
CollisionOffset.Y * Position.Y +
|
||||
Offset + CollisionOffset.Z;
|
||||
Radius + CollisionOffset.Z;
|
||||
}
|
||||
|
||||
proj::xform_to_2d(&Position, pos2D);
|
||||
auto zDepth = proj::z_distance(&Position);
|
||||
auto pos2D = proj::xform_to_2d(Position);
|
||||
auto zDepth = proj::z_distance(Position);
|
||||
|
||||
auto zArrPtr = VisualZArray;
|
||||
int index;
|
||||
for (index = 0; index < ListBitmap->Count() - 1; ++index, zArrPtr++)
|
||||
auto index = 0u;
|
||||
for (; index < ListBitmap->size() - 1; ++index)
|
||||
{
|
||||
if (*zArrPtr <= zDepth) break;
|
||||
if (VisualZArray[index] <= zDepth) break;
|
||||
}
|
||||
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(index));
|
||||
render::ball_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zDepth,
|
||||
pos2D[0] - bmp->Width / 2,
|
||||
pos2D[1] - bmp->Height / 2);
|
||||
SpriteSetBall(index, pos2D, zDepth);
|
||||
}
|
||||
|
||||
void TBall::not_again(TEdgeSegment* edge)
|
||||
{
|
||||
if (EdgeCollisionCount < 5)
|
||||
if (EdgeCollisionCount < 16)
|
||||
{
|
||||
Collisions[EdgeCollisionCount] = edge;
|
||||
++EdgeCollisionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
Collisions[i] = Collisions[i + 8];
|
||||
Collisions[8] = edge;
|
||||
EdgeCollisionCount = 9;
|
||||
}
|
||||
EdgeCollisionResetFlag = true;
|
||||
}
|
||||
|
||||
bool TBall::already_hit(TEdgeSegment* edge)
|
||||
bool TBall::already_hit(const TEdgeSegment& edge) const
|
||||
{
|
||||
for (int i = 0; i < EdgeCollisionCount; i++)
|
||||
{
|
||||
if (Collisions[i] == edge)
|
||||
if (Collisions[i] == &edge)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int TBall::Message(int code, float value)
|
||||
int TBall::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024)
|
||||
if (code == MessageCode::Reset)
|
||||
{
|
||||
render::ball_set(RenderSprite, nullptr, 0.0, 0, 0);
|
||||
SpriteSetBall(-1, {0, 0}, 0.0f);
|
||||
Position.X = 0.0;
|
||||
CollisionComp = nullptr;
|
||||
Position.Y = 0.0;
|
||||
ActiveFlag = 0;
|
||||
CollisionFlag = 0;
|
||||
FieldFlag = 1;
|
||||
Acceleration.Y = 0.0;
|
||||
Position.Z = Offset;
|
||||
Acceleration.X = 0.0;
|
||||
CollisionMask = 1;
|
||||
Direction.Y = 0.0;
|
||||
Position.Z = Radius;
|
||||
Direction.X = 0.0;
|
||||
Speed = 0.0;
|
||||
RayMaxDistance = 0.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TBall::throw_ball(TBall* ball, vector_type* acceleration, float angleMult, float speedMult1, float speedMult2)
|
||||
void TBall::throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2)
|
||||
{
|
||||
ball->CollisionComp = nullptr;
|
||||
ball->Acceleration = *acceleration;
|
||||
float rnd = static_cast<float>(rand());
|
||||
float angle = (1.0f - (rnd * 0.00003051850947599719f + rnd * 0.00003051850947599719f)) * angleMult;
|
||||
maths::RotateVector(&ball->Acceleration, angle);
|
||||
rnd = static_cast<float>(rand());
|
||||
ball->Speed = (1.0f - (rnd * 0.00003051850947599719f + rnd * 0.00003051850947599719f)) * (speedMult1 *
|
||||
speedMult2) + speedMult1;
|
||||
CollisionComp = nullptr;
|
||||
Direction = *direction;
|
||||
float rnd = RandFloat();
|
||||
float angle = (1.0f - (rnd + rnd)) * angleMult;
|
||||
maths::RotateVector(Direction, angle);
|
||||
rnd = RandFloat();
|
||||
Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
|
||||
}
|
||||
|
||||
void TBall::EdgeCollision(TBall* ball, float distance)
|
||||
{
|
||||
ball->CollisionDisabledFlag = true;
|
||||
ball->Position.X += ball->Direction.X * distance;
|
||||
ball->Position.Y += ball->Direction.Y * distance;
|
||||
ball->Direction.X *= ball->Speed;
|
||||
ball->Direction.Y *= ball->Speed;
|
||||
Direction.X *= Speed;
|
||||
Direction.Y *= Speed;
|
||||
|
||||
// AB - vector from ball to this, BA - from this to ball; collision direction
|
||||
vector2 AB{ball->Position.X - Position.X, ball->Position.Y - Position.Y};
|
||||
maths::normalize_2d(AB);
|
||||
vector2 BA{-AB.X, -AB.Y};
|
||||
|
||||
// Projection = difference between ball directions and collision direction
|
||||
auto projAB = -maths::DotProduct(ball->Direction, AB);
|
||||
auto projBA = -maths::DotProduct(Direction, BA);
|
||||
vector2 delta{AB.X * projAB - BA.X * projBA, AB.Y * projAB - BA.Y * projBA};
|
||||
|
||||
ball->Direction.X += delta.X;
|
||||
ball->Direction.Y += delta.Y;
|
||||
ball->Speed = maths::normalize_2d(ball->Direction);
|
||||
Direction.X -= delta.X;
|
||||
Direction.Y -= delta.Y;
|
||||
Speed = maths::normalize_2d(Direction);
|
||||
}
|
||||
|
||||
float TBall::FindCollisionDistance(const ray_type& ray)
|
||||
{
|
||||
// Original inherits TCircle and aliases position.
|
||||
const circle_type ballCircle{{Position.X, Position.Y}, Radius * Radius * 4.0f};
|
||||
return maths::ray_intersect_circle(ray, ballCircle);
|
||||
}
|
||||
|
||||
vector2 TBall::get_coordinates()
|
||||
{
|
||||
return TTableLayer::edge_manager->NormalizeBox(Position);
|
||||
}
|
||||
|
||||
void TBall::Disable()
|
||||
{
|
||||
ActiveFlag = false;
|
||||
CollisionDisabledFlag = true;
|
||||
SpriteSet(-1);
|
||||
}
|
||||
@@ -1,37 +1,41 @@
|
||||
#pragma once
|
||||
#include "maths.h"
|
||||
#include "TPinballComponent.h"
|
||||
#include "TCollisionComponent.h"
|
||||
#include "TEdgeSegment.h"
|
||||
|
||||
class TCollisionComponent;
|
||||
class TEdgeSegment;
|
||||
|
||||
class TBall : public TPinballComponent
|
||||
class TBall : public TCollisionComponent, public TEdgeSegment
|
||||
{
|
||||
public :
|
||||
TBall(TPinballTable* table);
|
||||
TBall(TPinballTable* table, int groupIndex);
|
||||
void Repaint();
|
||||
void not_again(TEdgeSegment* edge);
|
||||
bool already_hit(TEdgeSegment* edge);
|
||||
int Message(int code, float value) override;
|
||||
bool already_hit(const TEdgeSegment& edge) const;
|
||||
int Message(MessageCode code, float value) override;
|
||||
vector2 get_coordinates() override;
|
||||
void Disable();
|
||||
void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2);
|
||||
void place_in_grid(RectF* aabb) override {}
|
||||
void EdgeCollision(TBall* ball, float distance) override;
|
||||
float FindCollisionDistance(const ray_type& ray) override;
|
||||
|
||||
static void throw_ball(TBall* ball, struct vector_type* acceleration, float angleMult, float speedMult1,
|
||||
float speedMult2);
|
||||
|
||||
vector_type Position;
|
||||
vector_type Acceleration;
|
||||
vector3 Position{};
|
||||
vector3 PrevPosition{};
|
||||
vector3 Direction{};
|
||||
float Speed;
|
||||
float RayMaxDistance;
|
||||
float TimeDelta;
|
||||
float TimeNow;
|
||||
vector_type InvAcceleration;
|
||||
vector_type RampFieldForce;
|
||||
vector2 RampFieldForce{};
|
||||
TCollisionComponent* CollisionComp;
|
||||
int FieldFlag;
|
||||
TEdgeSegment* Collisions[5];
|
||||
int CollisionMask;
|
||||
TEdgeSegment* Collisions[16]{};
|
||||
int EdgeCollisionCount;
|
||||
vector_type CollisionOffset;
|
||||
bool EdgeCollisionResetFlag{};
|
||||
vector3 CollisionOffset{};
|
||||
int CollisionFlag;
|
||||
float Offset;
|
||||
int Unknown29;
|
||||
float VisualZArray[50];
|
||||
float Radius;
|
||||
bool HasGroupFlag;
|
||||
int StuckCounter = 0;
|
||||
int LastActiveTime{};
|
||||
float VisualZArray[50]{};
|
||||
bool CollisionDisabledFlag{};
|
||||
};
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "loader.h"
|
||||
#include "render.h"
|
||||
#include "timer.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TBlocker::TBlocker(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
@@ -15,23 +14,23 @@ TBlocker::TBlocker(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
SoundIndex4 = visual.SoundIndex4;
|
||||
SoundIndex3 = visual.SoundIndex3;
|
||||
TurnOnMsgValue = 55;
|
||||
TurnOffMsgValue = 5;
|
||||
InitialDuration = 55;
|
||||
ExtendedDuration = 5;
|
||||
Threshold = 1000000000.0f;
|
||||
Timer = 0;
|
||||
MessageField = 0;
|
||||
ActiveFlag = 0;
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
SpriteSet(-1);
|
||||
}
|
||||
|
||||
int TBlocker::Message(int code, float value)
|
||||
int TBlocker::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 1011:
|
||||
case 1020:
|
||||
case 1024:
|
||||
case 51:
|
||||
case MessageCode::SetTiltLock:
|
||||
case MessageCode::PlayerChanged:
|
||||
case MessageCode::Reset:
|
||||
case MessageCode::TBlockerDisable:
|
||||
if (Timer)
|
||||
{
|
||||
timer::kill(Timer);
|
||||
@@ -39,29 +38,29 @@ int TBlocker::Message(int code, float value)
|
||||
}
|
||||
MessageField = 0;
|
||||
ActiveFlag = 0;
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
if (code == 51)
|
||||
loader::play_sound(SoundIndex3);
|
||||
return 0;
|
||||
case 52:
|
||||
ActiveFlag = 1;
|
||||
loader::play_sound(SoundIndex4);
|
||||
render::sprite_set_bitmap(RenderSprite, static_cast<gdrv_bitmap8*>(ListBitmap->Get(0)));
|
||||
SpriteSet(-1);
|
||||
if (code == MessageCode::TBlockerDisable)
|
||||
loader::play_sound(SoundIndex3, this, "TBlocker1");
|
||||
break;
|
||||
case 59:
|
||||
case MessageCode::TBlockerEnable:
|
||||
ActiveFlag = 1;
|
||||
loader::play_sound(SoundIndex4, this, "TBlocker2");
|
||||
SpriteSet(0);
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
if (value >= 0)
|
||||
Timer = timer::set(value, this, TimerExpired);
|
||||
break;
|
||||
case MessageCode::TBlockerRestartTimeout:
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = timer::set(std::max(value, 0.0f), this, TimerExpired);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
|
||||
float timerTime;
|
||||
if (value <= 0.0)
|
||||
timerTime = 0.0;
|
||||
else
|
||||
timerTime = value;
|
||||
Timer = timer::set(timerTime, this, TimerExpired);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -69,5 +68,5 @@ void TBlocker::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto blocker = static_cast<TBlocker*>(caller);
|
||||
blocker->Timer = 0;
|
||||
control::handler(60, blocker);
|
||||
control::handler(MessageCode::ControlTimerExpired, blocker);
|
||||
}
|
||||
@@ -6,12 +6,12 @@ class TBlocker :
|
||||
{
|
||||
public:
|
||||
TBlocker(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
|
||||
int TurnOnMsgValue;
|
||||
int TurnOffMsgValue;
|
||||
int InitialDuration;
|
||||
int ExtendedDuration;
|
||||
int Timer;
|
||||
int SoundIndex4;
|
||||
int SoundIndex3;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "render.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TBumper::TBumper(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
@@ -22,47 +21,48 @@ TBumper::TBumper(TPinballTable* table, int groupIndex) : TCollisionComponent(tab
|
||||
OriginalThreshold = Threshold;
|
||||
}
|
||||
|
||||
int TBumper::Message(int code, float value)
|
||||
int TBumper::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 11:
|
||||
case MessageCode::TBumperSetBmpIndex:
|
||||
{
|
||||
auto nextBmp = static_cast<int>(floor(value));
|
||||
if (2 * nextBmp > ListBitmap->Count() - 1)
|
||||
nextBmp = (ListBitmap->Count() - 1) / 2;
|
||||
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
||||
if (2 * nextBmp > maxBmp)
|
||||
nextBmp = maxBmp / 2;
|
||||
if (nextBmp < 0)
|
||||
nextBmp = 0;
|
||||
if (nextBmp != BmpIndex)
|
||||
{
|
||||
if (nextBmp >= BmpIndex)
|
||||
loader::play_sound(SoundIndex4);
|
||||
loader::play_sound(SoundIndex4, this, "TBumper1");
|
||||
if (nextBmp < BmpIndex)
|
||||
loader::play_sound(SoundIndex3);
|
||||
loader::play_sound(SoundIndex3, this, "TBumper2");
|
||||
BmpIndex = nextBmp;
|
||||
Fire();
|
||||
control::handler(11, this);
|
||||
control::handler(MessageCode::TBumperSetBmpIndex, this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
case MessageCode::TBumperIncBmpIndex:
|
||||
{
|
||||
auto nextBmp = BmpIndex + 1;
|
||||
auto maxBmp = ListBitmap->Count() - 1;
|
||||
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
||||
if (2 * nextBmp > maxBmp)
|
||||
nextBmp = maxBmp / 2;
|
||||
TBumper::Message(11, static_cast<float>(nextBmp));
|
||||
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(nextBmp));
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
case MessageCode::TBumperDecBmpIndex:
|
||||
{
|
||||
auto nextBmp = BmpIndex - 1;
|
||||
if (nextBmp < 0)
|
||||
nextBmp = 0;
|
||||
TBumper::Message(11, static_cast<float>(nextBmp));
|
||||
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(nextBmp));
|
||||
break;
|
||||
}
|
||||
case 1020:
|
||||
case MessageCode::PlayerChanged:
|
||||
{
|
||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||
playerPtr->BmpIndex = BmpIndex;
|
||||
@@ -71,10 +71,10 @@ int TBumper::Message(int code, float value)
|
||||
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
||||
BmpIndex = playerPtr->BmpIndex;
|
||||
MessageField = playerPtr->MessageField;
|
||||
TBumper::Message(11, static_cast<float>(BmpIndex));
|
||||
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(BmpIndex));
|
||||
break;
|
||||
}
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
{
|
||||
if (Timer)
|
||||
{
|
||||
@@ -83,12 +83,10 @@ int TBumper::Message(int code, float value)
|
||||
}
|
||||
BmpIndex = 0;
|
||||
MessageField = 0;
|
||||
auto playerPtr = PlayerData;
|
||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||
for (auto& playerPtr : PlayerData)
|
||||
{
|
||||
playerPtr->BmpIndex = 0;
|
||||
playerPtr->MessageField = 0;
|
||||
++playerPtr;
|
||||
playerPtr.BmpIndex = 0;
|
||||
playerPtr.MessageField = 0;
|
||||
}
|
||||
TimerExpired(0, this);
|
||||
break;
|
||||
@@ -100,53 +98,26 @@ int TBumper::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TBumper::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TBumper::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (DefaultCollision(ball, nextPosition, direction))
|
||||
{
|
||||
Fire();
|
||||
control::handler(63, this);
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
|
||||
void TBumper::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 4)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
|
||||
int TBumper::get_scoring(int index)
|
||||
{
|
||||
return index < 4 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TBumper::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto bump = static_cast<TBumper*>(caller);
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(bump->ListBitmap->Get(bump->BmpIndex * 2));
|
||||
auto zMap = static_cast<zmap_header_type*>(bump->ListZMap->Get(bump->BmpIndex * 2));
|
||||
bump->SpriteSet(bump->BmpIndex * 2);
|
||||
bump->Timer = 0;
|
||||
render::sprite_set(
|
||||
bump->RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - bump->PinballTable->XOffset,
|
||||
bmp->YPosition - bump->PinballTable->YOffset);
|
||||
bump->Threshold = bump->OriginalThreshold;
|
||||
}
|
||||
|
||||
void TBumper::Fire()
|
||||
{
|
||||
int bmpIndex = 2 * BmpIndex + 1;
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(bmpIndex));
|
||||
auto zMap = static_cast<zmap_header_type*>(ListZMap->Get(bmpIndex));
|
||||
render::sprite_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - PinballTable->XOffset,
|
||||
bmp->YPosition - PinballTable->YOffset);
|
||||
SpriteSet(2 * BmpIndex + 1);
|
||||
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||
Threshold = 1000000000.0;
|
||||
}
|
||||
@@ -13,11 +13,9 @@ class TBumper :
|
||||
public:
|
||||
TBumper(TPinballTable* table, int groupIndex);
|
||||
~TBumper() override = default;
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void Fire();
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
@@ -28,6 +26,5 @@ public:
|
||||
float OriginalThreshold;
|
||||
int SoundIndex4;
|
||||
int SoundIndex3;
|
||||
int Scores[4];
|
||||
TBumper_player_backup PlayerData[4];
|
||||
TBumper_player_backup PlayerData[4]{};
|
||||
};
|
||||
@@ -5,31 +5,40 @@
|
||||
#include "TCollisionComponent.h"
|
||||
#include "TTableLayer.h"
|
||||
|
||||
TCircle::TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup, vector_type* center,
|
||||
TCircle::TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup, vector2* center,
|
||||
float radius): TEdgeSegment(collComp, activeFlag, collisionGroup)
|
||||
{
|
||||
Circle.RadiusSq = radius * radius;
|
||||
Circle.Center = *center;
|
||||
}
|
||||
|
||||
float TCircle::FindCollisionDistance(ray_type* ray)
|
||||
float TCircle::FindCollisionDistance(const ray_type& ray)
|
||||
{
|
||||
return maths::ray_intersect_circle(ray, &Circle);
|
||||
return maths::ray_intersect_circle(ray, Circle);
|
||||
}
|
||||
|
||||
void TCircle::EdgeCollision(TBall* ball, float coef)
|
||||
void TCircle::EdgeCollision(TBall* ball, float distance)
|
||||
{
|
||||
vector_type direction{}, nextPosition{};
|
||||
vector2 direction{}, nextPosition{};
|
||||
|
||||
nextPosition.X = coef * ball->Acceleration.X + ball->Position.X;
|
||||
nextPosition.Y = coef * ball->Acceleration.Y + ball->Position.Y;
|
||||
nextPosition.X = distance * ball->Direction.X + ball->Position.X;
|
||||
nextPosition.Y = distance * ball->Direction.Y + ball->Position.Y;
|
||||
direction.X = nextPosition.X - Circle.Center.X;
|
||||
direction.Y = nextPosition.Y - Circle.Center.Y;
|
||||
maths::normalize_2d(&direction);
|
||||
CollisionComponent->Collision(ball, &nextPosition, &direction, coef, this);
|
||||
maths::normalize_2d(direction);
|
||||
CollisionComponent->Collision(ball, &nextPosition, &direction, distance, this);
|
||||
}
|
||||
|
||||
void TCircle::place_in_grid()
|
||||
void TCircle::place_in_grid(RectF* aabb)
|
||||
{
|
||||
if(aabb)
|
||||
{
|
||||
const auto radius = sqrt(Circle.RadiusSq);
|
||||
aabb->Merge({
|
||||
Circle.Center.X + radius, Circle.Center.Y + radius,
|
||||
Circle.Center.X - radius, Circle.Center.Y - radius
|
||||
});
|
||||
}
|
||||
|
||||
TTableLayer::edges_insert_circle(&Circle, this, nullptr);
|
||||
}
|
||||
@@ -6,11 +6,11 @@ class TCircle :
|
||||
public TEdgeSegment
|
||||
{
|
||||
public:
|
||||
circle_type Circle;
|
||||
circle_type Circle{};
|
||||
|
||||
TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector_type* center,
|
||||
TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector2* center,
|
||||
float radius);
|
||||
float FindCollisionDistance(ray_type* ray) override;
|
||||
void EdgeCollision(TBall* ball, float coef) override;
|
||||
void place_in_grid() override;
|
||||
float FindCollisionDistance(const ray_type& ray) override;
|
||||
void EdgeCollision(TBall* ball, float distance) override;
|
||||
void place_in_grid(RectF* aabb) override;
|
||||
};
|
||||
@@ -2,18 +2,18 @@
|
||||
#include "TCollisionComponent.h"
|
||||
#include "loader.h"
|
||||
#include "maths.h"
|
||||
#include "objlist_class.h"
|
||||
#include "TEdgeSegment.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TBall.h"
|
||||
|
||||
|
||||
TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall) : TPinballComponent(
|
||||
table, groupIndex, true)
|
||||
TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall) :
|
||||
TPinballComponent(table, groupIndex, true)
|
||||
{
|
||||
visualStruct visual{};
|
||||
|
||||
EdgeList = new objlist_class(4, 4);
|
||||
ActiveFlag = 1;
|
||||
AABB = { -10000, -10000, 10000, 10000 };
|
||||
if (GroupName != nullptr)
|
||||
UnusedBaseFlag = 1;
|
||||
if (groupIndex <= 0)
|
||||
@@ -42,57 +42,48 @@ TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, b
|
||||
|
||||
TCollisionComponent::~TCollisionComponent()
|
||||
{
|
||||
for (TEdgeSegment* edge; EdgeList->Count() > 0;)
|
||||
{
|
||||
edge = static_cast<TEdgeSegment*>(EdgeList->Get(0));
|
||||
EdgeList->Delete(edge);
|
||||
for (auto edge : EdgeList)
|
||||
delete edge;
|
||||
}
|
||||
delete EdgeList;
|
||||
}
|
||||
|
||||
|
||||
void TCollisionComponent::port_draw()
|
||||
{
|
||||
for (int index = EdgeList->Count() - 1; index >= 0; index--)
|
||||
{
|
||||
static_cast<TEdgeSegment*>(EdgeList->Get(index))->port_draw();
|
||||
}
|
||||
for (auto edge : EdgeList)
|
||||
edge->port_draw();
|
||||
}
|
||||
|
||||
int TCollisionComponent::DefaultCollision(TBall* ball, vector_type* nextPosition, vector_type* direction)
|
||||
bool TCollisionComponent::DefaultCollision(TBall* ball, vector2* nextPosition, vector2* direction)
|
||||
{
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool collision = false;
|
||||
auto projSpeed = maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, Boost);
|
||||
if (projSpeed <= Threshold)
|
||||
if (projSpeed > Threshold)
|
||||
{
|
||||
if (projSpeed > 0.2)
|
||||
{
|
||||
if (SoftHitSoundId)
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
}
|
||||
return 0;
|
||||
loader::play_sound(HardHitSoundId, ball, "TCollisionComponent1");
|
||||
collision = true;
|
||||
}
|
||||
if (HardHitSoundId)
|
||||
loader::play_sound(HardHitSoundId);
|
||||
return 1;
|
||||
else if (projSpeed > 0.2f)
|
||||
loader::play_sound(SoftHitSoundId, ball, "TCollisionComponent2");
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
void TCollisionComponent::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction,
|
||||
float coef, TEdgeSegment* edge)
|
||||
void TCollisionComponent::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
|
||||
float distance, TEdgeSegment* edge)
|
||||
{
|
||||
int soundIndex;
|
||||
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
||||
return;
|
||||
}
|
||||
double projSpeed = maths::basic_collision(
|
||||
|
||||
auto projSpeed = maths::basic_collision(
|
||||
ball,
|
||||
nextPosition,
|
||||
direction,
|
||||
@@ -100,21 +91,13 @@ void TCollisionComponent::Collision(TBall* ball, vector_type* nextPosition, vect
|
||||
Smoothness,
|
||||
Threshold,
|
||||
Boost);
|
||||
if (projSpeed <= Threshold)
|
||||
{
|
||||
if (projSpeed <= 0.2)
|
||||
return;
|
||||
soundIndex = SoftHitSoundId;
|
||||
}
|
||||
else
|
||||
{
|
||||
soundIndex = HardHitSoundId;
|
||||
}
|
||||
if (soundIndex)
|
||||
loader::play_sound(soundIndex);
|
||||
if (projSpeed > Threshold)
|
||||
loader::play_sound(HardHitSoundId, ball, "TCollisionComponent3");
|
||||
else if (projSpeed > 0.2f)
|
||||
loader::play_sound(SoftHitSoundId, ball, "TCollisionComponent4");
|
||||
}
|
||||
|
||||
int TCollisionComponent::FieldEffect(TBall* ball, vector_type* vecDst)
|
||||
int TCollisionComponent::FieldEffect(TBall* ball, vector2* vecDst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,27 +1,28 @@
|
||||
#pragma once
|
||||
#include "maths.h"
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
class objlist_class;
|
||||
struct vector_type;
|
||||
struct vector2;
|
||||
class TEdgeSegment;
|
||||
class TBall;
|
||||
|
||||
class TCollisionComponent : public TPinballComponent
|
||||
{
|
||||
public:
|
||||
objlist_class* EdgeList;
|
||||
std::vector<TEdgeSegment*> EdgeList;
|
||||
float Elasticity;
|
||||
float Smoothness;
|
||||
float Boost;
|
||||
float Threshold;
|
||||
int SoftHitSoundId;
|
||||
int HardHitSoundId;
|
||||
RectF AABB;
|
||||
|
||||
TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall);
|
||||
~TCollisionComponent() override;
|
||||
void port_draw() override;
|
||||
virtual void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
virtual void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge);
|
||||
virtual int FieldEffect(TBall* ball, vector_type* vecDst);
|
||||
int DefaultCollision(TBall* ball, vector_type* nextPosition, vector_type* direction);
|
||||
virtual int FieldEffect(TBall* ball, vector2* vecDst);
|
||||
bool DefaultCollision(TBall* ball, vector2* nextPosition, vector2* direction);
|
||||
};
|
||||
@@ -4,13 +4,11 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
|
||||
TComponentGroup::TComponentGroup(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, false)
|
||||
{
|
||||
List = new objlist_class(4, 4);
|
||||
Timer = 0;
|
||||
if (groupIndex > 0)
|
||||
{
|
||||
@@ -21,7 +19,7 @@ TComponentGroup::TComponentGroup(TPinballTable* table, int groupIndex) : TPinbal
|
||||
{
|
||||
auto component = table->find_component(*shortArrPtr);
|
||||
if (component)
|
||||
List->Add(component);
|
||||
List.push_back(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,26 +31,26 @@ TComponentGroup::~TComponentGroup()
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
}
|
||||
delete List;
|
||||
}
|
||||
|
||||
int TComponentGroup::Message(int code, float value)
|
||||
int TComponentGroup::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 48)
|
||||
if (code == MessageCode::TComponentGroupResetNotifyTimer)
|
||||
{
|
||||
if (this->Timer)
|
||||
{
|
||||
timer::kill(this->Timer);
|
||||
this->Timer = 0;
|
||||
}
|
||||
if (value > 0.0)
|
||||
if (value > 0.0f)
|
||||
this->Timer = timer::set(value, this, NotifyTimerExpired);
|
||||
}
|
||||
else if (code <= 1007 || code > 1011 && code != 1020 && code != 1022)
|
||||
else if (code < MessageCode::Pause || (code > MessageCode::SetTiltLock &&
|
||||
code != MessageCode::PlayerChanged && code != MessageCode::GameOver))
|
||||
{
|
||||
for (int i = 0; i < List->Count(); i++)
|
||||
for (auto component : List)
|
||||
{
|
||||
static_cast<TPinballComponent*>(List->Get(i))->Message(code, value);
|
||||
component->Message(code, value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -62,5 +60,5 @@ void TComponentGroup::NotifyTimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto compGroup = static_cast<TComponentGroup*>(caller);
|
||||
compGroup->Timer = 0;
|
||||
control::handler(61, compGroup);
|
||||
control::handler(MessageCode::ControlNotifyTimerExpired, compGroup);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
class objlist_class;
|
||||
|
||||
class TComponentGroup :
|
||||
public TPinballComponent
|
||||
@@ -9,9 +8,9 @@ class TComponentGroup :
|
||||
public:
|
||||
TComponentGroup(TPinballTable* table, int groupIndex);
|
||||
~TComponentGroup() override;
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
static void NotifyTimerExpired(int timerId, void* caller);
|
||||
|
||||
objlist_class* List;
|
||||
std::vector<TPinballComponent*> List;
|
||||
int Timer;
|
||||
};
|
||||
@@ -56,23 +56,23 @@ TDemo::TDemo(TPinballTable* table, int groupIndex)
|
||||
Edge3 = TEdgeSegment::install_wall(v9, this, &ActiveFlag, visual.CollisionGroup, table->CollisionCompOffset, 1404);
|
||||
}
|
||||
|
||||
int TDemo::Message(int code, float value)
|
||||
int TDemo::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 1014:
|
||||
case MessageCode::NewGame:
|
||||
if (RestartGameTimer)
|
||||
timer::kill(RestartGameTimer);
|
||||
RestartGameTimer = 0;
|
||||
break;
|
||||
case 1022:
|
||||
case MessageCode::GameOver:
|
||||
if (RestartGameTimer)
|
||||
timer::kill(RestartGameTimer);
|
||||
RestartGameTimer = 0;
|
||||
if (ActiveFlag != 0)
|
||||
RestartGameTimer = timer::set(5.0, this, NewGameRestartTimer);
|
||||
break;
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
if (FlipLeftTimer)
|
||||
timer::kill(FlipLeftTimer);
|
||||
FlipLeftTimer = 0;
|
||||
@@ -93,20 +93,19 @@ int TDemo::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TDemo::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TDemo::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
ball->not_again(edge);
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= coef;
|
||||
ball->RayMaxDistance -= distance;
|
||||
|
||||
switch (reinterpret_cast<int>(edge->WallValue))
|
||||
switch (reinterpret_cast<size_t>(edge->WallValue))
|
||||
{
|
||||
case 1400:
|
||||
if (!FlipLeftTimer && !FlipLeftFlag)
|
||||
{
|
||||
float time = FlipTimerTime1 + FlipTimerTime2 - static_cast<float>(rand()) *
|
||||
0.00003051850947599719f * (FlipTimerTime2 + FlipTimerTime2);
|
||||
float time = FlipTimerTime1 + FlipTimerTime2 - RandFloat() * (FlipTimerTime2 + FlipTimerTime2);
|
||||
FlipLeftTimer = timer::set(time, this, FlipLeft);
|
||||
}
|
||||
break;
|
||||
@@ -116,8 +115,7 @@ void TDemo::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||
case 1402:
|
||||
if (!FlipRightTimer && !FlipRightFlag)
|
||||
{
|
||||
float time = FlipTimerTime1 + FlipTimerTime2 - static_cast<float>(rand()) *
|
||||
0.00003051850947599719f * (FlipTimerTime2 + FlipTimerTime2);
|
||||
float time = FlipTimerTime1 + FlipTimerTime2 - RandFloat() * (FlipTimerTime2 + FlipTimerTime2);
|
||||
FlipRightTimer = timer::set(time, this, FlipRight);
|
||||
}
|
||||
break;
|
||||
@@ -127,8 +125,8 @@ void TDemo::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||
case 1404:
|
||||
if (!PlungerFlag)
|
||||
{
|
||||
PinballTable->Message(1004, ball->TimeNow);
|
||||
float time = static_cast<float>(rand()) * 0.00003051850947599719f + 2.0f;
|
||||
PinballTable->Message(MessageCode::PlungerInputPressed, 0);
|
||||
float time = RandFloat() + 2.0f;
|
||||
PlungerFlag = timer::set(time, this, PlungerRelease);
|
||||
}
|
||||
break;
|
||||
@@ -141,14 +139,14 @@ void TDemo::PlungerRelease(int timerId, void* caller)
|
||||
{
|
||||
auto demo = static_cast<TDemo*>(caller);
|
||||
demo->PlungerFlag = 0;
|
||||
demo->PinballTable->Message(1005, pb::time_next);
|
||||
demo->PinballTable->Message(MessageCode::PlungerInputReleased, pb::time_next);
|
||||
}
|
||||
|
||||
void TDemo::UnFlipRight(int timerId, void* caller)
|
||||
{
|
||||
auto demo = static_cast<TDemo*>(caller);
|
||||
if (demo->FlipRightFlag)
|
||||
demo->PinballTable->Message(1003, pb::time_next);
|
||||
demo->PinballTable->Message(MessageCode::RightFlipperInputReleased, pb::time_next);
|
||||
demo->FlipRightFlag = 0;
|
||||
}
|
||||
|
||||
@@ -156,7 +154,7 @@ void TDemo::UnFlipLeft(int timerId, void* caller)
|
||||
{
|
||||
auto demo = static_cast<TDemo*>(caller);
|
||||
if (demo->FlipLeftFlag)
|
||||
demo->PinballTable->Message(1001, pb::time_next);
|
||||
demo->PinballTable->Message(MessageCode::LeftFlipperInputReleased, pb::time_next);
|
||||
demo->FlipLeftFlag = 0;
|
||||
}
|
||||
|
||||
@@ -170,10 +168,10 @@ void TDemo::FlipRight(int timerId, void* caller)
|
||||
timer::kill(demo->FlipRightTimer);
|
||||
demo->FlipRightTimer = 0;
|
||||
}
|
||||
demo->PinballTable->Message(1002, pb::time_next);
|
||||
demo->PinballTable->Message(MessageCode::RightFlipperInputPressed, pb::time_next);
|
||||
demo->FlipRightFlag = 1;
|
||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - static_cast<float>(rand()) *
|
||||
0.00003051850947599719f * (demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
||||
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||
timer::set(time, demo, UnFlipRight);
|
||||
}
|
||||
}
|
||||
@@ -188,10 +186,10 @@ void TDemo::FlipLeft(int timerId, void* caller)
|
||||
timer::kill(demo->FlipLeftTimer);
|
||||
demo->FlipLeftTimer = 0;
|
||||
}
|
||||
demo->PinballTable->Message(1000, pb::time_next);
|
||||
demo->PinballTable->Message(MessageCode::LeftFlipperInputPressed, pb::time_next);
|
||||
demo->FlipLeftFlag = 1;
|
||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - static_cast<float>(rand()) *
|
||||
0.00003051850947599719f * (demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
||||
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||
timer::set(time, demo, UnFlipLeft);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +197,7 @@ void TDemo::FlipLeft(int timerId, void* caller)
|
||||
void TDemo::NewGameRestartTimer(int timerId, void* caller)
|
||||
{
|
||||
auto demo = static_cast<TDemo*>(caller);
|
||||
pb::replay_level(1);
|
||||
demo->PinballTable->Message(1014, static_cast<float>(demo->PinballTable->PlayerCount));
|
||||
pb::replay_level(true);
|
||||
demo->PinballTable->Message(MessageCode::NewGame, static_cast<float>(demo->PinballTable->PlayerCount));
|
||||
demo->RestartGameTimer = 0;
|
||||
}
|
||||
@@ -6,8 +6,8 @@ class TDemo :
|
||||
{
|
||||
public:
|
||||
TDemo(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void PlungerRelease(int timerId, void* caller);
|
||||
|
||||
@@ -14,30 +14,35 @@ TDrain::TDrain(TPinballTable* table, int groupIndex) : TCollisionComponent(table
|
||||
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||
}
|
||||
|
||||
int TDrain::Message(int code, float value)
|
||||
int TDrain::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024)
|
||||
if (code == MessageCode::Reset)
|
||||
{
|
||||
if (Timer)
|
||||
{
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
}
|
||||
PinballTable->BallInSink = 0;
|
||||
PinballTable->BallInDrainFlag = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TDrain::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TDrain::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
ball->Message(1024, 0.0);
|
||||
PinballTable->BallInSink = 1;
|
||||
Timer = timer::set(TimerTime, this, TimerCallback);
|
||||
control::handler(63, this);
|
||||
ball->Disable();
|
||||
--PinballTable->MultiballCount;
|
||||
if (PinballTable->MultiballCount <= 0)
|
||||
{
|
||||
PinballTable->MultiballCount = 0;
|
||||
PinballTable->BallInDrainFlag = 1;
|
||||
Timer = timer::set(TimerTime, this, TimerCallback);
|
||||
}
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
|
||||
void TDrain::TimerCallback(int timerId, void* caller)
|
||||
{
|
||||
auto drain = static_cast<TDrain*>(caller);
|
||||
control::handler(60, drain);
|
||||
control::handler(MessageCode::ControlTimerExpired, drain);
|
||||
}
|
||||
@@ -6,8 +6,8 @@ class TDrain :
|
||||
{
|
||||
public:
|
||||
TDrain(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void TimerCallback(int timerId, void* caller);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "TEdgeBox.h"
|
||||
|
||||
#include "objlist_class.h"
|
||||
|
||||
TEdgeBox::TEdgeBox()
|
||||
{
|
||||
EdgeList = new objlist_class(0, 4);
|
||||
FieldList = new objlist_class(0, 1);
|
||||
}
|
||||
|
||||
TEdgeBox::~TEdgeBox()
|
||||
{
|
||||
delete EdgeList;
|
||||
delete FieldList;
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
#pragma once
|
||||
class objlist_class;
|
||||
|
||||
struct field_effect_type;
|
||||
class TEdgeSegment;
|
||||
|
||||
class TEdgeBox
|
||||
{
|
||||
public:
|
||||
TEdgeBox();
|
||||
~TEdgeBox();
|
||||
|
||||
objlist_class* EdgeList;
|
||||
objlist_class* FieldList;
|
||||
std::vector<TEdgeSegment*> EdgeList{};
|
||||
std::vector<field_effect_type*> FieldList{};
|
||||
};
|
||||
|
||||
@@ -3,23 +3,24 @@
|
||||
|
||||
|
||||
#include "maths.h"
|
||||
#include "objlist_class.h"
|
||||
#include "TBall.h"
|
||||
#include "TEdgeBox.h"
|
||||
#include "TEdgeSegment.h"
|
||||
#include "TTableLayer.h"
|
||||
|
||||
TEdgeManager::TEdgeManager(float posX, float posY, float width, float height)
|
||||
TEdgeManager::TEdgeManager(float xMin, float yMin, float width, float height)
|
||||
{
|
||||
X = posX;
|
||||
Y = posY;
|
||||
Width = width;
|
||||
Height = height;
|
||||
MinX = xMin;
|
||||
MinY = yMin;
|
||||
MaxX = MinX + width;
|
||||
MaxY = MinY + height;
|
||||
MaxBoxX = 10;
|
||||
MaxBoxY = 15;
|
||||
AdvanceX = width / 10.0f;
|
||||
AdvanceY = height / 15.0f;
|
||||
AdvanceXInv = 1.0f / AdvanceX;
|
||||
AdvanceYInv = 1.0f / AdvanceY;
|
||||
BoxArray = new TEdgeBox[150];
|
||||
AdvanceX = width / static_cast<float>(MaxBoxX);
|
||||
AdvanceY = height / static_cast<float>(MaxBoxY);
|
||||
BoxArray = new TEdgeBox[MaxBoxX * MaxBoxY];
|
||||
}
|
||||
|
||||
TEdgeManager::~TEdgeManager()
|
||||
@@ -29,53 +30,61 @@ TEdgeManager::~TEdgeManager()
|
||||
|
||||
int TEdgeManager::box_x(float x)
|
||||
{
|
||||
return static_cast<int>((max(0, min(floor((x - X) * AdvanceXInv), (MaxBoxX - 1)))));
|
||||
return std::max(0, std::min(static_cast<int>(floor((x - MinX) / AdvanceX)), MaxBoxX - 1));
|
||||
}
|
||||
|
||||
int TEdgeManager::box_y(float y)
|
||||
{
|
||||
return static_cast<int>((max(0, min(floor((y - Y) * AdvanceYInv), (MaxBoxY - 1)))));
|
||||
return std::max(0, std::min(static_cast<int>(floor((y - MinY) / AdvanceY)), MaxBoxY - 1));
|
||||
}
|
||||
|
||||
int TEdgeManager::increment_box_x(int x)
|
||||
{
|
||||
return min(x + 1, MaxBoxX - 1);
|
||||
return std::min(x + 1, MaxBoxX - 1);
|
||||
}
|
||||
|
||||
int TEdgeManager::increment_box_y(int y)
|
||||
{
|
||||
return min(y + 1, MaxBoxY - 1);
|
||||
return std::min(y + 1, MaxBoxY - 1);
|
||||
}
|
||||
|
||||
void TEdgeManager::add_edge_to_box(int x, int y, TEdgeSegment* edge)
|
||||
{
|
||||
BoxArray[x + y * MaxBoxX].EdgeList->Add(edge);
|
||||
assertm((unsigned)x < (unsigned)MaxBoxX && (unsigned)y < (unsigned)MaxBoxY, "Box coordinates out of range");
|
||||
|
||||
auto& list = BoxArray[x + y * MaxBoxX].EdgeList;
|
||||
assertm(std::find(list.begin(), list.end(), edge) == list.end(), "Duplicate inserted into box");
|
||||
list.push_back(edge);
|
||||
}
|
||||
|
||||
void TEdgeManager::add_field_to_box(int x, int y, field_effect_type* field)
|
||||
{
|
||||
BoxArray[x + y * MaxBoxX].FieldList->Add(field);
|
||||
assertm((unsigned)x < (unsigned)MaxBoxX && (unsigned)y < (unsigned)MaxBoxY, "Box coordinates out of range");
|
||||
|
||||
auto& list = BoxArray[x + y * MaxBoxX].FieldList;
|
||||
assertm(std::find(list.begin(), list.end(), field) == list.end(), "Duplicate inserted into box");
|
||||
list.push_back(field);
|
||||
}
|
||||
|
||||
int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeDst, ray_type* ray, TBall* ball,
|
||||
int edgeIndex)
|
||||
{
|
||||
if (x >= 0 && x < 10 && y >= 0 && y < 15)
|
||||
if (x >= 0 && x < MaxBoxX && y >= 0 && y < MaxBoxY)
|
||||
{
|
||||
TEdgeBox* edgeBox = &BoxArray[x + y * MaxBoxX];
|
||||
TEdgeSegment** edgePtr = &EdgeArray[edgeIndex];
|
||||
for (auto index = edgeBox->EdgeList->Count() - 1; index >= 0; --index)
|
||||
for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it)
|
||||
{
|
||||
auto edge = static_cast<TEdgeSegment*>(edgeBox->EdgeList->Get(index));
|
||||
if (!edge->ProcessedFlag && *edge->ActiveFlag && (edge->CollisionGroup & ray->FieldFlag))
|
||||
auto edge = *it;
|
||||
if (!edge->ProcessedFlag && *edge->ActiveFlagPtr && (edge->CollisionGroup & ray->CollisionMask) != 0)
|
||||
{
|
||||
if (!ball->already_hit(edge))
|
||||
if (!ball->already_hit(*edge))
|
||||
{
|
||||
++edgeIndex;
|
||||
*edgePtr = edge;
|
||||
++edgePtr;
|
||||
edge->ProcessedFlag = 1;
|
||||
auto dist = edge->FindCollisionDistance(ray);
|
||||
auto dist = edge->FindCollisionDistance(*ray);
|
||||
if (dist < *distPtr)
|
||||
{
|
||||
*distPtr = dist;
|
||||
@@ -88,20 +97,20 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD
|
||||
return edgeIndex;
|
||||
}
|
||||
|
||||
void TEdgeManager::FieldEffects(TBall* ball, vector_type* dstVec)
|
||||
void TEdgeManager::FieldEffects(TBall* ball, vector2* dstVec)
|
||||
{
|
||||
vector_type vec{};
|
||||
vector2 vec{};
|
||||
TEdgeBox* edgeBox = &BoxArray[box_x(ball->Position.X) + box_y(ball->Position.Y) *
|
||||
MaxBoxX];
|
||||
|
||||
for (int index = edgeBox->FieldList->Count() - 1; index >= 0; --index)
|
||||
for (auto it = edgeBox->FieldList.rbegin(); it != edgeBox->FieldList.rend(); ++it)
|
||||
{
|
||||
auto field = static_cast<field_effect_type*>(edgeBox->FieldList->Get(index));
|
||||
if (*field->Flag2Ptr && ball->FieldFlag & field->Mask)
|
||||
auto field = *it;
|
||||
if (*field->ActiveFlag && ball->CollisionMask & field->CollisionGroup)
|
||||
{
|
||||
if (field->CollisionComp->FieldEffect(ball, &vec))
|
||||
{
|
||||
maths::vector_add(dstVec, &vec);
|
||||
maths::vector_add(*dstVec, vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,157 +121,83 @@ float TEdgeManager::FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegme
|
||||
auto distance = 1000000000.0f;
|
||||
auto edgeIndex = 0;
|
||||
|
||||
auto rayX = ray->Origin.X;
|
||||
auto rayY = ray->Origin.Y;
|
||||
auto rayBoxX = box_x(rayX);
|
||||
auto rayBoxY = box_y(rayY);
|
||||
auto x0 = ray->Origin.X;
|
||||
auto y0 = ray->Origin.Y;
|
||||
auto x1 = ray->Direction.X * ray->MaxDistance + ray->Origin.X;
|
||||
auto y1 = ray->Direction.Y * ray->MaxDistance + ray->Origin.Y;
|
||||
|
||||
auto rayEndX = ray->Direction.X * ray->MaxDistance + ray->Origin.X;
|
||||
auto rayEndY = ray->Direction.Y * ray->MaxDistance + ray->Origin.Y;
|
||||
auto rayEndBoxX = box_x(rayEndX);
|
||||
auto rayEndBoxY = box_y(rayEndY);
|
||||
auto xBox0 = box_x(x0);
|
||||
auto yBox0 = box_y(y0);
|
||||
auto xBox1 = box_x(x1);
|
||||
auto yBox1 = box_y(y1);
|
||||
|
||||
auto rayDirX = rayX >= rayEndX ? -1 : 1;
|
||||
auto rayDirY = rayY >= rayEndY ? -1 : 1;
|
||||
auto dirX = x0 >= x1 ? -1 : 1;
|
||||
auto dirY = y0 >= y1 ? -1 : 1;
|
||||
|
||||
if (rayBoxY == rayEndBoxY)
|
||||
if (yBox0 == yBox1)
|
||||
{
|
||||
if (rayDirX == 1)
|
||||
if (dirX == 1)
|
||||
{
|
||||
for (auto indexX = rayBoxX; indexX <= rayEndBoxX; indexX++)
|
||||
for (auto indexX = xBox0; indexX <= xBox1; indexX++)
|
||||
{
|
||||
edgeIndex = TestGridBox(indexX, rayBoxY, &distance, edge, ray, ball, edgeIndex);
|
||||
edgeIndex = TestGridBox(indexX, yBox0, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto indexX = rayBoxX; indexX >= rayEndBoxX; indexX--)
|
||||
for (auto indexX = xBox0; indexX >= xBox1; indexX--)
|
||||
{
|
||||
edgeIndex = TestGridBox(indexX, rayBoxY, &distance, edge, ray, ball, edgeIndex);
|
||||
edgeIndex = TestGridBox(indexX, yBox0, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rayBoxX == rayEndBoxX)
|
||||
else if (xBox0 == xBox1)
|
||||
{
|
||||
if (rayDirY == 1)
|
||||
if (dirY == 1)
|
||||
{
|
||||
for (auto indexY = rayBoxY; indexY <= rayEndBoxY; indexY++)
|
||||
for (auto indexY = yBox0; indexY <= yBox1; indexY++)
|
||||
{
|
||||
edgeIndex = TestGridBox(rayBoxX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
edgeIndex = TestGridBox(xBox0, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto indexY = rayBoxY; indexY >= rayEndBoxY; indexY--)
|
||||
for (auto indexY = yBox0; indexY >= yBox1; indexY--)
|
||||
{
|
||||
edgeIndex = TestGridBox(rayBoxX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
edgeIndex = TestGridBox(xBox0, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rayDyDX = (rayY - rayEndY) / (rayX - rayEndX);
|
||||
auto indexX = rayBoxX;
|
||||
auto indexY = rayBoxY;
|
||||
auto bresIndexX = rayBoxX + 1;
|
||||
auto bresIndexY = rayBoxY + 1;
|
||||
auto bresXAdd = rayY - rayDyDX * rayX;
|
||||
edgeIndex = TestGridBox(rayBoxX, rayBoxY, &distance, edge, ray, ball, 0);
|
||||
if (rayDirX == 1)
|
||||
edgeIndex = TestGridBox(xBox0, yBox0, &distance, edge, ray, ball, 0);
|
||||
|
||||
// Bresenham line formula: y = dYdX * (x - x0) + y0; dYdX = (y0 - y1) / (x0 - x1)
|
||||
auto dyDx = (y0 - y1) / (x0 - x1);
|
||||
// Precompute constant part: dYdX * (-x0) + y0
|
||||
auto precomp = -x0 * dyDx + y0;
|
||||
// X and Y indexes are offset by one when going forwards, not sure why
|
||||
auto xBias = dirX == 1 ? 1 : 0, yBias = dirY == 1 ? 1 : 0;
|
||||
|
||||
for (auto indexX = xBox0, indexY = yBox0; indexX != xBox1 || indexY != yBox1;)
|
||||
{
|
||||
if (rayDirY == 1)
|
||||
// Calculate y from indexY and from line formula
|
||||
auto yDiscrete = (indexY + yBias) * AdvanceY + MinY;
|
||||
auto ylinear = ((indexX + xBias) * AdvanceX + MinX) * dyDx + precomp;
|
||||
if (dirY == 1 ? ylinear >= yDiscrete : ylinear <= yDiscrete)
|
||||
{
|
||||
do
|
||||
{
|
||||
auto yCoord = bresIndexY * AdvanceY + Y;
|
||||
auto xCoord = (bresIndexX * AdvanceX + X) * rayDyDX + bresXAdd;
|
||||
if (xCoord >= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
{
|
||||
++indexX;
|
||||
++bresIndexX;
|
||||
}
|
||||
++indexY;
|
||||
++bresIndexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
++indexX;
|
||||
++bresIndexX;
|
||||
}
|
||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
while (indexX < rayEndBoxX || indexY < rayEndBoxY);
|
||||
// Advance indexY when discrete value is ahead/behind
|
||||
// Advance indexX when discrete value matches linear value
|
||||
indexY += dirY;
|
||||
if (ylinear == yDiscrete)
|
||||
indexX += dirX;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
auto yCoord = indexY * AdvanceY + Y;
|
||||
auto xCoord = (bresIndexX * AdvanceX + X) * rayDyDX + bresXAdd;
|
||||
if (xCoord <= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
{
|
||||
++indexX;
|
||||
++bresIndexX;
|
||||
}
|
||||
--indexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
++indexX;
|
||||
++bresIndexX;
|
||||
}
|
||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
while (indexX < rayEndBoxX || indexY > rayEndBoxY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rayDirY == 1)
|
||||
{
|
||||
do
|
||||
{
|
||||
auto yCoord = bresIndexY * AdvanceY + Y;
|
||||
auto xCoord = (indexX * AdvanceX + X) * rayDyDX + bresXAdd;
|
||||
if (xCoord >= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
--indexX;
|
||||
++indexY;
|
||||
++bresIndexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
--indexX;
|
||||
}
|
||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
while (indexX > rayEndBoxX || indexY < rayEndBoxY);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
auto yCoord = indexY * AdvanceY + Y;
|
||||
auto xCoord = (indexX * AdvanceX + X) * rayDyDX + bresXAdd;
|
||||
if (xCoord <= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
--indexX;
|
||||
--indexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
--indexX;
|
||||
}
|
||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
while (indexX > rayEndBoxX || indexY > rayEndBoxY);
|
||||
// Advance indexX otherwise
|
||||
indexX += dirX;
|
||||
}
|
||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,3 +209,23 @@ float TEdgeManager::FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegme
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
vector2 TEdgeManager::NormalizeBox(vector2 pt) const
|
||||
{
|
||||
// Standard PB Box ranges: X [-8, 8]; Y [-14, 15]; Top right corner: (-8, -14)
|
||||
// Bring them to: X [0, 16]; Y [0, 29]; Top right corner: (0, 0)
|
||||
auto x = Clamp(pt.X, MinX, MaxX) + abs(MinX);
|
||||
auto y = Clamp(pt.Y, MinY, MaxY) + abs(MinY);
|
||||
|
||||
// Normalize and invert to: X [0, 1]; Y [0, 1]; Top right corner: (1, 1)
|
||||
x /= Width; y /= Height;
|
||||
return vector2{ 1 - x, 1 - y };
|
||||
}
|
||||
|
||||
vector2 TEdgeManager::DeNormalizeBox(vector2 pt) const
|
||||
{
|
||||
// Undo normalization by applying steps in reverse
|
||||
auto x = (1 - pt.X) * Width - abs(MinX);
|
||||
auto y = (1 - pt.Y) * Height - abs(MinY);
|
||||
return vector2{ x, y };
|
||||
}
|
||||
@@ -6,17 +6,17 @@ class TEdgeBox;
|
||||
|
||||
struct field_effect_type
|
||||
{
|
||||
char* Flag2Ptr;
|
||||
int Mask;
|
||||
char* ActiveFlag;
|
||||
int CollisionGroup;
|
||||
TCollisionComponent* CollisionComp;
|
||||
};
|
||||
|
||||
class TEdgeManager
|
||||
{
|
||||
public:
|
||||
TEdgeManager(float posX, float posY, float width, float height);
|
||||
TEdgeManager(float xMin, float yMin, float width, float height);
|
||||
~TEdgeManager();
|
||||
void FieldEffects(TBall* ball, struct vector_type* dstVec);
|
||||
void FieldEffects(TBall* ball, struct vector2* dstVec);
|
||||
int box_x(float x);
|
||||
int box_y(float y);
|
||||
int increment_box_x(int x);
|
||||
@@ -25,15 +25,19 @@ public:
|
||||
void add_field_to_box(int x, int y, field_effect_type* field);
|
||||
int TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeDst, ray_type* ray, TBall* ball, int edgeIndex);
|
||||
float FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegment** edge);
|
||||
vector2 NormalizeBox(vector2 pt) const;
|
||||
vector2 DeNormalizeBox(vector2 pt) const;
|
||||
|
||||
float AdvanceX;
|
||||
float AdvanceY;
|
||||
float AdvanceXInv;
|
||||
float AdvanceYInv;
|
||||
int MaxBoxX;
|
||||
int MaxBoxY;
|
||||
float X;
|
||||
float Y;
|
||||
float MinX;
|
||||
float MinY;
|
||||
float MaxX;
|
||||
float MaxY;
|
||||
float Width;
|
||||
float Height;
|
||||
TEdgeBox* BoxArray;
|
||||
TEdgeSegment* EdgeArray[1000];
|
||||
TEdgeSegment* EdgeArray[1000]{};
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "TEdgeSegment.h"
|
||||
|
||||
#include "objlist_class.h"
|
||||
#include "TCircle.h"
|
||||
#include "TCollisionComponent.h"
|
||||
#include "TLine.h"
|
||||
@@ -9,7 +8,7 @@
|
||||
TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup)
|
||||
{
|
||||
CollisionComponent = collComp;
|
||||
ActiveFlag = activeFlag;
|
||||
ActiveFlagPtr = activeFlag;
|
||||
CollisionGroup = collisionGroup;
|
||||
ProcessedFlag = 0;
|
||||
}
|
||||
@@ -19,9 +18,10 @@ void TEdgeSegment::port_draw()
|
||||
}
|
||||
|
||||
TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
||||
unsigned int collisionGroup, float offset, int wallValue)
|
||||
unsigned int collisionGroup, float offset, size_t wallValue)
|
||||
{
|
||||
vector_type center{}, start{}, end{}, prevCenter{}, vec1{}, vec2{}, dstVec{};
|
||||
vector2 center{}, start{}, end{}, prevCenter{};
|
||||
vector3 vec1{}, vec2{}, dstVec{};
|
||||
TEdgeSegment* edge = nullptr;
|
||||
|
||||
wall_type wallType = static_cast<wall_type>(static_cast<int>(floor(*floatArr) - 1.0f));
|
||||
@@ -33,15 +33,10 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||
center.Y = floatArr[2];
|
||||
auto radius = offset + floatArr[3];
|
||||
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
circle->place_in_grid(&collComp->AABB);
|
||||
collComp->EdgeList.push_back(circle);
|
||||
edge = circle;
|
||||
|
||||
if (circle)
|
||||
{
|
||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
circle->place_in_grid();
|
||||
}
|
||||
|
||||
collComp->EdgeList->Add(circle);
|
||||
break;
|
||||
}
|
||||
case wall_type::Line:
|
||||
@@ -50,16 +45,12 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||
start.Y = floatArr[2];
|
||||
end.X = floatArr[3];
|
||||
end.Y = floatArr[4];
|
||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, &start, &end);
|
||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, start, end);
|
||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
line->Offset(offset);
|
||||
line->place_in_grid(&collComp->AABB);
|
||||
collComp->EdgeList.push_back(line);
|
||||
edge = line;
|
||||
|
||||
if (line)
|
||||
{
|
||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
line->Offset(offset);
|
||||
line->place_in_grid();
|
||||
collComp->EdgeList->Add(line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -87,25 +78,21 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||
|
||||
center.X = centerX1;
|
||||
center.Y = centerY1;
|
||||
if (offset != 0.0)
|
||||
if (offset != 0.0f)
|
||||
{
|
||||
vec1.X = centerX1 - prevCenter.X;
|
||||
vec1.Y = center.Y - prevCenter.Y;
|
||||
vec2.X = centerX2 - centerX1;
|
||||
vec2.Y = centerY2 - center.Y;
|
||||
maths::cross(&vec1, &vec2, &dstVec);
|
||||
if (dstVec.Z > 0.0 && offset > 0.0 ||
|
||||
dstVec.Z < 0.0 && offset < 0.0)
|
||||
maths::cross(vec1, vec2, dstVec);
|
||||
if ((dstVec.Z > 0.0f && offset > 0.0f) ||
|
||||
(dstVec.Z < 0.0f && offset < 0.0f))
|
||||
{
|
||||
float radius = offset * 1.001f;
|
||||
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
||||
|
||||
if (circle)
|
||||
{
|
||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
circle->place_in_grid();
|
||||
collComp->EdgeList->Add(circle);
|
||||
}
|
||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
circle->place_in_grid(&collComp->AABB);
|
||||
collComp->EdgeList.push_back(circle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,17 +100,13 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||
start.Y = floatArrPtr[1];
|
||||
end.X = floatArrPtr[2];
|
||||
end.Y = floatArrPtr[3];
|
||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, &start, &end);
|
||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, start, end);
|
||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
line->Offset(offset);
|
||||
line->place_in_grid(&collComp->AABB);
|
||||
collComp->EdgeList.push_back(line);
|
||||
|
||||
edge = line;
|
||||
|
||||
if (line)
|
||||
{
|
||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||
line->Offset(offset);
|
||||
line->place_in_grid();
|
||||
collComp->EdgeList->Add(line);
|
||||
}
|
||||
|
||||
prevCenter = center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class TBall;
|
||||
class TCollisionComponent;
|
||||
struct ray_type;
|
||||
struct RectF;
|
||||
|
||||
enum class wall_type : int
|
||||
{
|
||||
@@ -14,19 +15,19 @@ class TEdgeSegment
|
||||
{
|
||||
public:
|
||||
TCollisionComponent* CollisionComponent;
|
||||
char* ActiveFlag;
|
||||
char* ActiveFlagPtr;
|
||||
char ProcessedFlag;
|
||||
void* WallValue;
|
||||
void* WallValue{};
|
||||
unsigned int CollisionGroup;
|
||||
|
||||
TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup);
|
||||
virtual ~TEdgeSegment() = default;
|
||||
|
||||
virtual void EdgeCollision(TBall* ball, float coef) = 0;
|
||||
virtual void EdgeCollision(TBall* ball, float distance) = 0;
|
||||
virtual void port_draw();
|
||||
virtual void place_in_grid() = 0;
|
||||
virtual float FindCollisionDistance(ray_type* ray) = 0;
|
||||
virtual void place_in_grid(RectF* aabb) = 0;
|
||||
virtual float FindCollisionDistance(const ray_type& ray) = 0;
|
||||
|
||||
static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
||||
unsigned int collisionGroup, float offset, int wallValue);
|
||||
unsigned int collisionGroup, float offset, size_t wallValue);
|
||||
};
|
||||
@@ -4,38 +4,36 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "render.h"
|
||||
#include "TBall.h"
|
||||
#include "timer.h"
|
||||
#include "TLine.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||
{
|
||||
visualStruct visual{};
|
||||
vector_type end{}, start{};
|
||||
vector2 end{}, start{};
|
||||
|
||||
Timer = 0;
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
end.X = *visual.FloatArr;
|
||||
end.X = visual.FloatArr[0];
|
||||
end.Y = visual.FloatArr[1];
|
||||
start.X = visual.FloatArr[2];
|
||||
start.Y = visual.FloatArr[3];
|
||||
auto line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &start, &end);
|
||||
auto line = new TLine(this, &ActiveFlag, visual.CollisionGroup, start, end);
|
||||
if (line)
|
||||
{
|
||||
line->place_in_grid();
|
||||
EdgeList->Add(line);
|
||||
line->place_in_grid(&AABB);
|
||||
EdgeList.push_back(line);
|
||||
}
|
||||
|
||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &end, &start);
|
||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, end, start);
|
||||
PrevCollider = line;
|
||||
if (line)
|
||||
{
|
||||
line->place_in_grid();
|
||||
EdgeList->Add(line);
|
||||
line->place_in_grid(&AABB);
|
||||
EdgeList.push_back(line);
|
||||
}
|
||||
|
||||
SpeedDecrement = 0.64999998f;
|
||||
@@ -52,9 +50,9 @@ TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionCom
|
||||
MinSpeed = *minSpeed;
|
||||
}
|
||||
|
||||
int TFlagSpinner::Message(int code, float value)
|
||||
int TFlagSpinner::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024)
|
||||
if (code == MessageCode::Reset)
|
||||
{
|
||||
if (Timer)
|
||||
{
|
||||
@@ -62,28 +60,21 @@ int TFlagSpinner::Message(int code, float value)
|
||||
Timer = 0;
|
||||
}
|
||||
BmpIndex = 0;
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
auto zMap = static_cast<zmap_header_type*>(ListZMap->Get(0));
|
||||
render::sprite_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - PinballTable->XOffset,
|
||||
bmp->YPosition - PinballTable->YOffset);
|
||||
SpriteSet(BmpIndex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TFlagSpinner::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void TFlagSpinner::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge)
|
||||
{
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance = ball->RayMaxDistance - coef;
|
||||
ball->RayMaxDistance = ball->RayMaxDistance - distance;
|
||||
ball->not_again(edge);
|
||||
|
||||
SpinDirection = 2 * (PrevCollider != edge) - 1;
|
||||
if (ball->Speed == 0.0)
|
||||
if (ball->Speed == 0.0f)
|
||||
Speed = MinSpeed;
|
||||
else
|
||||
Speed = ball->Speed * 20.0f;
|
||||
@@ -94,22 +85,11 @@ void TFlagSpinner::Collision(TBall* ball, vector_type* nextPosition, vector_type
|
||||
NextFrame();
|
||||
}
|
||||
|
||||
void TFlagSpinner::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 2)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TFlagSpinner::get_scoring(int index)
|
||||
{
|
||||
return index < 2 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TFlagSpinner::NextFrame()
|
||||
{
|
||||
BmpIndex += SpinDirection;
|
||||
int bmpIndex = BmpIndex;
|
||||
int bmpCount = ListBitmap->Count();
|
||||
int bmpCount = static_cast<int>(ListBitmap->size());
|
||||
if (bmpIndex >= bmpCount)
|
||||
BmpIndex = 0;
|
||||
else if (bmpIndex < 0)
|
||||
@@ -117,22 +97,14 @@ void TFlagSpinner::NextFrame()
|
||||
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
control::handler(63, this);
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
if (SoftHitSoundId)
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
loader::play_sound(SoftHitSoundId, this, "TFlagSpinner");
|
||||
if (!BmpIndex)
|
||||
control::handler(62, this);
|
||||
control::handler(MessageCode::ControlSpinnerLoopReset, this);
|
||||
}
|
||||
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(BmpIndex));
|
||||
auto zMap = static_cast<zmap_header_type*>(ListZMap->Get(BmpIndex));
|
||||
render::sprite_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - PinballTable->XOffset,
|
||||
bmp->YPosition - PinballTable->YOffset);
|
||||
|
||||
SpriteSet(BmpIndex);
|
||||
Speed *= SpeedDecrement;
|
||||
if (Speed >= MinSpeed)
|
||||
{
|
||||
|
||||
@@ -6,23 +6,20 @@ class TFlagSpinner :
|
||||
{
|
||||
public:
|
||||
TFlagSpinner(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void NextFrame();
|
||||
|
||||
static void SpinTimer(int timerId, void* caller);
|
||||
|
||||
float Speed;
|
||||
float Speed{};
|
||||
float MaxSpeed;
|
||||
float MinSpeed;
|
||||
float SpeedDecrement;
|
||||
int SpinDirection;
|
||||
int BmpIndex;
|
||||
int SpinDirection{};
|
||||
int BmpIndex{};
|
||||
int Timer;
|
||||
TEdgeSegment* PrevCollider;
|
||||
int Scores[2];
|
||||
};
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include "loader.h"
|
||||
#include "pb.h"
|
||||
#include "render.h"
|
||||
#include "TBall.h"
|
||||
#include "TFlipperEdge.h"
|
||||
#include "timer.h"
|
||||
#include "TZmapList.h"
|
||||
#include "TPinballTable.h"
|
||||
|
||||
TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||
{
|
||||
@@ -18,18 +19,14 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||
HardHitSoundId = visual.SoundIndex4;
|
||||
SoftHitSoundId = visual.SoundIndex3;
|
||||
Elasticity = visual.Elasticity;
|
||||
Timer = 0;
|
||||
Smoothness = visual.Smoothness;
|
||||
|
||||
auto floatArr = loader::query_float_attribute(groupIndex, 0, 803);
|
||||
auto floatArr2 = loader::query_float_attribute(groupIndex, 0, 805);
|
||||
auto floatArr3 = loader::query_float_attribute(groupIndex, 0, 804);
|
||||
auto collMult = *floatArr;
|
||||
auto bmpCoef2 = *floatArr2;
|
||||
auto bmpCoef1 = *floatArr3;
|
||||
auto vecT2 = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 802));
|
||||
auto vecT1 = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 801));
|
||||
auto origin = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 800));
|
||||
auto collMult = *loader::query_float_attribute(groupIndex, 0, 803);
|
||||
auto retractTime = *loader::query_float_attribute(groupIndex, 0, 805);
|
||||
auto extendTime = *loader::query_float_attribute(groupIndex, 0, 804);
|
||||
auto vecT2 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 802));
|
||||
auto vecT1 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 801));
|
||||
auto origin = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 800));
|
||||
auto flipperEdge = new TFlipperEdge(
|
||||
this,
|
||||
&ActiveFlag,
|
||||
@@ -38,86 +35,72 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||
origin,
|
||||
vecT1,
|
||||
vecT2,
|
||||
bmpCoef1,
|
||||
bmpCoef2,
|
||||
extendTime,
|
||||
retractTime,
|
||||
collMult,
|
||||
Elasticity,
|
||||
Smoothness);
|
||||
flipperEdge->place_in_grid(&AABB);
|
||||
|
||||
FlipperEdge = flipperEdge;
|
||||
if (flipperEdge)
|
||||
{
|
||||
BmpCoef1 = flipperEdge->BmpCoef1 / static_cast<float>(ListBitmap->Count() - 1);
|
||||
BmpCoef2 = flipperEdge->BmpCoef2 / static_cast<float>(ListBitmap->Count() - 1);
|
||||
}
|
||||
BmpIndex = 0;
|
||||
InputTime = 0.0;
|
||||
if (table)
|
||||
table->FlipperList.push_back(this);
|
||||
}
|
||||
|
||||
TFlipper::~TFlipper()
|
||||
{
|
||||
delete FlipperEdge;
|
||||
if (PinballTable)
|
||||
{
|
||||
auto& flippers = PinballTable->FlipperList;
|
||||
auto position = std::find(flippers.begin(), flippers.end(), this);
|
||||
if (position != flippers.end())
|
||||
flippers.erase(position);
|
||||
}
|
||||
}
|
||||
|
||||
int TFlipper::Message(int code, float value)
|
||||
int TFlipper::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1 || code == 2 || code > 1008 && code <= 1011 || code == 1022)
|
||||
switch (code)
|
||||
{
|
||||
float timerTime;
|
||||
int soundIndex = 0, code2 = code;
|
||||
if (code == 1)
|
||||
case MessageCode::TFlipperExtend:
|
||||
case MessageCode::TFlipperRetract:
|
||||
case MessageCode::Resume:
|
||||
case MessageCode::LooseFocus:
|
||||
case MessageCode::SetTiltLock:
|
||||
case MessageCode::GameOver:
|
||||
if (code == MessageCode::TFlipperExtend)
|
||||
{
|
||||
control::handler(1, this);
|
||||
TimerTime = BmpCoef1;
|
||||
soundIndex = HardHitSoundId;
|
||||
control::handler(MessageCode::TFlipperExtend, this);
|
||||
loader::play_sound(HardHitSoundId, this, "TFlipper1");
|
||||
}
|
||||
else if (code == 2)
|
||||
else if (code == MessageCode::TFlipperRetract)
|
||||
{
|
||||
TimerTime = BmpCoef2;
|
||||
soundIndex = SoftHitSoundId;
|
||||
loader::play_sound(SoftHitSoundId, this, "TFlipper2");
|
||||
}
|
||||
else
|
||||
{
|
||||
code2 = 2;
|
||||
TimerTime = BmpCoef2;
|
||||
// Retract for all non-input messages
|
||||
code = MessageCode::TFlipperRetract;
|
||||
}
|
||||
|
||||
if (soundIndex)
|
||||
loader::play_sound(soundIndex);
|
||||
if (Timer)
|
||||
{
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
}
|
||||
MessageField = FlipperEdge->SetMotion(code);
|
||||
break;
|
||||
case MessageCode::PlayerChanged:
|
||||
case MessageCode::Reset:
|
||||
if (MessageField)
|
||||
{
|
||||
auto v10 = value - FlipperEdge->InputTime;
|
||||
timerTime = v10 - floor(v10 / TimerTime) * TimerTime;
|
||||
if (timerTime < 0.0)
|
||||
timerTime = 0.0;
|
||||
FlipperEdge->CurrentAngle = 0;
|
||||
FlipperEdge->set_control_points(0);
|
||||
MessageField = 0;
|
||||
FlipperEdge->SetMotion(MessageCode::Reset);
|
||||
UpdateSprite();
|
||||
}
|
||||
else
|
||||
{
|
||||
timerTime = TimerTime;
|
||||
}
|
||||
MessageField = code2;
|
||||
InputTime = value;
|
||||
Timer = timer::set(timerTime, this, TimerExpired);
|
||||
FlipperEdge->SetMotion(code2, value);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (code == 1020 || code == 1024)
|
||||
{
|
||||
if (MessageField)
|
||||
{
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
BmpIndex = -1;
|
||||
MessageField = 2;
|
||||
TimerExpired(Timer, this);
|
||||
FlipperEdge->SetMotion(code, value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,63 +109,95 @@ void TFlipper::port_draw()
|
||||
FlipperEdge->port_draw();
|
||||
}
|
||||
|
||||
void TFlipper::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TFlipper::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
}
|
||||
|
||||
void TFlipper::TimerExpired(int timerId, void* caller)
|
||||
void TFlipper::UpdateSprite()
|
||||
{
|
||||
auto flip = static_cast<TFlipper*>(caller);
|
||||
int timer; // eax
|
||||
auto bmpCountSub1 = static_cast<int>(ListBitmap->size()) - 1;
|
||||
auto newBmpIndex = static_cast<int>(floor(FlipperEdge->CurrentAngle / FlipperEdge->AngleMax * bmpCountSub1 + 0.5f));
|
||||
newBmpIndex = Clamp(newBmpIndex, 0, bmpCountSub1);
|
||||
if (BmpIndex == newBmpIndex)
|
||||
return;
|
||||
|
||||
bool bmpIndexOutOfBounds = false;
|
||||
auto bmpIndexAdvance = static_cast<int>(floor((pb::time_now - flip->InputTime) / flip->TimerTime + 0.5f));
|
||||
int bmpCount = flip->ListBitmap->Count();
|
||||
if (bmpIndexAdvance > bmpCount)
|
||||
bmpIndexAdvance = bmpCount;
|
||||
if (bmpIndexAdvance < 0)
|
||||
bmpIndexAdvance = 0;
|
||||
|
||||
if (!bmpIndexAdvance)
|
||||
bmpIndexAdvance = 1;
|
||||
|
||||
if (flip->MessageField == 1)
|
||||
BmpIndex = newBmpIndex;
|
||||
SpriteSet(BmpIndex);
|
||||
}
|
||||
|
||||
int TFlipper::GetFlipperStepAngle(float dt, float* dst) const
|
||||
{
|
||||
if (!MessageField)
|
||||
return 0;
|
||||
|
||||
auto deltaAngle = FlipperEdge->flipper_angle_delta(dt);
|
||||
auto step = std::fabs(std::ceil(FlipperEdge->DistanceDiv * deltaAngle * FlipperEdge->InvT1Radius));
|
||||
if (step > 3.0f)
|
||||
step = 3.0f;
|
||||
if (step >= 2.0f)
|
||||
{
|
||||
*dst = deltaAngle / step;
|
||||
return static_cast<int>(step);
|
||||
}
|
||||
|
||||
*dst = deltaAngle;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void TFlipper::FlipperCollision(float deltaAngle)
|
||||
{
|
||||
if (!MessageField)
|
||||
return;
|
||||
|
||||
ray_type ray{}, rayDst{};
|
||||
ray.MinDistance = 0.002f;
|
||||
bool collisionFlag = false;
|
||||
for (auto ball : pb::MainTable->BallList)
|
||||
{
|
||||
flip->BmpIndex += bmpIndexAdvance;
|
||||
int countSub1 = flip->ListBitmap->Count() - 1;
|
||||
if (flip->BmpIndex >= countSub1)
|
||||
if ((FlipperEdge->CollisionGroup & ball->CollisionMask) != 0 &&
|
||||
FlipperEdge->YMax >= ball->Position.Y && FlipperEdge->YMin <= ball->Position.Y &&
|
||||
FlipperEdge->XMax >= ball->Position.X && FlipperEdge->XMin <= ball->Position.X)
|
||||
{
|
||||
flip->BmpIndex = countSub1;
|
||||
bmpIndexOutOfBounds = true;
|
||||
if (FlipperEdge->ControlPointDirtyFlag)
|
||||
FlipperEdge->set_control_points(FlipperEdge->CurrentAngle);
|
||||
ray.CollisionMask = ball->CollisionMask;
|
||||
ray.Origin = ball->Position;
|
||||
|
||||
float sin, cos;
|
||||
auto ballPosRot = ray.Origin;
|
||||
maths::SinCos(-deltaAngle, sin, cos);
|
||||
maths::RotatePt(ballPosRot, sin, cos, FlipperEdge->RotOrigin);
|
||||
ray.Direction.X = ballPosRot.X - ray.Origin.X;
|
||||
ray.Direction.Y = ballPosRot.Y - ray.Origin.Y;
|
||||
ray.MaxDistance = maths::normalize_2d(ray.Direction);
|
||||
auto distance = maths::distance_to_flipper(FlipperEdge, ray, rayDst);
|
||||
if (distance < 1e9f)
|
||||
{
|
||||
FlipperEdge->NextBallPosition = ball->Position;
|
||||
FlipperEdge->CollisionDirection = rayDst.Direction;
|
||||
FlipperEdge->EdgeCollision(ball, distance);
|
||||
collisionFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flip->MessageField == 2)
|
||||
|
||||
if (collisionFlag)
|
||||
{
|
||||
flip->BmpIndex -= bmpIndexAdvance;
|
||||
timer = 0;
|
||||
if (flip->BmpIndex <= 0)
|
||||
{
|
||||
flip->BmpIndex = 0;
|
||||
bmpIndexOutOfBounds = true;
|
||||
}
|
||||
auto angleAdvance = deltaAngle / (std::fabs(FlipperEdge->MoveSpeed) * 5.0f);
|
||||
FlipperEdge->CurrentAngle -= angleAdvance;
|
||||
FlipperEdge->AngleRemainder += std::fabs(angleAdvance);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = 0;
|
||||
FlipperEdge->CurrentAngle += deltaAngle;
|
||||
FlipperEdge->AngleRemainder -= std::fabs(deltaAngle);
|
||||
}
|
||||
|
||||
if (bmpIndexOutOfBounds)
|
||||
flip->MessageField = 0;
|
||||
else
|
||||
timer = timer::set(flip->TimerTime, flip, TimerExpired);
|
||||
flip->Timer = timer;
|
||||
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(flip->ListBitmap->Get(flip->BmpIndex));
|
||||
auto zMap = static_cast<zmap_header_type*>(flip->ListZMap->Get(flip->BmpIndex));
|
||||
render::sprite_set(
|
||||
flip->RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - flip->PinballTable->XOffset,
|
||||
bmp->YPosition - flip->PinballTable->YOffset);
|
||||
if (FlipperEdge->AngleRemainder <= 0.0001f)
|
||||
{
|
||||
FlipperEdge->CurrentAngle = FlipperEdge->AngleDst;
|
||||
FlipperEdge->FlipperFlag = MessageCode::TFlipperNull;
|
||||
MessageField = 0;
|
||||
}
|
||||
FlipperEdge->ControlPointDirtyFlag = true;
|
||||
}
|
||||
@@ -9,18 +9,14 @@ class TFlipper :
|
||||
public:
|
||||
TFlipper(TPinballTable* table, int groupIndex);
|
||||
~TFlipper() override;
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
void port_draw() override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
void UpdateSprite();
|
||||
int GetFlipperStepAngle(float dt, float* dst) const;
|
||||
void FlipperCollision(float deltaAngle);
|
||||
|
||||
int BmpIndex;
|
||||
TFlipperEdge* FlipperEdge;
|
||||
int Timer;
|
||||
float BmpCoef1;
|
||||
float BmpCoef2;
|
||||
float TimerTime;
|
||||
float InputTime;
|
||||
};
|
||||
@@ -2,29 +2,26 @@
|
||||
#include "TFlipperEdge.h"
|
||||
|
||||
|
||||
#include "pb.h"
|
||||
#include "TLine.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TTableLayer.h"
|
||||
|
||||
float TFlipperEdge::flipper_sin_angle, TFlipperEdge::flipper_cos_angle;
|
||||
vector_type TFlipperEdge::A1, TFlipperEdge::A2, TFlipperEdge::B1, TFlipperEdge::B2, TFlipperEdge::T1;
|
||||
line_type TFlipperEdge::lineA, TFlipperEdge::lineB;
|
||||
circle_type TFlipperEdge::circlebase, TFlipperEdge::circleT1;
|
||||
|
||||
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
|
||||
vector_type* origin, vector_type* vecT1, vector_type* vecT2, float bmpCoef1, float bmpCoef2,
|
||||
float collMult, float elasticity, float smoothness): TEdgeSegment(collComp, activeFlag, collisionGroup)
|
||||
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup,
|
||||
TPinballTable* table,
|
||||
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed,
|
||||
float collMult, float elasticity, float smoothness): TEdgeSegment(
|
||||
collComp, activeFlag, collisionGroup)
|
||||
{
|
||||
vector_type crossProd{}, vecDir1{}, vecDir2{};
|
||||
vector3 crossProd{}, vecOriginT1{}, vecOriginT2{};
|
||||
|
||||
Elasticity = elasticity;
|
||||
Smoothness = smoothness;
|
||||
BmpCoef1 = bmpCoef1;
|
||||
BmpCoef2 = bmpCoef2;
|
||||
CollisionMult = collMult;
|
||||
|
||||
T1Src = *vecT1;
|
||||
T2Src = *vecT2;
|
||||
T1Src = static_cast<vector2>(*vecT1);
|
||||
T2Src = static_cast<vector2>(*vecT2);
|
||||
RotOrigin.X = origin->X;
|
||||
RotOrigin.Y = origin->Y;
|
||||
|
||||
@@ -36,294 +33,146 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
|
||||
CircleT1RadiusMSq = CircleT1Radius * 1.01f * (CircleT1Radius * 1.01f);
|
||||
CircleT1RadiusSq = CircleT1Radius * CircleT1Radius;
|
||||
|
||||
vecDir1.X = vecT1->X - origin->X;
|
||||
vecDir1.Y = vecT1->Y - origin->Y;
|
||||
vecDir1.Z = 0.0;
|
||||
maths::normalize_2d(&vecDir1);
|
||||
vecOriginT1.X = vecT1->X - origin->X;
|
||||
vecOriginT1.Y = vecT1->Y - origin->Y;
|
||||
vecOriginT1.Z = 0.0;
|
||||
maths::normalize_2d(vecOriginT1);
|
||||
|
||||
vecDir2.X = vecT2->X - origin->X;
|
||||
vecDir2.Y = vecT2->Y - origin->Y;
|
||||
vecDir2.Z = 0.0;
|
||||
maths::normalize_2d(&vecDir2);
|
||||
vecOriginT2.X = vecT2->X - origin->X;
|
||||
vecOriginT2.Y = vecT2->Y - origin->Y;
|
||||
vecOriginT2.Z = 0.0;
|
||||
maths::normalize_2d(vecOriginT2);
|
||||
|
||||
AngleMax = acos(maths::DotProduct(&vecDir1, &vecDir2));
|
||||
maths::cross(&vecDir1, &vecDir2, &crossProd);
|
||||
if (crossProd.Z < 0.0)
|
||||
AngleMax = acos(maths::DotProduct(vecOriginT1, vecOriginT2));
|
||||
maths::cross(vecOriginT1, vecOriginT2, crossProd);
|
||||
if (crossProd.Z < 0.0f)
|
||||
AngleMax = -AngleMax;
|
||||
FlipperFlag = 0;
|
||||
Angle1 = 0.0;
|
||||
|
||||
auto dirX1 = vecDir1.X;
|
||||
auto dirY1 = -vecDir1.Y;
|
||||
A2Src.X = dirY1 * CirclebaseRadius + origin->X;
|
||||
A2Src.Y = dirX1 * CirclebaseRadius + origin->Y;
|
||||
A1Src.X = dirY1 * CircleT1Radius + vecT1->X;
|
||||
A1Src.Y = dirX1 * CircleT1Radius + vecT1->Y;
|
||||
FlipperFlag = MessageCode::TFlipperNull;
|
||||
AngleDst = 0.0;
|
||||
|
||||
dirX1 = -dirX1;
|
||||
dirY1 = -dirY1;
|
||||
B1Src.X = dirY1 * CirclebaseRadius + origin->X;
|
||||
B1Src.Y = dirX1 * CirclebaseRadius + origin->Y;
|
||||
B2Src.X = dirY1 * CircleT1Radius + vecT1->X;
|
||||
B2Src.Y = dirX1 * CircleT1Radius + vecT1->Y;
|
||||
|
||||
if (AngleMax < 0.0)
|
||||
// 3DPB and FT have different formats for flipper speed:
|
||||
// 3DPB: Time it takes for flipper to go from source to destination, in sec.
|
||||
// FT: Flipper movement speed, in radians per sec.
|
||||
if (!pb::FullTiltMode)
|
||||
{
|
||||
maths::vswap(&A1Src, &B1Src);
|
||||
maths::vswap(&A2Src, &B2Src);
|
||||
auto angleMax = std::abs(AngleMax);
|
||||
retractSpeed = angleMax / retractSpeed;
|
||||
extendSpeed = angleMax / extendSpeed;
|
||||
}
|
||||
ExtendSpeed = extendSpeed;
|
||||
RetractSpeed = retractSpeed;
|
||||
|
||||
const vector2 perpOriginT1Cc = {-vecOriginT1.Y, vecOriginT1.X};
|
||||
A2Src.X = perpOriginT1Cc.X * CirclebaseRadius + origin->X;
|
||||
A2Src.Y = perpOriginT1Cc.Y * CirclebaseRadius + origin->Y;
|
||||
A1Src.X = perpOriginT1Cc.X * CircleT1Radius + vecT1->X;
|
||||
A1Src.Y = perpOriginT1Cc.Y * CircleT1Radius + vecT1->Y;
|
||||
|
||||
const vector2 perpOriginT1C = {vecOriginT1.Y, -vecOriginT1.X};
|
||||
B1Src.X = perpOriginT1C.X * CirclebaseRadius + origin->X;
|
||||
B1Src.Y = perpOriginT1C.Y * CirclebaseRadius + origin->Y;
|
||||
B2Src.X = perpOriginT1C.X * CircleT1Radius + vecT1->X;
|
||||
B2Src.Y = perpOriginT1C.Y * CircleT1Radius + vecT1->Y;
|
||||
|
||||
if (AngleMax < 0.0f)
|
||||
{
|
||||
std::swap(A1Src, B1Src);
|
||||
std::swap(A2Src, B2Src);
|
||||
}
|
||||
|
||||
auto dx = vecT1->X - RotOrigin.X;
|
||||
auto dy = vecT1->Y - RotOrigin.Y;
|
||||
auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z;
|
||||
DistanceDiv = distance1;
|
||||
DistanceDivSq = distance1 * distance1;
|
||||
InvT1Radius = 1.0f / CircleT1Radius * 1.5f;
|
||||
|
||||
float bmpCoef = min(BmpCoef1, BmpCoef2);
|
||||
auto distance = maths::Distance(vecT1, vecT2);
|
||||
CollisionTimeAdvance = bmpCoef / (distance / CircleT1Radius + distance / CircleT1Radius);
|
||||
|
||||
TFlipperEdge::place_in_grid();
|
||||
EdgeCollisionFlag = 0;
|
||||
InputTime = 0.0;
|
||||
CollisionFlag1 = 0;
|
||||
AngleStopTime = 0.0;
|
||||
AngleMult = 0.0;
|
||||
if (AngleMax <= 0.0f)
|
||||
{
|
||||
ExtendSpeed = -ExtendSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
RetractSpeed = -RetractSpeed;
|
||||
}
|
||||
set_control_points(CurrentAngle);
|
||||
}
|
||||
|
||||
void TFlipperEdge::port_draw()
|
||||
{
|
||||
set_control_points(InputTime);
|
||||
build_edges_in_motion();
|
||||
set_control_points(CurrentAngle);
|
||||
}
|
||||
|
||||
float TFlipperEdge::FindCollisionDistance(ray_type* ray)
|
||||
float TFlipperEdge::FindCollisionDistance(const ray_type& ray)
|
||||
{
|
||||
auto ogRay = ray;
|
||||
ray_type dstRay{}, srcRay{};
|
||||
ray_type dstRay{};
|
||||
if (ControlPointDirtyFlag)
|
||||
set_control_points(CurrentAngle);
|
||||
auto distance = maths::distance_to_flipper(this, ray, dstRay);
|
||||
if (distance >= 1e9f)
|
||||
return 1e9f;
|
||||
|
||||
if (ogRay->TimeNow > AngleStopTime)
|
||||
NextBallPosition = dstRay.Origin;
|
||||
CollisionDirection = dstRay.Direction;
|
||||
return distance;
|
||||
}
|
||||
|
||||
void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
|
||||
{
|
||||
if (FlipperFlag == MessageCode::TFlipperNull)
|
||||
{
|
||||
FlipperFlag = 0;
|
||||
maths::basic_collision(
|
||||
ball,
|
||||
&NextBallPosition,
|
||||
&CollisionDirection,
|
||||
Elasticity,
|
||||
Smoothness,
|
||||
1e9f,
|
||||
0);
|
||||
return;
|
||||
}
|
||||
if (EdgeCollisionFlag == 0)
|
||||
|
||||
vector2 t1NextPos{NextBallPosition.X - T1.X, NextBallPosition.Y - T1.Y},
|
||||
t1RotOrigin{RotOrigin.X - T1.X, RotOrigin.Y - T1.Y};
|
||||
auto crossProduct = maths::cross(t1RotOrigin, t1NextPos);
|
||||
|
||||
bool frontCollision = false;
|
||||
if (crossProduct <= 0)
|
||||
{
|
||||
if (FlipperFlag == 0)
|
||||
{
|
||||
EdgeCollisionFlag = 0;
|
||||
CollisionFlag1 = 0;
|
||||
CollisionFlag2 = 0;
|
||||
set_control_points(ogRay->TimeNow);
|
||||
build_edges_in_motion();
|
||||
auto ballInside = is_ball_inside(ogRay->Origin.X, ogRay->Origin.Y);
|
||||
srcRay.MinDistance = ogRay->MinDistance;
|
||||
if (ballInside == 0)
|
||||
{
|
||||
srcRay.Direction = ogRay->Direction;
|
||||
srcRay.MaxDistance = ogRay->MaxDistance;
|
||||
srcRay.Origin = ogRay->Origin;
|
||||
auto distance = maths::distance_to_flipper(&srcRay, &dstRay);
|
||||
if (distance == 0.0)
|
||||
{
|
||||
NextBallPosition = dstRay.Origin;
|
||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
||||
}
|
||||
else
|
||||
{
|
||||
NextBallPosition = dstRay.Origin;
|
||||
}
|
||||
CollisionDirection = dstRay.Direction;
|
||||
return distance;
|
||||
}
|
||||
if (AngleMax > 0)
|
||||
frontCollision = true;
|
||||
}
|
||||
else if (AngleMax <= 0)
|
||||
{
|
||||
frontCollision = true;
|
||||
}
|
||||
|
||||
if (maths::Distance_Squared(ogRay->Origin, RotOrigin) >= CirclebaseRadiusMSq)
|
||||
{
|
||||
if (maths::Distance_Squared(ogRay->Origin, T1) >= CircleT1RadiusMSq)
|
||||
{
|
||||
srcRay.Direction.Y = lineB.PerpendicularL.Y;
|
||||
srcRay.Direction.X = lineB.PerpendicularL.X;
|
||||
if (ballInside == 4)
|
||||
{
|
||||
srcRay.Direction.Y = lineA.PerpendicularL.Y;
|
||||
srcRay.Direction.X = lineA.PerpendicularL.X;
|
||||
}
|
||||
srcRay.Direction.X = -srcRay.Direction.X;
|
||||
srcRay.Direction.Y = -srcRay.Direction.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
srcRay.Direction.X = T1.X - ogRay->Origin.X;
|
||||
srcRay.Direction.Y = T1.Y - ogRay->Origin.Y;
|
||||
maths::normalize_2d(&srcRay.Direction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
srcRay.Direction.X = RotOrigin.X - ogRay->Origin.X;
|
||||
srcRay.Direction.Y = RotOrigin.Y - ogRay->Origin.Y;
|
||||
maths::normalize_2d(&srcRay.Direction);
|
||||
}
|
||||
|
||||
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
|
||||
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
|
||||
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
|
||||
if (maths::distance_to_flipper(&srcRay, &dstRay) >= 1e+09)
|
||||
{
|
||||
srcRay.Direction.X = RotOrigin.X - ogRay->Origin.X;
|
||||
srcRay.Direction.Y = RotOrigin.Y - ogRay->Origin.Y;
|
||||
maths::normalize_2d(&srcRay.Direction);
|
||||
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
|
||||
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
|
||||
if (maths::distance_to_flipper(&srcRay, &dstRay) >= 1e+09)
|
||||
{
|
||||
return 1e+09;
|
||||
}
|
||||
}
|
||||
|
||||
NextBallPosition = dstRay.Origin;
|
||||
CollisionDirection = dstRay.Direction;
|
||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
auto posX = ogRay->Origin.X;
|
||||
auto posY = ogRay->Origin.Y;
|
||||
auto posXAdvance = ogRay->Direction.X * CollisionTimeAdvance;
|
||||
auto posYAdvance = ogRay->Direction.Y * CollisionTimeAdvance;
|
||||
auto rayMaxDistance = ogRay->MaxDistance * CollisionTimeAdvance;
|
||||
auto timeNow = ogRay->TimeNow;
|
||||
auto stopTime = ogRay->TimeDelta + ogRay->TimeNow;
|
||||
while (timeNow < stopTime)
|
||||
{
|
||||
set_control_points(timeNow);
|
||||
build_edges_in_motion();
|
||||
auto ballInside = is_ball_inside(posX, posY);
|
||||
if (ballInside != 0)
|
||||
{
|
||||
vector_type* linePtr;
|
||||
if (FlipperFlag == 1 && ballInside != 5)
|
||||
{
|
||||
linePtr = &lineA.PerpendicularL;
|
||||
srcRay.Direction.Y = lineA.PerpendicularL.Y;
|
||||
srcRay.Direction.X = lineA.PerpendicularL.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FlipperFlag != 2 || ballInside == 4)
|
||||
{
|
||||
CollisionFlag1 = 0;
|
||||
CollisionFlag2 = 1;
|
||||
srcRay.Direction.X = RotOrigin.X - posX;
|
||||
srcRay.Direction.Y = RotOrigin.Y - posY;
|
||||
maths::normalize_2d(&srcRay.Direction);
|
||||
|
||||
srcRay.Origin.X = posX - srcRay.Direction.X * 5.0f;
|
||||
srcRay.Origin.Y = posY - srcRay.Direction.Y * 5.0f;
|
||||
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
|
||||
if (maths::distance_to_flipper(&srcRay, &dstRay) >= 1e+09)
|
||||
{
|
||||
NextBallPosition.X = posX;
|
||||
NextBallPosition.Y = posY;
|
||||
CollisionDirection.X = -srcRay.Direction.X;
|
||||
CollisionDirection.Y = -srcRay.Direction.Y;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
NextBallPosition = dstRay.Origin;
|
||||
CollisionDirection = dstRay.Direction;
|
||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
||||
return 0.0;
|
||||
}
|
||||
linePtr = &lineB.PerpendicularL;
|
||||
srcRay.Direction.Y = lineB.PerpendicularL.Y;
|
||||
srcRay.Direction.X = lineB.PerpendicularL.X;
|
||||
}
|
||||
|
||||
CollisionLinePerp = *linePtr;
|
||||
CollisionFlag2 = 0;
|
||||
CollisionFlag1 = 1;
|
||||
srcRay.Direction.X = -srcRay.Direction.X;
|
||||
srcRay.Direction.Y = -srcRay.Direction.Y;
|
||||
srcRay.MinDistance = 0.002f;
|
||||
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
|
||||
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
|
||||
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
|
||||
auto distance = maths::distance_to_flipper(&srcRay, &dstRay);
|
||||
CollisionDirection = dstRay.Direction;
|
||||
if (distance >= 1e+09)
|
||||
{
|
||||
return 1e+09;
|
||||
}
|
||||
NextBallPosition = dstRay.Origin;
|
||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
srcRay.Direction = ogRay->Direction;
|
||||
srcRay.MinDistance = ogRay->MinDistance;
|
||||
srcRay.Origin = ogRay->Origin;
|
||||
srcRay.MaxDistance = rayMaxDistance;
|
||||
auto distance = maths::distance_to_flipper(&srcRay, &dstRay);
|
||||
if (distance < 1e+09)
|
||||
{
|
||||
NextBallPosition = dstRay.Origin;
|
||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
||||
vector_type* linePtr;
|
||||
if (FlipperFlag == 2)
|
||||
{
|
||||
linePtr = &lineB.PerpendicularL;
|
||||
CollisionFlag1 = AngleMax <= 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CollisionFlag1 = AngleMax > 0.0;
|
||||
linePtr = &lineA.PerpendicularL;
|
||||
}
|
||||
CollisionLinePerp = *linePtr;
|
||||
CollisionDirection = dstRay.Direction;
|
||||
return distance;
|
||||
}
|
||||
timeNow = timeNow + CollisionTimeAdvance;
|
||||
posX = posX + posXAdvance;
|
||||
posY = posY + posYAdvance;
|
||||
}
|
||||
if (FlipperFlag == MessageCode::TFlipperRetract)
|
||||
{
|
||||
frontCollision ^= true;
|
||||
CollisionLinePerp = LineB.PerpendicularC;
|
||||
}
|
||||
else
|
||||
{
|
||||
EdgeCollisionFlag = 0;
|
||||
CollisionLinePerp = LineA.PerpendicularC;
|
||||
}
|
||||
return 1e+09;
|
||||
}
|
||||
|
||||
void TFlipperEdge::EdgeCollision(TBall* ball, float coef)
|
||||
{
|
||||
EdgeCollisionFlag = 1;
|
||||
if (!FlipperFlag || !CollisionFlag2 || CollisionFlag1)
|
||||
auto dx = NextBallPosition.X - RotOrigin.X;
|
||||
auto dy = NextBallPosition.Y - RotOrigin.Y;
|
||||
auto distanceSq = dy * dy + dx * dx;
|
||||
if (frontCollision)
|
||||
{
|
||||
float boost = 0.0;
|
||||
if (CollisionFlag1)
|
||||
float boost = 0;
|
||||
if (circlebase.RadiusSq * 1.01f < distanceSq)
|
||||
{
|
||||
float dx = NextBallPosition.X - RotOrigin.X;
|
||||
float dy = NextBallPosition.Y - RotOrigin.Y;
|
||||
float distance = dy * dy + dx * dx;
|
||||
if (circlebase.RadiusSq * 1.01 < distance)
|
||||
{
|
||||
float v11;
|
||||
float v20 = sqrt(distance / DistanceDivSq) * (fabs(AngleMax) / AngleMult);
|
||||
float dot1 = maths::DotProduct(&CollisionLinePerp, &CollisionDirection);
|
||||
if (dot1 >= 0.0)
|
||||
v11 = dot1 * v20;
|
||||
else
|
||||
v11 = 0.0;
|
||||
boost = v11 * CollisionMult;
|
||||
}
|
||||
auto v21 = std::fabs(MoveSpeed) * std::sqrt(distanceSq / DistanceDivSq);
|
||||
auto dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
|
||||
if (dot1 >= 0)
|
||||
boost = CollisionMult * dot1 * v21;
|
||||
}
|
||||
|
||||
float threshold = boost <= 0.0 ? 1000000000.0f : -1.0f;
|
||||
auto threshold = boost <= 0.0f ? 1e9f : -1.0f;
|
||||
maths::basic_collision(
|
||||
ball,
|
||||
&NextBallPosition,
|
||||
@@ -332,159 +181,93 @@ void TFlipperEdge::EdgeCollision(TBall* ball, float coef)
|
||||
Smoothness,
|
||||
threshold,
|
||||
boost);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto elasticity = Elasticity;
|
||||
if (circlebase.RadiusSq * 1.01f < distanceSq)
|
||||
elasticity = (1.0f - std::sqrt(distanceSq / DistanceDivSq)) * Elasticity;
|
||||
maths::basic_collision(ball, &NextBallPosition, &CollisionDirection, elasticity, Smoothness, 1e9f, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
void TFlipperEdge::place_in_grid(RectF* aabb)
|
||||
{
|
||||
auto xMax = std::max(std::max(T2Src.X + CircleT1Radius, T1Src.X + CircleT1Radius), RotOrigin.X + CirclebaseRadius);
|
||||
auto yMax = std::max(std::max(T2Src.Y + CircleT1Radius, T1Src.Y + CircleT1Radius), RotOrigin.Y + CirclebaseRadius);
|
||||
auto xMin = std::min(std::min(T2Src.X - CircleT1Radius, T1Src.X - CircleT1Radius), RotOrigin.X - CirclebaseRadius);
|
||||
auto yMin = std::min(std::min(T2Src.Y - CircleT1Radius, T1Src.Y - CircleT1Radius), RotOrigin.Y - CirclebaseRadius);
|
||||
|
||||
if (aabb)
|
||||
{
|
||||
aabb->Merge({xMax, yMax, xMin, yMin});
|
||||
}
|
||||
|
||||
float elasticity;
|
||||
float dx = NextBallPosition.X - RotOrigin.X;
|
||||
float dy = NextBallPosition.Y - RotOrigin.Y;
|
||||
float distance = dy * dy + dx * dx;
|
||||
if (circlebase.RadiusSq * 1.01 < distance)
|
||||
elasticity = (1.0f - sqrt(distance / DistanceDivSq)) * Elasticity;
|
||||
else
|
||||
elasticity = Elasticity;
|
||||
maths::basic_collision(ball, &NextBallPosition, &CollisionDirection, elasticity, Smoothness, 1000000000.0, 0.0);
|
||||
TTableLayer::edges_insert_square(yMin, xMin, yMax, xMax, this, nullptr);
|
||||
|
||||
auto offset = 1.0f / InvT1Radius + pb::BallHalfRadius;
|
||||
XMin = xMin - offset;
|
||||
YMin = yMin - offset;
|
||||
XMax = xMax + offset;
|
||||
YMax = yMax + offset;
|
||||
}
|
||||
|
||||
void TFlipperEdge::place_in_grid()
|
||||
void TFlipperEdge::set_control_points(float angle)
|
||||
{
|
||||
float x0 = RotOrigin.X - CirclebaseRadius;
|
||||
float y0 = RotOrigin.Y - CirclebaseRadius;
|
||||
float x1 = RotOrigin.X + CirclebaseRadius;
|
||||
float y1 = RotOrigin.Y + CirclebaseRadius;
|
||||
|
||||
float v2 = T1Src.X - CircleT1Radius;
|
||||
if (v2 < x0)
|
||||
x0 = v2;
|
||||
|
||||
float v3 = T1Src.Y - CircleT1Radius;
|
||||
if (v3 < y0)
|
||||
y0 = v3;
|
||||
|
||||
float v4 = T1Src.X + CircleT1Radius;
|
||||
if (v4 > x1)
|
||||
x1 = v4;
|
||||
|
||||
float v5 = T1Src.Y + CircleT1Radius;
|
||||
if (v5 > y1)
|
||||
y1 = v5;
|
||||
|
||||
float v6 = T2Src.X - CircleT1Radius;
|
||||
if (v6 < x0)
|
||||
x0 = v6;
|
||||
|
||||
float v7 = T2Src.Y - CircleT1Radius;
|
||||
if (v7 < y0)
|
||||
y0 = v7;
|
||||
|
||||
float v8 = T2Src.X + CircleT1Radius;
|
||||
if (v8 > x1)
|
||||
x1 = v8;
|
||||
|
||||
float v9 = T2Src.Y + CircleT1Radius;
|
||||
if (v9 > y1)
|
||||
y1 = v9;
|
||||
|
||||
TTableLayer::edges_insert_square(y0, x0, y1, x1, this, nullptr);
|
||||
}
|
||||
|
||||
void TFlipperEdge::set_control_points(float timeNow)
|
||||
{
|
||||
maths::SinCos(flipper_angle(timeNow), &flipper_sin_angle, &flipper_cos_angle);
|
||||
float sin, cos;
|
||||
maths::SinCos(angle, sin, cos);
|
||||
A1 = A1Src;
|
||||
A2 = A2Src;
|
||||
B1 = B1Src;
|
||||
B2 = B2Src;
|
||||
T1 = T1Src;
|
||||
maths::RotatePt(&A1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
||||
maths::RotatePt(&A2, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
||||
maths::RotatePt(&T1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
||||
maths::RotatePt(&B1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
||||
maths::RotatePt(&B2, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
||||
maths::RotatePt(A1, sin, cos, RotOrigin);
|
||||
maths::RotatePt(A2, sin, cos, RotOrigin);
|
||||
maths::RotatePt(T1, sin, cos, RotOrigin);
|
||||
maths::RotatePt(B1, sin, cos, RotOrigin);
|
||||
maths::RotatePt(B2, sin, cos, RotOrigin);
|
||||
maths::line_init(LineA, A1.X, A1.Y, A2.X, A2.Y);
|
||||
maths::line_init(LineB, B1.X, B1.Y, B2.X, B2.Y);
|
||||
circlebase = {RotOrigin, CirclebaseRadiusSq};
|
||||
circleT1 = {T1, CircleT1RadiusSq};
|
||||
ControlPointDirtyFlag = false;
|
||||
}
|
||||
|
||||
void TFlipperEdge::build_edges_in_motion()
|
||||
float TFlipperEdge::flipper_angle_delta(float timeDelta)
|
||||
{
|
||||
maths::line_init(&lineA, A1.X, A1.Y, A2.X, A2.Y);
|
||||
maths::line_init(&lineB, B1.X, B1.Y, B2.X, B2.Y);
|
||||
circlebase.RadiusSq = CirclebaseRadiusSq;
|
||||
circlebase.Center.X = RotOrigin.X;
|
||||
circlebase.Center.Y = RotOrigin.Y;
|
||||
circleT1.RadiusSq = CircleT1RadiusSq;
|
||||
circleT1.Center.X = T1.X;
|
||||
circleT1.Center.Y = T1.Y;
|
||||
if (FlipperFlag == MessageCode::TFlipperNull)
|
||||
return 0.0f;
|
||||
|
||||
const auto deltaAngle = MoveSpeed * timeDelta;
|
||||
if (std::fabs(deltaAngle) > AngleRemainder)
|
||||
return AngleDst - CurrentAngle;
|
||||
return deltaAngle;
|
||||
}
|
||||
|
||||
float TFlipperEdge::flipper_angle(float timeNow)
|
||||
{
|
||||
if (!FlipperFlag)
|
||||
return Angle1;
|
||||
float angle = (Angle1 - Angle2) / AngleMax * AngleMult;
|
||||
if (angle < 0.0)
|
||||
angle = -angle;
|
||||
|
||||
if (angle >= 0.0000001)
|
||||
angle = (timeNow - InputTime) / angle;
|
||||
else
|
||||
angle = 1.0;
|
||||
|
||||
angle = min(1, max(angle, 0));
|
||||
if (FlipperFlag == 2)
|
||||
angle = 1.0f - angle;
|
||||
return angle * AngleMax;
|
||||
}
|
||||
|
||||
int TFlipperEdge::is_ball_inside(float x, float y)
|
||||
{
|
||||
vector_type testPoint{};
|
||||
float dx = RotOrigin.X - x;
|
||||
float dy = RotOrigin.Y - y;
|
||||
if ((A2.X - A1.X) * (y - A1.Y) - (A2.Y - A1.Y) * (x - A1.X) >= 0.0 &&
|
||||
(B1.X - A2.X) * (y - A2.Y) - (B1.Y - A2.Y) * (x - A2.X) >= 0.0 &&
|
||||
(B2.X - B1.X) * (y - B1.Y) - (B2.Y - B1.Y) * (x - B1.X) >= 0.0 &&
|
||||
(A1.X - B2.X) * (y - B2.Y) - (A1.Y - B2.Y) * (x - B2.X) >= 0.0 ||
|
||||
dy * dy + dx * dx <= CirclebaseRadiusSq ||
|
||||
(T1.Y - y) * (T1.Y - y) + (T1.X - x) * (T1.X - x) < CircleT1RadiusSq)
|
||||
{
|
||||
float flipperLR = AngleMax < 0.0 ? -1.0f : 1.0f;
|
||||
if (FlipperFlag == 1)
|
||||
testPoint = AngleMax < 0.0 ? B1 : B2;
|
||||
else if (FlipperFlag == 2)
|
||||
testPoint = AngleMax < 0.0 ? A2 : A1;
|
||||
else
|
||||
testPoint = T1;
|
||||
|
||||
if (((y - testPoint.Y) * (RotOrigin.X - testPoint.X) -
|
||||
(x - testPoint.X) * (RotOrigin.Y - testPoint.Y)) * flipperLR < 0.0)
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TFlipperEdge::SetMotion(int code, float value)
|
||||
int TFlipperEdge::SetMotion(MessageCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 1:
|
||||
Angle2 = flipper_angle(value);
|
||||
Angle1 = AngleMax;
|
||||
AngleMult = BmpCoef1;
|
||||
case MessageCode::TFlipperExtend:
|
||||
AngleRemainder = std::fabs(AngleMax - CurrentAngle);
|
||||
AngleDst = AngleMax;
|
||||
MoveSpeed = ExtendSpeed;
|
||||
break;
|
||||
case 2:
|
||||
Angle2 = flipper_angle(value);
|
||||
Angle1 = 0.0;
|
||||
AngleMult = BmpCoef2;
|
||||
case MessageCode::TFlipperRetract:
|
||||
AngleRemainder = std::fabs(CurrentAngle);
|
||||
AngleDst = 0.0f;
|
||||
MoveSpeed = RetractSpeed;
|
||||
break;
|
||||
case MessageCode::Reset:
|
||||
AngleRemainder = 0.0f;
|
||||
AngleDst = 0.0f;
|
||||
break;
|
||||
case 1024:
|
||||
FlipperFlag = 0;
|
||||
Angle1 = 0.0;
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!FlipperFlag)
|
||||
InputTime = value;
|
||||
if (AngleRemainder == 0.0f)
|
||||
code = MessageCode::TFlipperNull;
|
||||
|
||||
FlipperFlag = code;
|
||||
AngleStopTime = AngleMult + InputTime;
|
||||
return static_cast<int>(code);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "maths.h"
|
||||
#include "TEdgeSegment.h"
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
class TPinballTable;
|
||||
|
||||
@@ -8,22 +9,20 @@ class TFlipperEdge : public TEdgeSegment
|
||||
{
|
||||
public:
|
||||
TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
|
||||
vector_type* origin, vector_type* vecT1, vector_type* vecT2, float bmpCoef1, float bmpCoef2, float collMult,
|
||||
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult,
|
||||
float elasticity, float smoothness);
|
||||
void port_draw() override;
|
||||
float FindCollisionDistance(ray_type* ray) override;
|
||||
void EdgeCollision(TBall* ball, float coef) override;
|
||||
void place_in_grid() override;
|
||||
void set_control_points(float timeNow);
|
||||
void build_edges_in_motion();
|
||||
float flipper_angle(float timeNow);
|
||||
int is_ball_inside(float x, float y);
|
||||
void SetMotion(int code, float value);
|
||||
float FindCollisionDistance(const ray_type& ray) override;
|
||||
void EdgeCollision(TBall* ball, float distance) override;
|
||||
void place_in_grid(RectF* aabb) override;
|
||||
void set_control_points(float angle);
|
||||
float flipper_angle_delta(float timeDelta);
|
||||
int SetMotion(MessageCode code);
|
||||
|
||||
int FlipperFlag;
|
||||
MessageCode FlipperFlag{};
|
||||
float Elasticity;
|
||||
float Smoothness;
|
||||
vector_type RotOrigin;
|
||||
vector2 RotOrigin{};
|
||||
float CirclebaseRadius;
|
||||
float CircleT1Radius;
|
||||
float CirclebaseRadiusSq;
|
||||
@@ -31,31 +30,27 @@ public:
|
||||
float CirclebaseRadiusMSq;
|
||||
float CircleT1RadiusMSq;
|
||||
float AngleMax;
|
||||
float Angle2;
|
||||
float Angle1;
|
||||
int CollisionFlag1;
|
||||
int CollisionFlag2;
|
||||
vector_type CollisionLinePerp;
|
||||
vector_type A1Src;
|
||||
vector_type A2Src;
|
||||
vector_type B1Src;
|
||||
vector_type B2Src;
|
||||
float AngleRemainder{};
|
||||
float AngleDst;
|
||||
float CurrentAngle{};
|
||||
vector2 CollisionLinePerp{};
|
||||
vector2 A1Src{};
|
||||
vector2 A2Src{};
|
||||
vector2 B1Src{};
|
||||
vector2 B2Src{};
|
||||
float CollisionMult;
|
||||
vector_type T1Src;
|
||||
vector_type T2Src;
|
||||
float DistanceDivSq;
|
||||
float CollisionTimeAdvance;
|
||||
vector_type CollisionDirection;
|
||||
int EdgeCollisionFlag;
|
||||
float InputTime;
|
||||
float AngleStopTime;
|
||||
float AngleMult;
|
||||
float BmpCoef1;
|
||||
float BmpCoef2;
|
||||
vector_type NextBallPosition;
|
||||
|
||||
static float flipper_sin_angle, flipper_cos_angle;
|
||||
static vector_type A1, A2, B1, B2, T1;
|
||||
static line_type lineA, lineB;
|
||||
static circle_type circlebase, circleT1;
|
||||
vector2 T1Src{};
|
||||
vector2 T2Src{};
|
||||
float DistanceDiv, DistanceDivSq;
|
||||
vector2 CollisionDirection{};
|
||||
float ExtendSpeed;
|
||||
float RetractSpeed;
|
||||
float MoveSpeed;
|
||||
vector2 NextBallPosition{};
|
||||
vector2 A1, A2, B1, B2, T1;
|
||||
line_type LineA, LineB;
|
||||
circle_type circlebase, circleT1;
|
||||
float InvT1Radius;
|
||||
float YMin, YMax, XMin, XMax;
|
||||
bool ControlPointDirtyFlag{};
|
||||
};
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "render.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TGate::TGate(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
@@ -15,28 +14,29 @@ TGate::TGate(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
SoundIndex4 = visual.SoundIndex4;
|
||||
SoundIndex3 = visual.SoundIndex3;
|
||||
ActiveFlag = 1;
|
||||
render::sprite_set_bitmap(RenderSprite, static_cast<gdrv_bitmap8*>(ListBitmap->Get(0)));
|
||||
control::handler(1024, this);
|
||||
SpriteSet(0);
|
||||
control::handler(MessageCode::Reset, this);
|
||||
}
|
||||
|
||||
int TGate::Message(int code, float value)
|
||||
int TGate::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code != 1020)
|
||||
switch (code)
|
||||
{
|
||||
if (code == 53)
|
||||
{
|
||||
ActiveFlag = 0;
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
loader::play_sound(SoundIndex3);
|
||||
}
|
||||
else if (code == 54 || code == 1024)
|
||||
{
|
||||
ActiveFlag = 1;
|
||||
render::sprite_set_bitmap(RenderSprite, static_cast<gdrv_bitmap8*>(ListBitmap->Get(0)));
|
||||
if (code == 54)
|
||||
loader::play_sound(SoundIndex4);
|
||||
}
|
||||
control::handler(code, this);
|
||||
case MessageCode::TGateDisable:
|
||||
ActiveFlag = 0;
|
||||
SpriteSet(-1);
|
||||
loader::play_sound(SoundIndex3, this, "TGate1");
|
||||
break;
|
||||
case MessageCode::Reset:
|
||||
case MessageCode::TGateEnable:
|
||||
ActiveFlag = 1;
|
||||
SpriteSet(0);
|
||||
if (code == MessageCode::TGateEnable)
|
||||
loader::play_sound(SoundIndex4, this, "TGate2");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
control::handler(code, this);
|
||||
return 0;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ class TGate :
|
||||
{
|
||||
public:
|
||||
TGate(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
|
||||
int SoundIndex4;
|
||||
int SoundIndex3;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "pb.h"
|
||||
#include "TBall.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
@@ -18,105 +18,105 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
Unknown4 = 0.050000001f;
|
||||
MessageField = 0;
|
||||
Timer = 0;
|
||||
BallCapturedFlag = 0;
|
||||
auto floatArr1 = loader::query_float_attribute(groupIndex, 0, 407);
|
||||
if (floatArr1)
|
||||
Unknown3 = *floatArr1;
|
||||
else
|
||||
Unknown3 = 0.25;
|
||||
auto floatArr2 = loader::query_float_attribute(groupIndex, 0, 701);
|
||||
if (floatArr2)
|
||||
GravityMult = *floatArr2;
|
||||
else
|
||||
GravityMult = 0.5;
|
||||
BallCapturedFlag = false;
|
||||
Unknown3 = loader::query_float_attribute(groupIndex, 0, 407, 0.25f);
|
||||
GravityMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
||||
GravityPull = *loader::query_float_attribute(groupIndex, 0, 305);
|
||||
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
Circle.Center.X = visual.FloatArr[0];
|
||||
Circle.Center.Y = visual.FloatArr[1];
|
||||
Circle.RadiusSq = *loader::query_float_attribute(groupIndex, 0, 306) * visual.FloatArr[2];
|
||||
if (Circle.RadiusSq == 0.0)
|
||||
if (Circle.RadiusSq == 0.0f)
|
||||
Circle.RadiusSq = 0.001f;
|
||||
|
||||
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup, reinterpret_cast<vector_type*>(visual.FloatArr),
|
||||
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
|
||||
reinterpret_cast<vector3*>(visual.FloatArr),
|
||||
Circle.RadiusSq);
|
||||
if (tCircle)
|
||||
{
|
||||
tCircle->place_in_grid();
|
||||
EdgeList->Add(tCircle);
|
||||
}
|
||||
tCircle->place_in_grid(&AABB);
|
||||
EdgeList.push_back(tCircle);
|
||||
|
||||
ZSetValue = loader::query_float_attribute(groupIndex, 0, 408)[2];
|
||||
FieldFlag = static_cast<int>(floor(*loader::query_float_attribute(groupIndex, 0, 1304)));
|
||||
CollisionMask = static_cast<int>(floor(*loader::query_float_attribute(groupIndex, 0, 1304)));
|
||||
|
||||
// CollisionMask difference: 3DPB - value given as is, FT: mask = 1 << value
|
||||
if (pb::FullTiltMode)
|
||||
CollisionMask = 1 << CollisionMask;
|
||||
|
||||
Circle.RadiusSq = visual.FloatArr[2] * visual.FloatArr[2];
|
||||
circle.RadiusSq = Circle.RadiusSq;
|
||||
circle.Center.X = Circle.Center.X;
|
||||
circle.Center.Y = Circle.Center.Y;
|
||||
circle.Center.Z = Circle.Center.Z;
|
||||
|
||||
Field.Flag2Ptr = &ActiveFlag;
|
||||
Field.ActiveFlag = &ActiveFlag;
|
||||
Field.CollisionComp = this;
|
||||
Field.Mask = visual.CollisionGroup;
|
||||
Field.CollisionGroup = visual.CollisionGroup;
|
||||
TTableLayer::edges_insert_circle(&circle, nullptr, &Field);
|
||||
}
|
||||
|
||||
int THole::Message(int code, float value)
|
||||
int THole::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024 && BallCapturedFlag)
|
||||
if (code == MessageCode::Reset && BallCapturedFlag)
|
||||
{
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
BallCapturedSecondStage = 1;
|
||||
BallCapturedSecondStage = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void THole::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void THole::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (!BallCapturedFlag)
|
||||
{
|
||||
BallCapturedSecondStage = 0;
|
||||
Threshold = 1000000000.0;
|
||||
BallCapturedFlag = 1;
|
||||
BallCapturedSecondStage = false;
|
||||
Threshold = 1e9f;
|
||||
BallCapturedFlag = true;
|
||||
ball->CollisionComp = this;
|
||||
ball->Position.X = Circle.Center.X;
|
||||
ball->Position.Y = Circle.Center.Y;
|
||||
ball->Acceleration.Z = 0.0;
|
||||
Timer = timer::set(0.5f, this, TimerExpired);
|
||||
ball->Direction.Z = 0.0;
|
||||
ball->CollisionDisabledFlag = true;
|
||||
|
||||
// Ramp hole has no delay in FT.
|
||||
auto captureTime = pb::FullTiltMode ? 0 : 0.5f;
|
||||
Timer = timer::set(captureTime, this, TimerExpired);
|
||||
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
loader::play_sound(HardHitSoundId);
|
||||
control::handler(57, this);
|
||||
loader::play_sound(HardHitSoundId, ball, "THole1");
|
||||
control::handler(MessageCode::ControlBallCaptured, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultCollision(ball, nextPosition, direction);
|
||||
}
|
||||
}
|
||||
|
||||
int THole::FieldEffect(TBall* ball, vector_type* vecDst)
|
||||
int THole::FieldEffect(TBall* ball, vector2* vecDst)
|
||||
{
|
||||
int result;
|
||||
vector_type direction{};
|
||||
vector2 direction{};
|
||||
|
||||
if (BallCapturedFlag)
|
||||
{
|
||||
if (BallCapturedSecondStage)
|
||||
{
|
||||
ball->Acceleration.Z -= PinballTable->GravityDirVectMult * ball->TimeDelta * GravityMult;
|
||||
ball->Position.Z += ball->Acceleration.Z;
|
||||
ball->Direction.Z -= PinballTable->GravityDirVectMult * ball->TimeDelta * GravityMult;
|
||||
ball->Position.Z += ball->Direction.Z;
|
||||
if (ball->Position.Z <= ZSetValue)
|
||||
{
|
||||
BallCapturedFlag = 0;
|
||||
BallCapturedSecondStage = 0;
|
||||
BallCapturedFlag = false;
|
||||
BallCapturedSecondStage = false;
|
||||
ball->Position.Z = ZSetValue;
|
||||
ball->Acceleration.Z = 0.0;
|
||||
ball->FieldFlag = FieldFlag;
|
||||
ball->Acceleration.Y = 0.0;
|
||||
ball->CollisionMask = CollisionMask;
|
||||
ball->CollisionComp = nullptr;
|
||||
ball->Acceleration.X = 0.0;
|
||||
ball->Speed = 0.0;
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
control::handler(58, this);
|
||||
ball->Direction = {};
|
||||
ball->Speed = 0.0f;
|
||||
loader::play_sound(SoftHitSoundId, ball, "THole2");
|
||||
control::handler(MessageCode::ControlBallReleased, this);
|
||||
}
|
||||
}
|
||||
result = 0;
|
||||
@@ -127,9 +127,9 @@ int THole::FieldEffect(TBall* ball, vector_type* vecDst)
|
||||
direction.Y = Circle.Center.Y - ball->Position.Y;
|
||||
if (direction.X * direction.X + direction.Y * direction.Y <= Circle.RadiusSq)
|
||||
{
|
||||
maths::normalize_2d(&direction);
|
||||
vecDst->X = direction.X * GravityPull - ball->Acceleration.X * ball->Speed;
|
||||
vecDst->Y = direction.Y * GravityPull - ball->Acceleration.Y * ball->Speed;
|
||||
maths::normalize_2d(direction);
|
||||
vecDst->X = direction.X * GravityPull - ball->Direction.X * ball->Speed;
|
||||
vecDst->Y = direction.Y * GravityPull - ball->Direction.Y * ball->Speed;
|
||||
result = 1;
|
||||
}
|
||||
else
|
||||
@@ -144,5 +144,5 @@ void THole::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto hole = static_cast<THole*>(caller);
|
||||
hole->Timer = 0;
|
||||
hole->BallCapturedSecondStage = 1;
|
||||
hole->BallCapturedSecondStage = true;
|
||||
}
|
||||
@@ -8,22 +8,22 @@ class THole :
|
||||
{
|
||||
public:
|
||||
THole(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
int FieldEffect(TBall* ball, vector_type* vecDst) override;
|
||||
int FieldEffect(TBall* ball, vector2* vecDst) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
|
||||
int BallCapturedFlag;
|
||||
int BallCapturedSecondStage;
|
||||
bool BallCapturedFlag{};
|
||||
bool BallCapturedSecondStage{};
|
||||
int Timer;
|
||||
float Unknown3;
|
||||
float Unknown4;
|
||||
float GravityMult;
|
||||
float ZSetValue;
|
||||
int FieldFlag;
|
||||
int CollisionMask;
|
||||
float GravityPull;
|
||||
circle_type Circle;
|
||||
field_effect_type Field;
|
||||
circle_type Circle{};
|
||||
field_effect_type Field{};
|
||||
};
|
||||
@@ -8,33 +8,31 @@
|
||||
#include "render.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TKickback::TKickback(TPinballTable* table, int groupIndex): TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
MessageField = 0;
|
||||
Timer = 0;
|
||||
ActiveFlag = 0;
|
||||
TimerTime = 0.69999999f;
|
||||
KickActiveFlag = 0;
|
||||
TimerTime = 0.7f;
|
||||
TimerTime2 = 0.1f;
|
||||
Threshold = 1000000000.0f;
|
||||
}
|
||||
|
||||
int TKickback::Message(int code, float value)
|
||||
int TKickback::Message(MessageCode code, float value)
|
||||
{
|
||||
if ((code == 1011 || code == 1024) && Timer)
|
||||
if ((code == MessageCode::SetTiltLock || code == MessageCode::Reset) && Timer)
|
||||
{
|
||||
timer::kill(Timer);
|
||||
if (ListBitmap)
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
SpriteSet(-1);
|
||||
Timer = 0;
|
||||
ActiveFlag = 0;
|
||||
KickActiveFlag = 0;
|
||||
Threshold = 1000000000.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TKickback::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void TKickback::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge)
|
||||
{
|
||||
if (PinballTable->TiltLockFlag)
|
||||
@@ -44,14 +42,14 @@ void TKickback::Collision(TBall* ball, vector_type* nextPosition, vector_type* d
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ActiveFlag)
|
||||
if (!KickActiveFlag)
|
||||
{
|
||||
Threshold = 1000000000.0;
|
||||
ActiveFlag = 1;
|
||||
KickActiveFlag = 1;
|
||||
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||
}
|
||||
if (DefaultCollision(ball, nextPosition, direction))
|
||||
ActiveFlag = 0;
|
||||
KickActiveFlag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,37 +57,17 @@ void TKickback::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto kick = static_cast<TKickback*>(caller);
|
||||
|
||||
if (kick->ActiveFlag)
|
||||
if (kick->KickActiveFlag)
|
||||
{
|
||||
kick->Threshold = 0.0;
|
||||
kick->Timer = timer::set(kick->TimerTime2, kick, TimerExpired);
|
||||
loader::play_sound(kick->HardHitSoundId);
|
||||
if (kick->ListBitmap)
|
||||
{
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(kick->ListBitmap->Get(1));
|
||||
auto zMap = static_cast<zmap_header_type*>(kick->ListZMap->Get(1));
|
||||
render::sprite_set(
|
||||
kick->RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - kick->PinballTable->XOffset,
|
||||
bmp->YPosition - kick->PinballTable->YOffset);
|
||||
}
|
||||
loader::play_sound(kick->HardHitSoundId, kick, "TKickback");
|
||||
kick->SpriteSet(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kick->ListBitmap)
|
||||
{
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(kick->ListBitmap->Get(0));
|
||||
auto zMap = static_cast<zmap_header_type*>(kick->ListZMap->Get(0));
|
||||
render::sprite_set(
|
||||
kick->RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - kick->PinballTable->XOffset,
|
||||
bmp->YPosition - kick->PinballTable->YOffset);
|
||||
}
|
||||
kick->SpriteSet(0);
|
||||
kick->Timer = 0;
|
||||
control::handler(60, kick);
|
||||
control::handler(MessageCode::ControlTimerExpired, kick);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ class TKickback :
|
||||
{
|
||||
public:
|
||||
TKickback(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
@@ -15,5 +15,5 @@ public:
|
||||
float TimerTime;
|
||||
float TimerTime2;
|
||||
int Timer;
|
||||
int ActiveFlag;
|
||||
int KickActiveFlag;
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "pb.h"
|
||||
#include "TBall.h"
|
||||
#include "TCircle.h"
|
||||
#include "timer.h"
|
||||
@@ -24,60 +24,60 @@ TKickout::TKickout(TPinballTable* table, int groupIndex, bool someFlag): TCollis
|
||||
TimerTime2 = 0.05f;
|
||||
MessageField = 0;
|
||||
Timer = 0;
|
||||
KickFlag1 = 0;
|
||||
BallCaputeredFlag = 0;
|
||||
FieldMult = *loader::query_float_attribute(groupIndex, 0, 305);
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
SoftHitSoundId = visual.SoftHitSoundId;
|
||||
HardHitSoundId = visual.Kicker.HardHitSoundId;
|
||||
|
||||
Circle.Center.X = *visual.FloatArr;
|
||||
Circle.Center.X = visual.FloatArr[0];
|
||||
Circle.Center.Y = visual.FloatArr[1];
|
||||
Circle.RadiusSq = *loader::query_float_attribute(groupIndex, 0, 306) * visual.FloatArr[2];
|
||||
if (Circle.RadiusSq == 0.0)
|
||||
if (Circle.RadiusSq == 0.0f)
|
||||
Circle.RadiusSq = 0.001f;
|
||||
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
|
||||
reinterpret_cast<vector_type*>(visual.FloatArr), Circle.RadiusSq);
|
||||
reinterpret_cast<vector3*>(visual.FloatArr), Circle.RadiusSq);
|
||||
if (tCircle)
|
||||
{
|
||||
tCircle->place_in_grid();
|
||||
EdgeList->Add(tCircle);
|
||||
tCircle->place_in_grid(&AABB);
|
||||
EdgeList.push_back(tCircle);
|
||||
}
|
||||
|
||||
Circle.RadiusSq = visual.FloatArr[2] * visual.FloatArr[2];
|
||||
CollisionBallSetZ = loader::query_float_attribute(groupIndex, 0, 408)[2];
|
||||
auto zAttr = loader::query_float_attribute(groupIndex, 0, 408);
|
||||
CollisionBallSetZ = pb::FullTiltMode && !pb::FullTiltDemoMode ? zAttr[3] : zAttr[2];
|
||||
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
||||
BallAcceleration = visual.Kicker.ThrowBallAcceleration;
|
||||
BallThrowDirection = visual.Kicker.ThrowBallDirection;
|
||||
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
||||
ThrowSpeedMult1 = visual.Kicker.Boost;
|
||||
|
||||
circle.RadiusSq = Circle.RadiusSq;
|
||||
circle.Center.X = Circle.Center.X;
|
||||
circle.Center.Y = Circle.Center.Y;
|
||||
circle.Center.Z = Circle.Center.Z;
|
||||
Field.Flag2Ptr = &ActiveFlag;
|
||||
Field.ActiveFlag = &ActiveFlag;
|
||||
Field.CollisionComp = this;
|
||||
Field.Mask = visual.CollisionGroup;
|
||||
Field.CollisionGroup = visual.CollisionGroup;
|
||||
TTableLayer::edges_insert_circle(&circle, nullptr, &Field);
|
||||
}
|
||||
|
||||
int TKickout::Message(int code, float value)
|
||||
int TKickout::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 55:
|
||||
if (KickFlag1)
|
||||
case MessageCode::TKickoutRestartTimer:
|
||||
if (BallCaputeredFlag)
|
||||
{
|
||||
if (value < 0.0)
|
||||
if (value < 0.0f)
|
||||
value = TimerTime1;
|
||||
Timer = timer::set(value, this, TimerExpired);
|
||||
}
|
||||
break;
|
||||
case 1011:
|
||||
case MessageCode::SetTiltLock:
|
||||
if (NotSomeFlag)
|
||||
ActiveFlag = 0;
|
||||
break;
|
||||
case 1024:
|
||||
if (KickFlag1)
|
||||
case MessageCode::Reset:
|
||||
if (BallCaputeredFlag)
|
||||
{
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
@@ -93,72 +93,69 @@ int TKickout::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TKickout::put_scoring(int index, int score)
|
||||
void TKickout::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (index < 5)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TKickout::get_scoring(int index)
|
||||
{
|
||||
return index < 5 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TKickout::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
{
|
||||
if (!KickFlag1)
|
||||
if (!BallCaputeredFlag)
|
||||
{
|
||||
Ball = ball;
|
||||
Threshold = 1000000000.0;
|
||||
KickFlag1 = 1;
|
||||
BallCaputeredFlag = 1;
|
||||
ball->CollisionComp = this;
|
||||
ball->Position.X = Circle.Center.X;
|
||||
ball->Position.Y = Circle.Center.Y;
|
||||
OriginalBallZ = ball->Position.Z;
|
||||
ball->Position.Z = CollisionBallSetZ;
|
||||
ball->Position.Z = CollisionBallSetZ;
|
||||
ball->CollisionDisabledFlag = true;
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
Message(55, 0.1f);
|
||||
Message(MessageCode::TKickoutRestartTimer, 0.1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
control::handler(63, this);
|
||||
loader::play_sound(SoftHitSoundId, ball, "TKickout1");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= distance;
|
||||
ball->not_again(edge);
|
||||
}
|
||||
}
|
||||
|
||||
int TKickout::FieldEffect(TBall* ball, vector_type* dstVec)
|
||||
int TKickout::FieldEffect(TBall* ball, vector2* dstVec)
|
||||
{
|
||||
vector_type direction{};
|
||||
vector2 direction{};
|
||||
|
||||
if (KickFlag1)
|
||||
if (BallCaputeredFlag)
|
||||
return 0;
|
||||
direction.X = Circle.Center.X - ball->Position.X;
|
||||
direction.Y = Circle.Center.Y - ball->Position.Y;
|
||||
if (direction.Y * direction.Y + direction.X * direction.X > Circle.RadiusSq)
|
||||
return 0;
|
||||
maths::normalize_2d(&direction);
|
||||
dstVec->X = direction.X * FieldMult - ball->Acceleration.X * ball->Speed;
|
||||
dstVec->Y = direction.Y * FieldMult - ball->Acceleration.Y * ball->Speed;
|
||||
maths::normalize_2d(direction);
|
||||
dstVec->X = direction.X * FieldMult - ball->Direction.X * ball->Speed;
|
||||
dstVec->Y = direction.Y * FieldMult - ball->Direction.Y * ball->Speed;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void TKickout::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto kick = static_cast<TKickout*>(caller);
|
||||
if (kick->KickFlag1)
|
||||
if (kick->BallCaputeredFlag)
|
||||
{
|
||||
kick->KickFlag1 = 0;
|
||||
kick->BallCaputeredFlag = 0;
|
||||
kick->Timer = timer::set(kick->TimerTime2, kick, ResetTimerExpired);
|
||||
if (kick->Ball)
|
||||
{
|
||||
loader::play_sound(kick->HardHitSoundId, kick->Ball, "TKickout2");
|
||||
kick->Ball->Position.Z = kick->OriginalBallZ;
|
||||
TBall::throw_ball(kick->Ball, &kick->BallAcceleration, kick->ThrowAngleMult, kick->ThrowSpeedMult1,
|
||||
kick->Ball->throw_ball(&kick->BallThrowDirection, kick->ThrowAngleMult, kick->ThrowSpeedMult1,
|
||||
kick->ThrowSpeedMult2);
|
||||
kick->ActiveFlag = 0;
|
||||
kick->Ball = nullptr;
|
||||
loader::play_sound(kick->HardHitSoundId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,30 +8,27 @@ class TKickout :
|
||||
{
|
||||
public:
|
||||
TKickout(TPinballTable* table, int groupIndex, bool someFlag);
|
||||
int Message(int code, float value) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
int FieldEffect(TBall* ball, vector_type* vecDst) override;
|
||||
int FieldEffect(TBall* ball, vector2* vecDst) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
static void ResetTimerExpired(int timerId, void* caller);
|
||||
|
||||
int KickFlag1;
|
||||
int BallCaputeredFlag;
|
||||
int NotSomeFlag;
|
||||
int Timer;
|
||||
float TimerTime1;
|
||||
float TimerTime2;
|
||||
float CollisionBallSetZ;
|
||||
TBall* Ball;
|
||||
TBall* Ball{};
|
||||
float FieldMult;
|
||||
circle_type Circle;
|
||||
float OriginalBallZ;
|
||||
vector_type BallAcceleration;
|
||||
circle_type Circle{};
|
||||
float OriginalBallZ{};
|
||||
vector3 BallThrowDirection{};
|
||||
float ThrowAngleMult;
|
||||
float ThrowSpeedMult1;
|
||||
float ThrowSpeedMult2;
|
||||
field_effect_type Field;
|
||||
int Scores[5];
|
||||
field_effect_type Field{};
|
||||
};
|
||||
@@ -7,228 +7,241 @@
|
||||
#include "render.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TLight::TLight(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, true)
|
||||
{
|
||||
Timer1 = 0;
|
||||
FlasherActive = 0;
|
||||
Timer2 = 0;
|
||||
Flasher.Timer = 0;
|
||||
TimeoutTimer = 0;
|
||||
FlasherOnFlag = false;
|
||||
UndoOverrideTimer = 0;
|
||||
FlashTimer = 0;
|
||||
Reset();
|
||||
float* floatArr1 = loader::query_float_attribute(groupIndex, 0, 900);
|
||||
Flasher.TimerDelay[0] = *floatArr1;
|
||||
FlasherDelay[0] = *floatArr1;
|
||||
FlashDelay[0] = *floatArr1;
|
||||
SourceDelay[0] = *floatArr1;
|
||||
float* floatArr2 = loader::query_float_attribute(groupIndex, 0, 901);
|
||||
Flasher.TimerDelay[1] = *floatArr2;
|
||||
FlasherDelay[1] = *floatArr2;
|
||||
FlashDelay[1] = *floatArr2;
|
||||
SourceDelay[1] = *floatArr2;
|
||||
}
|
||||
|
||||
int TLight::Message(int code, float value)
|
||||
int TLight::Message(MessageCode code, float value)
|
||||
{
|
||||
int bmpIndex;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
Reset();
|
||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||
{
|
||||
auto playerPtr = &PlayerData[index];
|
||||
playerPtr->FlasherActive = FlasherActive;
|
||||
playerPtr->BmpIndex2 = BmpIndex2;
|
||||
playerPtr->BmpIndex1 = BmpIndex1;
|
||||
playerPtr->FlasherOnFlag = FlasherOnFlag;
|
||||
playerPtr->LightOnBmpIndex = LightOnBmpIndex;
|
||||
playerPtr->LightOnFlag = LightOnFlag;
|
||||
playerPtr->MessageField = MessageField;
|
||||
}
|
||||
break;
|
||||
case 1020:
|
||||
case MessageCode::PlayerChanged:
|
||||
{
|
||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||
playerPtr->FlasherActive = FlasherActive;
|
||||
playerPtr->BmpIndex2 = BmpIndex2;
|
||||
playerPtr->BmpIndex1 = BmpIndex1;
|
||||
playerPtr->FlasherOnFlag = FlasherOnFlag;
|
||||
playerPtr->LightOnBmpIndex = LightOnBmpIndex;
|
||||
playerPtr->LightOnFlag = LightOnFlag;
|
||||
playerPtr->MessageField = MessageField;
|
||||
|
||||
Reset();
|
||||
|
||||
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
||||
FlasherActive = playerPtr->FlasherActive;
|
||||
BmpIndex2 = playerPtr->BmpIndex2;
|
||||
BmpIndex1 = playerPtr->BmpIndex1;
|
||||
FlasherOnFlag = playerPtr->FlasherOnFlag;
|
||||
LightOnBmpIndex = playerPtr->LightOnBmpIndex;
|
||||
LightOnFlag = playerPtr->LightOnFlag;
|
||||
MessageField = playerPtr->MessageField;
|
||||
if (BmpIndex2)
|
||||
if (LightOnBmpIndex)
|
||||
{
|
||||
Message(11, static_cast<float>(BmpIndex2));
|
||||
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(LightOnBmpIndex));
|
||||
}
|
||||
if (BmpIndex1)
|
||||
Message(1, 0.0);
|
||||
if (FlasherActive)
|
||||
Message(4, 0.0);
|
||||
if (LightOnFlag)
|
||||
Message(MessageCode::TLightTurnOn, 0.0);
|
||||
if (FlasherOnFlag)
|
||||
Message(MessageCode::TLightFlasherStart, 0.0);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
BmpIndex1 = 0;
|
||||
if (FlasherActive == 0 && !FlasherFlag1 && !FlasherFlag2)
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[0]);
|
||||
case MessageCode::TLightTurnOff:
|
||||
LightOnFlag = false;
|
||||
if (!FlasherOnFlag && !ToggledOffFlag && !ToggledOnFlag)
|
||||
SetSpriteBmp(BmpArr[0]);
|
||||
break;
|
||||
case 1:
|
||||
BmpIndex1 = 1;
|
||||
if (FlasherActive == 0 && !FlasherFlag1 && !FlasherFlag2)
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[1]);
|
||||
case MessageCode::TLightTurnOn:
|
||||
LightOnFlag = true;
|
||||
if (!FlasherOnFlag && !ToggledOffFlag && !ToggledOnFlag)
|
||||
SetSpriteBmp(BmpArr[1]);
|
||||
break;
|
||||
case 2:
|
||||
return BmpIndex1;
|
||||
case 3:
|
||||
return FlasherActive;
|
||||
case 4:
|
||||
case MessageCode::TLightGetLightOnFlag:
|
||||
return LightOnFlag;
|
||||
case MessageCode::TLightGetFlasherOnFlag:
|
||||
return FlasherOnFlag;
|
||||
case MessageCode::TLightFlasherStart:
|
||||
schedule_timeout(0.0);
|
||||
if (!FlasherActive || !Flasher.Timer)
|
||||
if (!FlasherOnFlag || !FlashTimer)
|
||||
{
|
||||
FlasherActive = 1;
|
||||
FlasherFlag2 = 0;
|
||||
FlasherFlag1 = 0;
|
||||
TurnOffAfterFlashingFg = 0;
|
||||
flasher_start(&Flasher, BmpIndex1);
|
||||
FlasherOnFlag = true;
|
||||
ToggledOnFlag = false;
|
||||
ToggledOffFlag = false;
|
||||
TurnOffAfterFlashingFg = false;
|
||||
flasher_start(LightOnFlag);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
Flasher.TimerDelay[0] = value * FlasherDelay[0];
|
||||
Flasher.TimerDelay[1] = value * FlasherDelay[1];
|
||||
case MessageCode::TLightApplyMultDelay:
|
||||
FlashDelay[0] = value * SourceDelay[0];
|
||||
FlashDelay[1] = value * SourceDelay[1];
|
||||
break;
|
||||
case 6:
|
||||
Flasher.TimerDelay[0] = FlasherDelay[0];
|
||||
Flasher.TimerDelay[1] = FlasherDelay[1];
|
||||
case MessageCode::TLightApplyDelay:
|
||||
FlashDelay[0] = SourceDelay[0];
|
||||
FlashDelay[1] = SourceDelay[1];
|
||||
break;
|
||||
case 7:
|
||||
if (!FlasherActive)
|
||||
flasher_start(&Flasher, BmpIndex1);
|
||||
FlasherActive = 1;
|
||||
FlasherFlag2 = 0;
|
||||
TurnOffAfterFlashingFg = 0;
|
||||
FlasherFlag1 = 0;
|
||||
case MessageCode::TLightFlasherStartTimed:
|
||||
if (!FlasherOnFlag)
|
||||
flasher_start(LightOnFlag);
|
||||
FlasherOnFlag = true;
|
||||
ToggledOnFlag = false;
|
||||
TurnOffAfterFlashingFg = false;
|
||||
ToggledOffFlag = false;
|
||||
schedule_timeout(value);
|
||||
break;
|
||||
case 8:
|
||||
if (!FlasherFlag1)
|
||||
case MessageCode::TLightTurnOffTimed:
|
||||
if (!ToggledOffFlag)
|
||||
{
|
||||
if (FlasherActive)
|
||||
if (FlasherOnFlag)
|
||||
{
|
||||
flasher_stop(&Flasher, 0);
|
||||
FlasherActive = 0;
|
||||
flasher_stop(0);
|
||||
FlasherOnFlag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[0]);
|
||||
SetSpriteBmp(BmpArr[0]);
|
||||
}
|
||||
FlasherFlag1 = 1;
|
||||
FlasherFlag2 = 0;
|
||||
ToggledOffFlag = true;
|
||||
ToggledOnFlag = false;
|
||||
}
|
||||
schedule_timeout(value);
|
||||
break;
|
||||
case 9:
|
||||
if (!FlasherFlag2)
|
||||
case MessageCode::TLightTurnOnTimed:
|
||||
if (!ToggledOnFlag)
|
||||
{
|
||||
if (FlasherActive)
|
||||
if (FlasherOnFlag)
|
||||
{
|
||||
flasher_stop(&Flasher, 1);
|
||||
FlasherActive = 0;
|
||||
flasher_stop(1);
|
||||
FlasherOnFlag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[1]);
|
||||
SetSpriteBmp(BmpArr[1]);
|
||||
}
|
||||
FlasherFlag2 = 1;
|
||||
FlasherFlag1 = 0;
|
||||
ToggledOnFlag = true;
|
||||
ToggledOffFlag = false;
|
||||
}
|
||||
schedule_timeout(value);
|
||||
break;
|
||||
case 11:
|
||||
BmpIndex2 = static_cast<int>(floor(value));
|
||||
if (BmpIndex2 > ListBitmap->Count())
|
||||
BmpIndex2 = ListBitmap->Count();
|
||||
bmpIndex = 0;
|
||||
if (BmpIndex2 < 0)
|
||||
BmpIndex2 = 0;
|
||||
Flasher.BmpArr[0] = nullptr;
|
||||
Flasher.BmpArr[1] = static_cast<gdrv_bitmap8*>(ListBitmap->Get(BmpIndex2));
|
||||
if (FlasherActive == 0)
|
||||
case MessageCode::TLightSetOnStateBmpIndex:
|
||||
LightOnBmpIndex = Clamp(static_cast<int>(floor(value)), 0, static_cast<int>(ListBitmap->size()) - 1);
|
||||
BmpArr[0] = -1;
|
||||
BmpArr[1] = LightOnBmpIndex;
|
||||
if (!FlasherOnFlag)
|
||||
{
|
||||
if (!FlasherFlag1)
|
||||
{
|
||||
if (FlasherFlag2)
|
||||
bmpIndex = 1;
|
||||
else
|
||||
bmpIndex = BmpIndex1;
|
||||
}
|
||||
if (ToggledOffFlag)
|
||||
bmpIndex = 0;
|
||||
else if (ToggledOnFlag)
|
||||
bmpIndex = 1;
|
||||
else
|
||||
bmpIndex = LightOnFlag;
|
||||
}
|
||||
else
|
||||
{
|
||||
bmpIndex = Flasher.BmpIndex;
|
||||
bmpIndex = FlashLightOnFlag;
|
||||
}
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[bmpIndex]);
|
||||
SetSpriteBmp(BmpArr[bmpIndex]);
|
||||
break;
|
||||
case 12:
|
||||
bmpIndex = BmpIndex2 + 1;
|
||||
if (bmpIndex > ListBitmap->Count())
|
||||
bmpIndex = ListBitmap->Count();
|
||||
Message(11, static_cast<float>(bmpIndex));
|
||||
case MessageCode::TLightIncOnStateBmpIndex:
|
||||
bmpIndex = LightOnBmpIndex + 1;
|
||||
if (bmpIndex >= static_cast<int>(ListBitmap->size()))
|
||||
bmpIndex = static_cast<int>(ListBitmap->size()) - 1;
|
||||
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(bmpIndex));
|
||||
break;
|
||||
case 13:
|
||||
bmpIndex = BmpIndex2 - 1;
|
||||
case MessageCode::TLightDecOnStateBmpIndex:
|
||||
bmpIndex = LightOnBmpIndex - 1;
|
||||
if (bmpIndex < 0)
|
||||
bmpIndex = 0;
|
||||
Message(11, static_cast<float>(bmpIndex));
|
||||
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(bmpIndex));
|
||||
break;
|
||||
case 14:
|
||||
if (Timer1)
|
||||
timer::kill(Timer1);
|
||||
Timer1 = 0;
|
||||
if (FlasherActive != 0)
|
||||
flasher_stop(&Flasher, -1);
|
||||
FlasherActive = 0;
|
||||
FlasherFlag1 = 0;
|
||||
FlasherFlag2 = 0;
|
||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[BmpIndex1]);
|
||||
case MessageCode::TLightResetTimed:
|
||||
if (TimeoutTimer)
|
||||
timer::kill(TimeoutTimer);
|
||||
TimeoutTimer = 0;
|
||||
if (FlasherOnFlag)
|
||||
flasher_stop(-1);
|
||||
FlasherOnFlag = false;
|
||||
ToggledOffFlag = false;
|
||||
ToggledOnFlag = false;
|
||||
SetSpriteBmp(BmpArr[LightOnFlag]);
|
||||
break;
|
||||
case 15:
|
||||
TurnOffAfterFlashingFg = 0;
|
||||
if (Timer2)
|
||||
timer::kill(Timer2);
|
||||
Timer2 = 0;
|
||||
Message(1, 0.0);
|
||||
Message(7, value);
|
||||
case MessageCode::TLightFlasherStartTimedThenStayOn:
|
||||
TurnOffAfterFlashingFg = false;
|
||||
if (UndoOverrideTimer)
|
||||
timer::kill(UndoOverrideTimer);
|
||||
UndoOverrideTimer = 0;
|
||||
Message(MessageCode::TLightTurnOn, 0.0);
|
||||
Message(MessageCode::TLightFlasherStartTimed, value);
|
||||
break;
|
||||
case 16:
|
||||
if (Timer2)
|
||||
timer::kill(Timer2);
|
||||
Timer2 = 0;
|
||||
Message(7, value);
|
||||
TurnOffAfterFlashingFg = 1;
|
||||
case MessageCode::TLightFlasherStartTimedThenStayOff:
|
||||
if (UndoOverrideTimer)
|
||||
timer::kill(UndoOverrideTimer);
|
||||
UndoOverrideTimer = 0;
|
||||
Message(MessageCode::TLightFlasherStartTimed, value);
|
||||
TurnOffAfterFlashingFg = true;
|
||||
break;
|
||||
case 17:
|
||||
Message(static_cast<int>(floor(value)) != 0, 0.0);
|
||||
return BmpIndex1;
|
||||
case 18:
|
||||
Message(17, value);
|
||||
Message(14, 0.0);
|
||||
return BmpIndex1;
|
||||
case 19:
|
||||
Message(1, 0.0);
|
||||
Message(14, 0.0);
|
||||
case MessageCode::TLightToggleValue:
|
||||
Message(static_cast<int>(floor(value)) ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||
return LightOnFlag;
|
||||
case MessageCode::TLightResetAndToggleValue:
|
||||
Message(MessageCode::TLightToggleValue, value);
|
||||
Message(MessageCode::TLightResetTimed, 0.0);
|
||||
return LightOnFlag;
|
||||
case MessageCode::TLightResetAndTurnOn:
|
||||
Message(MessageCode::TLightTurnOn, 0.0);
|
||||
Message(MessageCode::TLightResetTimed, 0.0);
|
||||
break;
|
||||
case 20:
|
||||
Message(0, 0.0);
|
||||
Message(14, 0.0);
|
||||
case MessageCode::TLightResetAndTurnOff:
|
||||
Message(MessageCode::TLightTurnOff, 0.0);
|
||||
Message(MessageCode::TLightResetTimed, 0.0);
|
||||
break;
|
||||
case 21:
|
||||
Message(17, static_cast<float>(BmpIndex1 == 0));
|
||||
return BmpIndex1;
|
||||
case 22:
|
||||
Message(18, static_cast<float>(BmpIndex1 == 0));
|
||||
return BmpIndex1;
|
||||
case 23:
|
||||
case MessageCode::TLightToggle:
|
||||
Message(MessageCode::TLightToggleValue, !LightOnFlag);
|
||||
return LightOnFlag;
|
||||
case MessageCode::TLightResetAndToggle:
|
||||
Message(MessageCode::TLightResetAndToggleValue, !LightOnFlag);
|
||||
return LightOnFlag;
|
||||
case MessageCode::TLightSetMessageField:
|
||||
MessageField = static_cast<int>(floor(value));
|
||||
break;
|
||||
case MessageCode::TLightFtTmpOverrideOn:
|
||||
case MessageCode::TLightFtTmpOverrideOff:
|
||||
// FT codes in negative to avoid overlap with 3DPB TLightGroup codes
|
||||
SpriteSet(BmpArr[code == MessageCode::TLightFtTmpOverrideOn]);
|
||||
if (UndoOverrideTimer)
|
||||
timer::kill(UndoOverrideTimer);
|
||||
UndoOverrideTimer = 0;
|
||||
if (value > 0)
|
||||
{
|
||||
TemporaryOverrideFlag = true;
|
||||
UndoOverrideTimer = timer::set(value, this, UndoTmpOverride);
|
||||
}
|
||||
break;
|
||||
case MessageCode::TLightFtResetOverride:
|
||||
if (UndoOverrideTimer)
|
||||
timer::kill(UndoOverrideTimer);
|
||||
UndoOverrideTimer = 0;
|
||||
TemporaryOverrideFlag = false;
|
||||
SpriteSet(PreviousBitmap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -238,85 +251,99 @@ int TLight::Message(int code, float value)
|
||||
|
||||
void TLight::Reset()
|
||||
{
|
||||
if (Timer1)
|
||||
timer::kill(Timer1);
|
||||
if (Timer2)
|
||||
timer::kill(Timer2);
|
||||
if (FlasherActive)
|
||||
flasher_stop(&Flasher, -1);
|
||||
Unknown20F = 1.0;
|
||||
Timer1 = 0;
|
||||
Timer2 = 0;
|
||||
BmpIndex1 = 0;
|
||||
BmpIndex2 = 0;
|
||||
FlasherFlag1 = 0;
|
||||
FlasherFlag2 = 0;
|
||||
FlasherActive = 0;
|
||||
TurnOffAfterFlashingFg = 0;
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
Flasher.Sprite = RenderSprite;
|
||||
Flasher.BmpArr[0] = nullptr;
|
||||
if (ListBitmap)
|
||||
Flasher.BmpArr[1] = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
Flasher.Unknown4 = 0;
|
||||
Flasher.Unknown3 = 0;
|
||||
if (TimeoutTimer)
|
||||
timer::kill(TimeoutTimer);
|
||||
if (UndoOverrideTimer)
|
||||
timer::kill(UndoOverrideTimer);
|
||||
if (FlasherOnFlag)
|
||||
flasher_stop(-1);
|
||||
TimeoutTimer = 0;
|
||||
UndoOverrideTimer = 0;
|
||||
LightOnFlag = false;
|
||||
LightOnBmpIndex = 0;
|
||||
ToggledOffFlag = false;
|
||||
ToggledOnFlag = false;
|
||||
FlasherOnFlag = false;
|
||||
TemporaryOverrideFlag = false;
|
||||
TurnOffAfterFlashingFg = false;
|
||||
PreviousBitmap = -1;
|
||||
BmpArr[0] = -1;
|
||||
BmpArr[1] = 0;
|
||||
SetSpriteBmp(BmpArr[0]);
|
||||
MessageField = 0;
|
||||
}
|
||||
|
||||
void TLight::schedule_timeout(float time)
|
||||
{
|
||||
Flasher.TimerDelay[0] = FlasherDelay[0];
|
||||
Flasher.TimerDelay[1] = FlasherDelay[1];
|
||||
if (Timer1)
|
||||
timer::kill(Timer1);
|
||||
Timer1 = 0;
|
||||
if (time > 0.0)
|
||||
Timer1 = timer::set(time, this, TimerExpired);
|
||||
FlashDelay[0] = SourceDelay[0];
|
||||
FlashDelay[1] = SourceDelay[1];
|
||||
if (TimeoutTimer)
|
||||
timer::kill(TimeoutTimer);
|
||||
TimeoutTimer = 0;
|
||||
if (time > 0.0f)
|
||||
TimeoutTimer = timer::set(time, this, TimerExpired);
|
||||
}
|
||||
|
||||
void TLight::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto light = static_cast<TLight*>(caller);
|
||||
if (light->FlasherActive)
|
||||
flasher_stop(&light->Flasher, -1);
|
||||
render::sprite_set_bitmap(light->RenderSprite, light->Flasher.BmpArr[light->BmpIndex1]);
|
||||
light->FlasherFlag1 = 0;
|
||||
light->FlasherFlag2 = 0;
|
||||
light->FlasherActive = 0;
|
||||
light->Timer1 = 0;
|
||||
if (light->TurnOffAfterFlashingFg != 0)
|
||||
if (light->FlasherOnFlag)
|
||||
light->flasher_stop(-1);
|
||||
light->SetSpriteBmp(light->BmpArr[light->LightOnFlag]);
|
||||
light->ToggledOffFlag = false;
|
||||
light->ToggledOnFlag = false;
|
||||
light->FlasherOnFlag = false;
|
||||
light->TimeoutTimer = 0;
|
||||
if (light->TurnOffAfterFlashingFg)
|
||||
{
|
||||
light->TurnOffAfterFlashingFg = 0;
|
||||
light->Message(20, 0.0);
|
||||
light->TurnOffAfterFlashingFg = false;
|
||||
light->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||
}
|
||||
if (light->Control)
|
||||
control::handler(60, light);
|
||||
control::handler(MessageCode::ControlTimerExpired, light);
|
||||
}
|
||||
|
||||
|
||||
void TLight::flasher_stop(flasher_type* flash, int bmpIndex)
|
||||
void TLight::flasher_stop(int bmpIndex)
|
||||
{
|
||||
if (flash->Timer)
|
||||
timer::kill(flash->Timer);
|
||||
flash->Timer = 0;
|
||||
if (FlashTimer)
|
||||
timer::kill(FlashTimer);
|
||||
FlashTimer = 0;
|
||||
if (bmpIndex >= 0)
|
||||
{
|
||||
flash->BmpIndex = bmpIndex;
|
||||
render::sprite_set_bitmap(flash->Sprite, flash->BmpArr[bmpIndex]);
|
||||
FlashLightOnFlag = bmpIndex;
|
||||
SetSpriteBmp(BmpArr[FlashLightOnFlag]);
|
||||
}
|
||||
}
|
||||
|
||||
void TLight::flasher_start(flasher_type* flash, int bmpIndex)
|
||||
void TLight::flasher_start(bool bmpIndex)
|
||||
{
|
||||
flash->BmpIndex = bmpIndex;
|
||||
flasher_callback(0, flash);
|
||||
FlashLightOnFlag = bmpIndex;
|
||||
flasher_callback(0, this);
|
||||
}
|
||||
|
||||
void TLight::SetSpriteBmp(int index)
|
||||
{
|
||||
PreviousBitmap = index;
|
||||
if (!TemporaryOverrideFlag)
|
||||
SpriteSet(index);
|
||||
}
|
||||
|
||||
void TLight::flasher_callback(int timerId, void* caller)
|
||||
{
|
||||
auto flash = static_cast<flasher_type*>(caller);
|
||||
auto index = 1 - flash->BmpIndex;
|
||||
flash->BmpIndex = index;
|
||||
render::sprite_set_bitmap(flash->Sprite, flash->BmpArr[index]);
|
||||
flash->Timer = timer::set(flash->TimerDelay[flash->BmpIndex], flash, flasher_callback);
|
||||
auto light = static_cast<TLight*>(caller);
|
||||
light->FlashLightOnFlag ^= true;
|
||||
light->SetSpriteBmp(light->BmpArr[light->FlashLightOnFlag]);
|
||||
light->FlashTimer = timer::set(light->FlashDelay[light->FlashLightOnFlag], light, flasher_callback);
|
||||
}
|
||||
|
||||
void TLight::UndoTmpOverride(int timerId, void* caller)
|
||||
{
|
||||
auto light = static_cast<TLight*>(caller);
|
||||
light->Message(MessageCode::TLightFtResetOverride, 0.0f);
|
||||
}
|
||||
|
||||
bool TLight::light_on() const
|
||||
{
|
||||
return LightOnFlag || ToggledOnFlag || FlasherOnFlag;
|
||||
}
|
||||
@@ -1,28 +1,12 @@
|
||||
#pragma once
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
struct gdrv_bitmap8;
|
||||
|
||||
struct flasher_type
|
||||
{
|
||||
render_sprite_type_struct* Sprite;
|
||||
gdrv_bitmap8* BmpArr[2];
|
||||
int Unknown3;
|
||||
int Unknown4;
|
||||
float TimerDelay[2];
|
||||
int Timer;
|
||||
int BmpIndex;
|
||||
};
|
||||
|
||||
|
||||
struct TLight_player_backup
|
||||
{
|
||||
int MessageField;
|
||||
int BmpIndex1;
|
||||
int FlasherActive;
|
||||
int Unknown3;
|
||||
int Unknown4;
|
||||
int BmpIndex2;
|
||||
bool LightOnFlag;
|
||||
int LightOnBmpIndex;
|
||||
bool FlasherOnFlag;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,26 +15,32 @@ class TLight :
|
||||
{
|
||||
public:
|
||||
TLight(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Reset();
|
||||
void schedule_timeout(float time);
|
||||
void flasher_stop(int bmpIndex);
|
||||
void flasher_start(bool bmpIndex);
|
||||
void SetSpriteBmp(int index);
|
||||
bool light_on() const;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
static void flasher_stop(flasher_type* flash, int bmpIndex);
|
||||
static void flasher_start(struct flasher_type* flash, int bmpIndex);
|
||||
static void flasher_callback(int timerId, void* caller);
|
||||
static void UndoTmpOverride(int timerId, void* caller);
|
||||
|
||||
flasher_type Flasher;
|
||||
int BmpIndex1;
|
||||
int FlasherActive;
|
||||
int FlasherFlag1;
|
||||
int FlasherFlag2;
|
||||
int TurnOffAfterFlashingFg;
|
||||
int BmpIndex2;
|
||||
float FlasherDelay[2];
|
||||
int Timer1;
|
||||
int Timer2;
|
||||
int Unknown19;
|
||||
float Unknown20F;
|
||||
TLight_player_backup PlayerData[4];
|
||||
int BmpArr[2]{-1};
|
||||
float FlashDelay[2]{};
|
||||
int FlashTimer;
|
||||
bool FlashLightOnFlag{};
|
||||
bool LightOnFlag{};
|
||||
bool FlasherOnFlag;
|
||||
bool ToggledOffFlag{};
|
||||
bool ToggledOnFlag{};
|
||||
bool TurnOffAfterFlashingFg{};
|
||||
int LightOnBmpIndex{};
|
||||
float SourceDelay[2]{};
|
||||
int TimeoutTimer;
|
||||
int UndoOverrideTimer;
|
||||
bool TemporaryOverrideFlag{};
|
||||
int PreviousBitmap = -1;
|
||||
TLight_player_backup PlayerData[4]{};
|
||||
};
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "objlist_class.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
|
||||
@@ -18,11 +16,11 @@ TLightBargraph::TLightBargraph(TPinballTable* table, int groupIndex) : TLightGro
|
||||
float* floatArr = loader::query_float_attribute(groupIndex, 0, 904);
|
||||
if (floatArr)
|
||||
{
|
||||
int count = 2 * List->Count();
|
||||
TimerTimeArray = reinterpret_cast<float*>(memory::allocate(count * sizeof(float)));
|
||||
auto count = 2 * List.size();
|
||||
TimerTimeArray = new float[count];
|
||||
if (TimerTimeArray)
|
||||
{
|
||||
for (int i = 0; i < count; ++floatArr)
|
||||
for (auto i = 0u; i < count; ++floatArr)
|
||||
TimerTimeArray[i++] = *floatArr;
|
||||
}
|
||||
}
|
||||
@@ -31,17 +29,16 @@ TLightBargraph::TLightBargraph(TPinballTable* table, int groupIndex) : TLightGro
|
||||
|
||||
TLightBargraph::~TLightBargraph()
|
||||
{
|
||||
if (TimerTimeArray)
|
||||
memory::free(TimerTimeArray);
|
||||
delete[] TimerTimeArray;
|
||||
}
|
||||
|
||||
int TLightBargraph::Message(int code, float value)
|
||||
int TLightBargraph::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 37:
|
||||
case MessageCode::TLightGroupGetOnCount:
|
||||
return TimeIndex;
|
||||
case 45:
|
||||
case MessageCode::TLightGroupToggleSplitIndex:
|
||||
{
|
||||
if (TimerBargraph)
|
||||
{
|
||||
@@ -49,30 +46,29 @@ int TLightBargraph::Message(int code, float value)
|
||||
TimerBargraph = 0;
|
||||
}
|
||||
auto timeIndex = static_cast<int>(floor(value));
|
||||
auto maxCount = 2 * List->Count();
|
||||
auto maxCount = static_cast<int>(List.size()) * 2;
|
||||
if (timeIndex >= maxCount)
|
||||
timeIndex = maxCount - 1;
|
||||
if (timeIndex >= 0)
|
||||
{
|
||||
TLightGroup::Message(45, static_cast<float>(timeIndex / 2));
|
||||
TLightGroup::Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(timeIndex / 2));
|
||||
if (!(timeIndex & 1))
|
||||
TLightGroup::Message(46, 0.0);
|
||||
float* timeArray = TimerTimeArray;
|
||||
if (timeArray)
|
||||
TimerBargraph = timer::set(timeArray[timeIndex], this, BargraphTimerExpired);
|
||||
TLightGroup::Message(MessageCode::TLightGroupStartFlasher, 0.0);
|
||||
if (TimerTimeArray)
|
||||
TimerBargraph = timer::set(TimerTimeArray[timeIndex], this, BargraphTimerExpired);
|
||||
TimeIndex = timeIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLightGroup::Message(20, 0.0);
|
||||
TLightGroup::Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||
TimeIndex = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1011:
|
||||
case MessageCode::SetTiltLock:
|
||||
Reset();
|
||||
break;
|
||||
case 1020:
|
||||
case MessageCode::PlayerChanged:
|
||||
if (TimerBargraph)
|
||||
{
|
||||
timer::kill(TimerBargraph);
|
||||
@@ -83,10 +79,10 @@ int TLightBargraph::Message(int code, float value)
|
||||
TimeIndex = PlayerTimerIndexBackup[static_cast<int>(floor(value))];
|
||||
if (TimeIndex)
|
||||
{
|
||||
TLightBargraph::Message(45, static_cast<float>(TimeIndex));
|
||||
TLightBargraph::Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(TimeIndex));
|
||||
}
|
||||
break;
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
{
|
||||
Reset();
|
||||
int* playerPtr = PlayerTimerIndexBackup;
|
||||
@@ -96,7 +92,7 @@ int TLightBargraph::Message(int code, float value)
|
||||
|
||||
++playerPtr;
|
||||
}
|
||||
TLightGroup::Message(1024, value);
|
||||
TLightGroup::Message(MessageCode::Reset, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -123,12 +119,12 @@ void TLightBargraph::BargraphTimerExpired(int timerId, void* caller)
|
||||
bar->TimerBargraph = 0;
|
||||
if (bar->TimeIndex)
|
||||
{
|
||||
bar->Message(45, static_cast<float>(bar->TimeIndex - 1));
|
||||
control::handler(60, bar);
|
||||
bar->Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(bar->TimeIndex - 1));
|
||||
control::handler(MessageCode::ControlTimerExpired, bar);
|
||||
}
|
||||
else
|
||||
{
|
||||
bar->Message(20, 0.0);
|
||||
control::handler(47, bar);
|
||||
bar->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||
control::handler(MessageCode::TLightGroupCountdownEnded, bar);
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ class TLightBargraph :
|
||||
public:
|
||||
TLightBargraph(TPinballTable* table, int groupIndex);
|
||||
~TLightBargraph() override;
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Reset() override;
|
||||
|
||||
static void BargraphTimerExpired(int timerId, void* caller);
|
||||
|
||||
float* TimerTimeArray;
|
||||
int TimerBargraph;
|
||||
int TimeIndex;
|
||||
int PlayerTimerIndexBackup[4];
|
||||
int TimerBargraph{};
|
||||
int TimeIndex{};
|
||||
int PlayerTimerIndexBackup[4]{};
|
||||
};
|
||||
@@ -4,45 +4,39 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "timer.h"
|
||||
#include "TLight.h"
|
||||
#include "TPinballTable.h"
|
||||
|
||||
TLightGroup::TLightGroup(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, false)
|
||||
{
|
||||
List = new objlist_class(4, 4);
|
||||
Timer = 0;
|
||||
NotifyTimer = 0;
|
||||
Reset();
|
||||
TLightGroup::Reset();
|
||||
if (groupIndex > 0)
|
||||
{
|
||||
int count;
|
||||
Timer1TimeDefault = *loader::query_float_attribute(groupIndex, 0, 903);
|
||||
__int16* groupIndArr = loader::query_iattribute(groupIndex, 1027, &count);
|
||||
int16_t* groupIndArr = loader::query_iattribute(groupIndex, 1027, &count);
|
||||
for (int index = 0; index < count; ++groupIndArr)
|
||||
{
|
||||
auto comp = table->find_component(*groupIndArr);
|
||||
auto comp = dynamic_cast<TLight*>(table->find_component(*groupIndArr));
|
||||
if (comp)
|
||||
List->Add(comp);
|
||||
List.push_back(comp);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TLightGroup::~TLightGroup()
|
||||
{
|
||||
delete List;
|
||||
}
|
||||
|
||||
int TLightGroup::Message(int code, float value)
|
||||
int TLightGroup::Message(MessageCode code, float value)
|
||||
{
|
||||
auto const count = static_cast<int>(List.size());
|
||||
switch (code)
|
||||
{
|
||||
case 1011:
|
||||
case 1022:
|
||||
case MessageCode::SetTiltLock:
|
||||
case MessageCode::GameOver:
|
||||
break;
|
||||
case 1020:
|
||||
case MessageCode::PlayerChanged:
|
||||
{
|
||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||
playerPtr->MessageField = MessageField;
|
||||
@@ -59,7 +53,7 @@ int TLightGroup::Message(int code, float value)
|
||||
TimerExpired(0, this);
|
||||
break;
|
||||
}
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
Reset();
|
||||
for (auto index = 0; index < PinballTable->PlayerCount; index++)
|
||||
{
|
||||
@@ -69,353 +63,325 @@ int TLightGroup::Message(int code, float value)
|
||||
playerPtr->Timer1Time = Timer1Time;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
case MessageCode::TLightGroupStepBackward:
|
||||
{
|
||||
auto count = List->Count();
|
||||
auto lastLight = static_cast<TLight*>(List->Get(count - 1));
|
||||
if (lastLight->FlasherActive || lastLight->FlasherFlag2 || lastLight->FlasherFlag1)
|
||||
auto lastLight = List.at(count - 1);
|
||||
if (lastLight->FlasherOnFlag || lastLight->ToggledOnFlag || lastLight->ToggledOffFlag)
|
||||
break;
|
||||
if (MessageField2)
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
{
|
||||
TLightGroup::Message(34, 0.0);
|
||||
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||
}
|
||||
AnimationFlag = 1;
|
||||
MessageField2 = code;
|
||||
auto lightMessageField = lastLight->MessageField;
|
||||
auto bmpIndex1 = lastLight->BmpIndex1;
|
||||
auto lastMessage = lastLight->MessageField;
|
||||
auto lastStatus = lastLight->LightOnFlag;
|
||||
for (auto index = count - 1; index > 0; --index)
|
||||
{
|
||||
auto lightCur = static_cast<TLight*>(List->Get(index));
|
||||
auto lightPrev = static_cast<TLight*>(List->Get(index - 1));
|
||||
lightCur->Message(lightPrev->BmpIndex1 != 0, 0.0);
|
||||
auto lightCur = List.at(index);
|
||||
auto lightPrev = List.at(index - 1);
|
||||
lightCur->Message(lightPrev->LightOnFlag ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||
lightCur->MessageField = lightPrev->MessageField;
|
||||
}
|
||||
auto firstLight = static_cast<TLight*>(List->Get(0));
|
||||
firstLight->Message(bmpIndex1 != 0, 0.0);
|
||||
firstLight->MessageField = lightMessageField;
|
||||
auto firstLight = List.at(0);
|
||||
firstLight->Message(lastStatus ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||
firstLight->MessageField = lastMessage;
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 25:
|
||||
case MessageCode::TLightGroupStepForward:
|
||||
{
|
||||
auto count = List->Count();
|
||||
auto lastLight = static_cast<TLight*>(List->Get(count - 1));
|
||||
if (lastLight->FlasherActive || lastLight->FlasherFlag2 || lastLight->FlasherFlag1)
|
||||
auto lastLight = List.at(count - 1);
|
||||
if (lastLight->FlasherOnFlag || lastLight->ToggledOnFlag || lastLight->ToggledOffFlag)
|
||||
break;
|
||||
if (MessageField2)
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
{
|
||||
TLightGroup::Message(34, 0.0);
|
||||
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||
}
|
||||
auto firstLight = static_cast<TLight*>(List->Get(0));
|
||||
auto firstLight = List.at(0);
|
||||
AnimationFlag = 1;
|
||||
MessageField2 = code;
|
||||
auto lightMessageField = firstLight->MessageField;
|
||||
auto bmpIndex1 = firstLight->BmpIndex1;
|
||||
auto firstMessage = firstLight->MessageField;
|
||||
auto firstStatus = firstLight->LightOnFlag;
|
||||
for (auto index = 0; index < count - 1; index++)
|
||||
{
|
||||
auto lightCur = static_cast<TLight*>(List->Get(index));
|
||||
auto lightNext = static_cast<TLight*>(List->Get(index + 1));
|
||||
lightCur->Message(lightNext->BmpIndex1 != 0, 0.0);
|
||||
auto lightCur = List.at(index);
|
||||
auto lightNext = List.at(index + 1);
|
||||
lightCur->Message(lightNext->LightOnFlag ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||
lightCur->MessageField = lightNext->MessageField;
|
||||
}
|
||||
lastLight->Message(bmpIndex1 != 0, 0.0);
|
||||
lastLight->MessageField = lightMessageField;
|
||||
lastLight->Message(firstStatus ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||
lastLight->MessageField = firstMessage;
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 26:
|
||||
case MessageCode::TLightGroupAnimationBackward:
|
||||
{
|
||||
if (AnimationFlag || !MessageField2)
|
||||
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
MessageField2 = code;
|
||||
AnimationFlag = 0;
|
||||
auto count = List->Count();
|
||||
auto lastLight = static_cast<TLight*>(List->Get(count - 1));
|
||||
auto flasherFlag2 = lastLight->FlasherFlag2;
|
||||
auto lastLight = List.at(count - 1);
|
||||
auto lastStatus = lastLight->ToggledOnFlag;
|
||||
for (auto i = count - 1; i > 0; --i)
|
||||
{
|
||||
auto lightCur = static_cast<TLight*>(List->Get(i));
|
||||
auto lightPrev = static_cast<TLight*>(List->Get(i - 1));
|
||||
lightCur->Message((lightPrev->FlasherFlag2 != 0) + 8, 0.0);
|
||||
auto lightCur = List.at(i);
|
||||
auto lightPrev = List.at(i - 1);
|
||||
lightCur->Message(lightPrev->ToggledOnFlag ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0.0);
|
||||
}
|
||||
auto firstLight = static_cast<TLight*>(List->Get(0));
|
||||
firstLight->Message((flasherFlag2 != 0) + 8, 0);
|
||||
auto firstLight = List.at(0);
|
||||
firstLight->Message(lastStatus ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0);
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 27:
|
||||
case MessageCode::TLightGroupAnimationForward:
|
||||
{
|
||||
if (AnimationFlag || !MessageField2)
|
||||
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
MessageField2 = code;
|
||||
AnimationFlag = 0;
|
||||
auto count = List->Count();
|
||||
auto firstLight = static_cast<TLight*>(List->Get(0));
|
||||
auto flasherFlag2 = firstLight->FlasherFlag2;
|
||||
auto firstLight = List.at(0);
|
||||
auto firstStatus = firstLight->ToggledOnFlag;
|
||||
for (auto i = 0; i < count - 1; i++)
|
||||
{
|
||||
auto lightCur = static_cast<TLight*>(List->Get(i));
|
||||
auto lightNext = static_cast<TLight*>(List->Get(i + 1));
|
||||
lightCur->Message((lightNext->FlasherFlag2 != 0) + 8, 0.0);
|
||||
auto lightCur = List.at(i);
|
||||
auto lightNext = List.at(i + 1);
|
||||
lightCur->Message(lightNext->ToggledOnFlag ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0.0);
|
||||
}
|
||||
auto lastLight = static_cast<TLight*>(List->Get(count - 1));
|
||||
lastLight->Message((flasherFlag2 != 0) + 8, 0);
|
||||
auto lastLight = List.at(count - 1);
|
||||
lastLight->Message(firstStatus ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0);
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 28:
|
||||
case MessageCode::TLightGroupLightShowAnimation:
|
||||
{
|
||||
if (AnimationFlag || !MessageField2)
|
||||
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
MessageField2 = code;
|
||||
AnimationFlag = 0;
|
||||
auto count = List->Count();
|
||||
for (auto i = 0; i < count - 1; i++)
|
||||
for (auto light : List)
|
||||
{
|
||||
if (rand() % 100 > 70)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
auto randVal = static_cast<float>(rand()) * 0.00003051850947599719f * value * 3.0f + 0.1f;
|
||||
light->Message(9, randVal);
|
||||
auto randVal = RandFloat() * value * 3.0f + 0.1f;
|
||||
light->Message(MessageCode::TLightTurnOnTimed, randVal);
|
||||
}
|
||||
}
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 29:
|
||||
case MessageCode::TLightGroupGameOverAnimation:
|
||||
{
|
||||
if (AnimationFlag || !MessageField2)
|
||||
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
MessageField2 = code;
|
||||
AnimationFlag = 0;
|
||||
auto count = List->Count();
|
||||
for (auto i = 0; i < count - 1; i++)
|
||||
for (auto light : List)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
auto randVal = static_cast<float>(rand() % 100 > 70);
|
||||
light->Message(18, randVal);
|
||||
light->Message(MessageCode::TLightResetAndToggleValue, randVal);
|
||||
}
|
||||
reschedule_animation(value);
|
||||
break;
|
||||
}
|
||||
case 30:
|
||||
case MessageCode::TLightGroupRandomAnimationSaturation:
|
||||
{
|
||||
auto noBmpInd1Count = 0;
|
||||
auto countSub1 = List->Count() - 1;
|
||||
if (countSub1 < 0)
|
||||
break;
|
||||
|
||||
for (auto i = countSub1; i >= 0; i--)
|
||||
for (auto light : List)
|
||||
{
|
||||
if (!static_cast<TLight*>(List->Get(i))->BmpIndex1)
|
||||
if (!light->LightOnFlag)
|
||||
++noBmpInd1Count;
|
||||
}
|
||||
if (!noBmpInd1Count)
|
||||
break;
|
||||
|
||||
auto randModCount = rand() % noBmpInd1Count;
|
||||
for (auto i = countSub1; i >= 0; i--)
|
||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
if (!light->BmpIndex1 && randModCount-- == 0)
|
||||
auto light = *it;
|
||||
if (!light->LightOnFlag && randModCount-- == 0)
|
||||
{
|
||||
light->Message(1, 0.0);
|
||||
light->Message(MessageCode::TLightTurnOn, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (MessageField2)
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
break;
|
||||
}
|
||||
case 31:
|
||||
case MessageCode::TLightGroupRandomAnimationDesaturation:
|
||||
{
|
||||
auto bmpInd1Count = 0;
|
||||
auto countSub1 = List->Count() - 1;
|
||||
if (countSub1 < 0)
|
||||
break;
|
||||
|
||||
for (auto i = countSub1; i >= 0; i--)
|
||||
for (auto light : List)
|
||||
{
|
||||
if (static_cast<TLight*>(List->Get(i))->BmpIndex1)
|
||||
if (light->LightOnFlag)
|
||||
++bmpInd1Count;
|
||||
}
|
||||
if (!bmpInd1Count)
|
||||
break;
|
||||
|
||||
auto randModCount = rand() % bmpInd1Count;
|
||||
for (auto i = countSub1; i >= 0; i--)
|
||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
if (light->BmpIndex1 && randModCount-- == 0)
|
||||
auto light = *it;
|
||||
if (light->LightOnFlag && randModCount-- == 0)
|
||||
{
|
||||
light->Message(0, 0.0);
|
||||
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (MessageField2)
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
break;
|
||||
}
|
||||
case 32:
|
||||
case MessageCode::TLightGroupOffsetAnimationForward:
|
||||
{
|
||||
auto index = next_light_up();
|
||||
if (index < 0)
|
||||
break;
|
||||
static_cast<TLight*>(List->Get(index))->Message(1, 0.0);
|
||||
if (MessageField2)
|
||||
List.at(index)->Message(MessageCode::TLightTurnOn, 0.0);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
return 1;
|
||||
}
|
||||
case 33:
|
||||
case MessageCode::TLightGroupOffsetAnimationBackward:
|
||||
{
|
||||
auto index = next_light_down();
|
||||
if (index < 0)
|
||||
break;
|
||||
static_cast<TLight*>(List->Get(index))->Message(0, 0.0);
|
||||
if (MessageField2)
|
||||
List.at(index)->Message(MessageCode::TLightTurnOff, 0.0);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
return 1;
|
||||
}
|
||||
case 34:
|
||||
case MessageCode::TLightGroupReset:
|
||||
{
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
if (MessageField2 == 26 || MessageField2 == 27 || MessageField2 == 28)
|
||||
TLightGroup::Message(14, 0.0);
|
||||
MessageField2 = 0;
|
||||
if (MessageField2 == MessageCode::TLightGroupAnimationBackward ||
|
||||
MessageField2 == MessageCode::TLightGroupAnimationForward || MessageField2 == MessageCode::TLightGroupLightShowAnimation)
|
||||
TLightGroup::Message(MessageCode::TLightResetTimed, 0.0);
|
||||
MessageField2 = MessageCode::TLightGroupNull;
|
||||
AnimationFlag = 0;
|
||||
break;
|
||||
}
|
||||
case 35:
|
||||
case MessageCode::TLightGroupTurnOnAtIndex:
|
||||
{
|
||||
auto index = static_cast<int>(floor(value));
|
||||
if (index >= List->Count() || index < 0)
|
||||
if (index >= count || index < 0)
|
||||
break;
|
||||
|
||||
auto light = static_cast<TLight*>(List->Get(index));
|
||||
light->Message(1, 0.0);
|
||||
if (MessageField2)
|
||||
auto light = List.at(index);
|
||||
light->Message(MessageCode::TLightTurnOn, 0.0);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
break;
|
||||
}
|
||||
case 36:
|
||||
case MessageCode::TLightGroupTurnOffAtIndex:
|
||||
{
|
||||
auto index = static_cast<int>(floor(value));
|
||||
if (index >= List->Count() || index < 0)
|
||||
if (index >= count || index < 0)
|
||||
break;
|
||||
|
||||
auto light = static_cast<TLight*>(List->Get(index));
|
||||
light->Message(0, 0.0);
|
||||
if (MessageField2)
|
||||
auto light = List.at(index);
|
||||
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||
start_animation();
|
||||
break;
|
||||
}
|
||||
case 37:
|
||||
case MessageCode::TLightGroupGetOnCount:
|
||||
{
|
||||
auto bmp1Count = 0;
|
||||
auto countSub1 = List->Count() - 1;
|
||||
for (auto i = countSub1; i >= 0; i--)
|
||||
for (auto light : List)
|
||||
{
|
||||
if (static_cast<TLight*>(List->Get(i))->BmpIndex1)
|
||||
if (light->LightOnFlag)
|
||||
++bmp1Count;
|
||||
}
|
||||
return bmp1Count;
|
||||
}
|
||||
case 38:
|
||||
return List->Count();
|
||||
case 39:
|
||||
return MessageField2;
|
||||
case 40:
|
||||
case MessageCode::TLightGroupGetLightCount:
|
||||
return count;
|
||||
case MessageCode::TLightGroupGetMessage2:
|
||||
return static_cast<int>(MessageField2);
|
||||
case MessageCode::TLightGroupGetAnimationFlag:
|
||||
return AnimationFlag;
|
||||
case 41:
|
||||
case MessageCode::TLightGroupResetAndTurnOn:
|
||||
{
|
||||
auto index = next_light_up();
|
||||
if (index < 0)
|
||||
break;
|
||||
if (MessageField2 || AnimationFlag)
|
||||
TLightGroup::Message(34, 0.0);
|
||||
static_cast<TLight*>(List->Get(index))->Message(15, value);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull || AnimationFlag)
|
||||
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||
List.at(index)->Message(MessageCode::TLightFlasherStartTimedThenStayOn, value);
|
||||
return 1;
|
||||
}
|
||||
case 42:
|
||||
case MessageCode::TLightGroupResetAndTurnOff:
|
||||
{
|
||||
auto index = next_light_down();
|
||||
if (index < 0)
|
||||
break;
|
||||
if (MessageField2 || AnimationFlag)
|
||||
TLightGroup::Message(34, 0.0);
|
||||
static_cast<TLight*>(List->Get(index))->Message(16, value);
|
||||
if (MessageField2 != MessageCode::TLightGroupNull || AnimationFlag)
|
||||
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||
List.at(index)->Message(MessageCode::TLightFlasherStartTimedThenStayOff, value);
|
||||
return 1;
|
||||
}
|
||||
case 43:
|
||||
case MessageCode::TLightGroupRestartNotifyTimer:
|
||||
if (NotifyTimer)
|
||||
timer::kill(NotifyTimer);
|
||||
NotifyTimer = 0;
|
||||
if (value > 0.0)
|
||||
if (value > 0.0f)
|
||||
NotifyTimer = timer::set(value, this, NotifyTimerExpired);
|
||||
break;
|
||||
case 44:
|
||||
case MessageCode::TLightGroupFlashWhenOn:
|
||||
{
|
||||
auto countSub1 = List->Count() - 1;
|
||||
for (auto index = countSub1; index >= 0; index--)
|
||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(index));
|
||||
if (light->BmpIndex1)
|
||||
auto light = *it;
|
||||
if (light->LightOnFlag)
|
||||
{
|
||||
light->Message(0, 0.0);
|
||||
light->Message(16, value);
|
||||
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||
light->Message(MessageCode::TLightFlasherStartTimedThenStayOff, value);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 45:
|
||||
case MessageCode::TLightGroupToggleSplitIndex:
|
||||
{
|
||||
control::handler(code, this);
|
||||
auto index = static_cast<int>(floor(value));
|
||||
if (index >= 0)
|
||||
if (index >= 0 && index < count)
|
||||
{
|
||||
auto count = List->Count();
|
||||
if (index <= count)
|
||||
// Turn off lights (index, end]
|
||||
for (auto i = count - 1; i > index; i--)
|
||||
{
|
||||
auto countSub1 = count - 1;
|
||||
if (countSub1 > index)
|
||||
{
|
||||
countSub1 = index;
|
||||
for (auto i = countSub1, k = countSub1 - index; k != 0; i--, k--)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
light->Message(20, 0.0);
|
||||
}
|
||||
}
|
||||
if (countSub1 >= 0)
|
||||
{
|
||||
for (auto i = countSub1; i != 0; i--)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(i));
|
||||
light->Message(19, 0.0);
|
||||
}
|
||||
}
|
||||
List.at(i)->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||
}
|
||||
|
||||
// Turn on lights [begin, index]
|
||||
for (auto i = index; i >= 0; i--)
|
||||
{
|
||||
List.at(i)->Message(MessageCode::TLightResetAndTurnOn, 0.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 46:
|
||||
case MessageCode::TLightGroupStartFlasher:
|
||||
{
|
||||
auto index = next_light_down();
|
||||
if (index >= 0)
|
||||
{
|
||||
static_cast<TLight*>(List->Get(index))->Message(4, 0.0);
|
||||
List.at(index)->Message(MessageCode::TLightFlasherStart, 0.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
for (auto index = List->Count() - 1; index >= 0; index--)
|
||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||
{
|
||||
static_cast<TLight*>(List->Get(index))->Message(code, value);
|
||||
(*it)->Message(code, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -430,7 +396,7 @@ void TLightGroup::Reset()
|
||||
if (NotifyTimer)
|
||||
timer::kill(NotifyTimer);
|
||||
NotifyTimer = 0;
|
||||
MessageField2 = 0;
|
||||
MessageField2 = MessageCode::TLightGroupNull;
|
||||
AnimationFlag = 0;
|
||||
Timer1Time = Timer1TimeDefault;
|
||||
}
|
||||
@@ -442,42 +408,42 @@ void TLightGroup::reschedule_animation(float time)
|
||||
Timer = 0;
|
||||
if (time == 0)
|
||||
{
|
||||
MessageField2 = 0;
|
||||
MessageField2 = MessageCode::TLightGroupNull;
|
||||
AnimationFlag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Timer1Time = time > 0.0 ? time : Timer1TimeDefault;
|
||||
Timer1Time = time > 0.0f ? time : Timer1TimeDefault;
|
||||
Timer = timer::set(Timer1Time, this, TimerExpired);
|
||||
}
|
||||
|
||||
void TLightGroup::start_animation()
|
||||
{
|
||||
for (int index = List->Count() - 1; index >= 0; --index)
|
||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||
{
|
||||
auto light = static_cast<TLight*>(List->Get(index));
|
||||
if (light->BmpIndex1)
|
||||
light->Message(9, 0.0);
|
||||
auto light = *it;
|
||||
if (light->LightOnFlag)
|
||||
light->Message(MessageCode::TLightTurnOnTimed, 0.0);
|
||||
else
|
||||
light->Message(8, 0.0);
|
||||
light->Message(MessageCode::TLightTurnOffTimed, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
int TLightGroup::next_light_up()
|
||||
{
|
||||
for (int index = 0; index < List->Count(); ++index)
|
||||
for (auto index = 0u; index < List.size(); ++index)
|
||||
{
|
||||
if (!static_cast<TLight*>(List->Get(index))->BmpIndex1)
|
||||
return index;
|
||||
if (!List[index]->LightOnFlag)
|
||||
return static_cast<int>(index);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int TLightGroup::next_light_down()
|
||||
{
|
||||
for (int index = List->Count() - 1; index >= 0; --index)
|
||||
for (auto index = static_cast<int>(List.size()) - 1; index >= 0; --index)
|
||||
{
|
||||
if (!static_cast<TLight*>(List->Get(index))->BmpIndex1)
|
||||
if (List.at(index)->LightOnFlag)
|
||||
return index;
|
||||
}
|
||||
return -1;
|
||||
@@ -494,5 +460,5 @@ void TLightGroup::NotifyTimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto group = static_cast<TLightGroup*>(caller);
|
||||
group->NotifyTimer = 0;
|
||||
control::handler(61, group);
|
||||
control::handler(MessageCode::ControlNotifyTimerExpired, group);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
#include "TPinballComponent.h"
|
||||
class objlist_class;
|
||||
|
||||
|
||||
class TLight;
|
||||
|
||||
struct TLightGroup_player_backup
|
||||
{
|
||||
int MessageField;
|
||||
int MessageField2;
|
||||
MessageCode MessageField2;
|
||||
float Timer1Time;
|
||||
int Unknown3;
|
||||
};
|
||||
@@ -16,8 +18,8 @@ class TLightGroup :
|
||||
{
|
||||
public:
|
||||
TLightGroup(TPinballTable* table, int groupIndex);
|
||||
~TLightGroup() override;
|
||||
int Message(int code, float value) override;
|
||||
~TLightGroup() override = default;
|
||||
int Message(MessageCode code, float value) override;
|
||||
virtual void Reset();
|
||||
void reschedule_animation(float time);
|
||||
void start_animation();
|
||||
@@ -27,12 +29,12 @@ public:
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
static void NotifyTimerExpired(int timerId, void* caller);
|
||||
|
||||
objlist_class* List;
|
||||
float Timer1Time;
|
||||
std::vector<TLight*> List;
|
||||
float Timer1Time{};
|
||||
float Timer1TimeDefault;
|
||||
int MessageField2;
|
||||
int AnimationFlag;
|
||||
MessageCode MessageField2{};
|
||||
int AnimationFlag{};
|
||||
int NotifyTimer;
|
||||
int Timer;
|
||||
TLightGroup_player_backup PlayerData[4];
|
||||
TLightGroup_player_backup PlayerData[4]{};
|
||||
};
|
||||
@@ -8,39 +8,36 @@
|
||||
#include "TBall.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TLightRollover::TLightRollover(TPinballTable* table, int groupIndex) : TRollover(table, groupIndex, false)
|
||||
{
|
||||
RolloverFlag = 0;
|
||||
Timer = 0;
|
||||
if (ListBitmap != nullptr)
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
SpriteSet(-1);
|
||||
build_walls(groupIndex);
|
||||
FloatArr = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||
}
|
||||
|
||||
int TLightRollover::Message(int code, float value)
|
||||
int TLightRollover::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024)
|
||||
if (code == MessageCode::Reset)
|
||||
{
|
||||
ActiveFlag = 1;
|
||||
RolloverFlag = 0;
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
if (ListBitmap)
|
||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
||||
SpriteSet(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TLightRollover::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void TLightRollover::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge)
|
||||
{
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= coef;
|
||||
ball->RayMaxDistance -= distance;
|
||||
ball->not_again(edge);
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
@@ -54,11 +51,10 @@ void TLightRollover::Collision(TBall* ball, vector_type* nextPosition, vector_ty
|
||||
}
|
||||
else
|
||||
{
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
control::handler(63, this);
|
||||
loader::play_sound(SoftHitSoundId, this, "TLightRollover");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
RolloverFlag = RolloverFlag == 0;
|
||||
if (ListBitmap)
|
||||
render::sprite_set_bitmap(RenderSprite, static_cast<gdrv_bitmap8*>(ListBitmap->Get(0)));
|
||||
SpriteSet(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +62,6 @@ void TLightRollover::Collision(TBall* ball, vector_type* nextPosition, vector_ty
|
||||
void TLightRollover::delay_expired(int timerId, void* caller)
|
||||
{
|
||||
auto roll = static_cast<TLightRollover*>(caller);
|
||||
render::sprite_set_bitmap(roll->RenderSprite, nullptr);
|
||||
roll->SpriteSet(-1);
|
||||
roll->Timer = 0;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ class TLightRollover :
|
||||
public:
|
||||
TLightRollover(TPinballTable* table, int groupIndex);
|
||||
~TLightRollover() override = default;
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void delay_expired(int timerId, void* caller);
|
||||
|
||||
@@ -11,48 +11,56 @@ TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collis
|
||||
Y0 = y0;
|
||||
X1 = x1;
|
||||
Y1 = y1;
|
||||
maths::line_init(&Line, x0, y0, x1, y1);
|
||||
maths::line_init(Line, x0, y0, x1, y1);
|
||||
}
|
||||
|
||||
TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, struct vector_type* start,
|
||||
struct vector_type* end) : TEdgeSegment(collCmp, activeFlag, collisionGroup)
|
||||
TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start,
|
||||
const vector2& end) : TEdgeSegment(collCmp, activeFlag, collisionGroup)
|
||||
{
|
||||
X0 = start->X;
|
||||
Y0 = start->Y;
|
||||
X1 = end->X;
|
||||
Y1 = end->Y;
|
||||
maths::line_init(&Line, X0, Y0, X1, Y1);
|
||||
X0 = start.X;
|
||||
Y0 = start.Y;
|
||||
X1 = end.X;
|
||||
Y1 = end.Y;
|
||||
maths::line_init(Line, X0, Y0, X1, Y1);
|
||||
}
|
||||
|
||||
void TLine::Offset(float offset)
|
||||
{
|
||||
float offX = offset * Line.PerpendicularL.X;
|
||||
float offY = offset * Line.PerpendicularL.Y;
|
||||
float offX = offset * Line.PerpendicularC.X;
|
||||
float offY = offset * Line.PerpendicularC.Y;
|
||||
|
||||
X0 += offX;
|
||||
Y0 += offY;
|
||||
X1 += offX;
|
||||
Y1 += offY;
|
||||
maths::line_init(&Line, X0, Y0, X1, Y1);
|
||||
maths::line_init(Line, X0, Y0, X1, Y1);
|
||||
}
|
||||
|
||||
float TLine::FindCollisionDistance(ray_type* ray)
|
||||
float TLine::FindCollisionDistance(const ray_type& ray)
|
||||
{
|
||||
return maths::ray_intersect_line(ray, &Line);
|
||||
return maths::ray_intersect_line(ray, Line);
|
||||
}
|
||||
|
||||
void TLine::EdgeCollision(TBall* ball, float coef)
|
||||
void TLine::EdgeCollision(TBall* ball, float distance)
|
||||
{
|
||||
CollisionComponent->Collision(
|
||||
ball,
|
||||
&Line.RayIntersect,
|
||||
&Line.PerpendicularL,
|
||||
coef,
|
||||
&Line.PerpendicularC,
|
||||
distance,
|
||||
this);
|
||||
}
|
||||
|
||||
void TLine::place_in_grid()
|
||||
void TLine::place_in_grid(RectF* aabb)
|
||||
{
|
||||
if (aabb)
|
||||
{
|
||||
aabb->Merge({
|
||||
std::max(X0, X1), std::max(Y0, Y1),
|
||||
std::min(X0, X1), std::min(Y0, Y1)
|
||||
});
|
||||
}
|
||||
|
||||
auto edgeMan = TTableLayer::edge_manager;
|
||||
auto xBox0 = edgeMan->box_x(X0);
|
||||
auto yBox0 = edgeMan->box_y(Y0);
|
||||
@@ -79,123 +87,45 @@ void TLine::place_in_grid()
|
||||
{
|
||||
if (dirY == 1)
|
||||
{
|
||||
if (yBox0 <= yBox1)
|
||||
{
|
||||
do
|
||||
edgeMan->add_edge_to_box(xBox0, yBox0++, this);
|
||||
while (yBox0 <= yBox1);
|
||||
}
|
||||
while (yBox0 <= yBox1)
|
||||
edgeMan->add_edge_to_box(xBox0, yBox0++, this);
|
||||
}
|
||||
else if (yBox0 >= yBox1)
|
||||
else
|
||||
{
|
||||
do
|
||||
while (yBox0 >= yBox1)
|
||||
edgeMan->add_edge_to_box(xBox0, yBox0--, this);
|
||||
while (yBox0 >= yBox1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float yCoord, xCoord;
|
||||
int indexX1 = xBox0, indexY1 = yBox0;
|
||||
int bresIndexX = xBox0 + 1, bresIndexY = yBox0 + 1;
|
||||
auto bresDyDx = (Y0 - Y1) / (X0 - X1);
|
||||
auto bresXAdd = Y0 - bresDyDx * X0;
|
||||
edgeMan->add_edge_to_box(xBox0, yBox0, this);
|
||||
if (dirX == 1)
|
||||
|
||||
// Bresenham line formula: y = dYdX * (x - x0) + y0; dYdX = (y0 - y1) / (x0 - x1)
|
||||
auto dyDx = (Y0 - Y1) / (X0 - X1);
|
||||
// Precompute constant part: dYdX * (-x0) + y0
|
||||
auto precomp = -X0 * dyDx + Y0;
|
||||
// X and Y indexes are offset by one when going forwards, not sure why
|
||||
auto xBias = dirX == 1 ? 1 : 0, yBias = dirY == 1 ? 1 : 0;
|
||||
|
||||
for (auto indexX = xBox0, indexY = yBox0; indexX != xBox1 || indexY != yBox1;)
|
||||
{
|
||||
if (dirY == 1)
|
||||
// Calculate y from indexY and from line formula
|
||||
auto yDiscrete = (indexY + yBias) * edgeMan->AdvanceY + edgeMan->MinY;
|
||||
auto ylinear = ((indexX + xBias) * edgeMan->AdvanceX + edgeMan->MinX) * dyDx + precomp;
|
||||
if (dirY == 1 ? ylinear >= yDiscrete : ylinear <= yDiscrete)
|
||||
{
|
||||
do
|
||||
{
|
||||
yCoord = bresIndexY * edgeMan->AdvanceY + edgeMan->Y;
|
||||
xCoord = (bresIndexX * edgeMan->AdvanceX + edgeMan->X) * bresDyDx + bresXAdd;
|
||||
if (xCoord >= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
{
|
||||
++indexX1;
|
||||
++bresIndexX;
|
||||
}
|
||||
++indexY1;
|
||||
++bresIndexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
++indexX1;
|
||||
++bresIndexX;
|
||||
}
|
||||
edgeMan->add_edge_to_box(indexX1, indexY1, this);
|
||||
}
|
||||
while (indexX1 != xBox1 || indexY1 != yBox1);
|
||||
// Advance indexY when discrete value is ahead/behind
|
||||
// Advance indexX when discrete value matches linear value
|
||||
indexY += dirY;
|
||||
if (ylinear == yDiscrete)
|
||||
indexX += dirX;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
yCoord = indexY1 * edgeMan->AdvanceY + edgeMan->Y;
|
||||
xCoord = (bresIndexX * edgeMan->AdvanceX + edgeMan->X) * bresDyDx + bresXAdd;
|
||||
if (xCoord <= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
{
|
||||
++indexX1;
|
||||
++bresIndexX;
|
||||
}
|
||||
--indexY1;
|
||||
}
|
||||
else
|
||||
{
|
||||
++indexX1;
|
||||
++bresIndexX;
|
||||
}
|
||||
edgeMan->add_edge_to_box(indexX1, indexY1, this);
|
||||
}
|
||||
while (indexX1 != xBox1 || indexY1 != yBox1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dirY == 1)
|
||||
{
|
||||
do
|
||||
{
|
||||
xCoord = bresIndexY * edgeMan->AdvanceY + edgeMan->Y;
|
||||
yCoord = (indexX1 * edgeMan->AdvanceX + edgeMan->X) * bresDyDx + bresXAdd;
|
||||
if (yCoord >= xCoord)
|
||||
{
|
||||
if (yCoord == xCoord)
|
||||
--indexX1;
|
||||
++indexY1;
|
||||
++bresIndexY;
|
||||
}
|
||||
else
|
||||
{
|
||||
--indexX1;
|
||||
}
|
||||
edgeMan->add_edge_to_box(indexX1, indexY1, this);
|
||||
}
|
||||
while (indexX1 != xBox1 || indexY1 != yBox1);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
yCoord = indexY1 * edgeMan->AdvanceY + edgeMan->Y;
|
||||
xCoord = (indexX1 * edgeMan->AdvanceX + edgeMan->X) * bresDyDx + bresXAdd;
|
||||
if (xCoord <= yCoord)
|
||||
{
|
||||
if (xCoord == yCoord)
|
||||
--indexX1;
|
||||
--indexY1;
|
||||
}
|
||||
else
|
||||
{
|
||||
--indexX1;
|
||||
}
|
||||
edgeMan->add_edge_to_box(indexX1, indexY1, this);
|
||||
}
|
||||
while (indexX1 != xBox1 || indexY1 != yBox1);
|
||||
// Advance indexX otherwise
|
||||
indexX += dirX;
|
||||
}
|
||||
edgeMan->add_edge_to_box(indexX, indexY, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ class TLine :
|
||||
public TEdgeSegment
|
||||
{
|
||||
public:
|
||||
line_type Line;
|
||||
line_type Line{};
|
||||
float X0, Y0, X1, Y1;
|
||||
TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, float x0, float y0, float x1, float y1);
|
||||
TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, vector_type* start, vector_type* end);
|
||||
TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start, const vector2& end);
|
||||
void Offset(float offset);
|
||||
float FindCollisionDistance(ray_type* ray) override;
|
||||
void EdgeCollision(TBall* ball, float coef) override;
|
||||
void place_in_grid() override;
|
||||
float FindCollisionDistance(const ray_type& ray) override;
|
||||
void EdgeCollision(TBall* ball, float distance) override;
|
||||
void place_in_grid(RectF* aabb) override;
|
||||
};
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "TBall.h"
|
||||
#include "TLine.h"
|
||||
#include "TPinballTable.h"
|
||||
@@ -12,7 +11,7 @@
|
||||
TOneway::TOneway(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||
{
|
||||
visualStruct visual{};
|
||||
vector_type linePt1{}, linePt2{};
|
||||
vector2 linePt1{}, linePt2{};
|
||||
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
if (visual.FloatArrCount == 2)
|
||||
@@ -22,38 +21,38 @@ TOneway::TOneway(TPinballTable* table, int groupIndex) : TCollisionComponent(tab
|
||||
linePt1.X = visual.FloatArr[2];
|
||||
linePt1.Y = visual.FloatArr[3];
|
||||
|
||||
auto line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &linePt2, &linePt1);
|
||||
auto line = new TLine(this, &ActiveFlag, visual.CollisionGroup, linePt2, linePt1);
|
||||
if (line)
|
||||
{
|
||||
line->Offset(table->CollisionCompOffset);
|
||||
line->place_in_grid();
|
||||
EdgeList->Add(line);
|
||||
line->place_in_grid(&AABB);
|
||||
EdgeList.push_back(line);
|
||||
}
|
||||
|
||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &linePt1, &linePt2);
|
||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, linePt1, linePt2);
|
||||
Line = line;
|
||||
if (line)
|
||||
{
|
||||
line->Offset(-table->CollisionCompOffset * 0.8f);
|
||||
Line->place_in_grid();
|
||||
EdgeList->Add(Line);
|
||||
Line->place_in_grid(&AABB);
|
||||
EdgeList.push_back(Line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TOneway::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TOneway::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (edge == Line)
|
||||
{
|
||||
ball->not_again(edge);
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= coef;
|
||||
ball->RayMaxDistance -= distance;
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
if (HardHitSoundId)
|
||||
loader::play_sound(HardHitSoundId);
|
||||
control::handler(63, this);
|
||||
loader::play_sound(HardHitSoundId, ball, "TOneway1");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
else if (PinballTable->TiltLockFlag)
|
||||
@@ -67,20 +66,9 @@ void TOneway::Collision(TBall* ball, vector_type* nextPosition, vector_type* dir
|
||||
Elasticity,
|
||||
Smoothness,
|
||||
Threshold,
|
||||
Boost) > 0.2)
|
||||
Boost) > 0.2f)
|
||||
{
|
||||
if (SoftHitSoundId)
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
loader::play_sound(SoftHitSoundId, ball, "TOneway2");
|
||||
}
|
||||
}
|
||||
|
||||
void TOneway::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 6)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TOneway::get_scoring(int index)
|
||||
{
|
||||
return index < 6 ? Scores[index] : 0;
|
||||
}
|
||||
@@ -8,11 +8,8 @@ class TOneway : public TCollisionComponent
|
||||
public:
|
||||
TOneway(TPinballTable* table, int groupIndex);
|
||||
~TOneway() override = default;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
|
||||
TLine* Line;
|
||||
int Scores[6];
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "pch.h"
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "proj.h"
|
||||
#include "render.h"
|
||||
#include "TZmapList.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TTableLayer.h"
|
||||
|
||||
TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals)
|
||||
{
|
||||
@@ -16,83 +18,91 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
|
||||
PinballTable = table;
|
||||
RenderSprite = nullptr;
|
||||
ListBitmap = nullptr;
|
||||
ListZMap = nullptr;
|
||||
GroupName = nullptr;
|
||||
Control = nullptr;
|
||||
VisualPosNormX= -1.0f;
|
||||
VisualPosNormY = -1.0f;
|
||||
GroupIndex = groupIndex;
|
||||
if (table)
|
||||
table->ComponentList->Add(this);
|
||||
table->ComponentList.push_back(this);
|
||||
if (groupIndex >= 0)
|
||||
GroupName = loader::query_name(groupIndex);
|
||||
|
||||
if (loadVisuals && groupIndex >= 0)
|
||||
{
|
||||
int visualCount = loader::query_visual_states(groupIndex);
|
||||
for (int index = 0; index < visualCount; ++index)
|
||||
{
|
||||
loader::query_visual(groupIndex, index, &visual);
|
||||
if (visual.Bitmap)
|
||||
if (visual.Bitmap.Bmp)
|
||||
{
|
||||
assertm(visual.Bitmap.ZMap, "Bitmap/zMap pairing is mandatory");
|
||||
if (!ListBitmap)
|
||||
ListBitmap = new TZmapList(visualCount, 4);
|
||||
if (ListBitmap)
|
||||
ListBitmap->Add(visual.Bitmap);
|
||||
}
|
||||
if (visual.ZMap)
|
||||
{
|
||||
if (!ListZMap)
|
||||
ListZMap = new TZmapList(visualCount, 4);
|
||||
if (ListZMap)
|
||||
ListZMap->Add(visual.ZMap);
|
||||
ListBitmap = new std::vector<SpriteData>();
|
||||
ListBitmap->push_back(visual.Bitmap);
|
||||
}
|
||||
}
|
||||
zmap_header_type* zMap = nullptr;
|
||||
if (ListZMap)
|
||||
zMap = static_cast<zmap_header_type*>(ListZMap->Get(0));
|
||||
|
||||
if (ListBitmap)
|
||||
{
|
||||
rectangle_type bmp1Rect{}, tmpRect{};
|
||||
auto rootBmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
const auto rootSprite = ListBitmap->at(0);
|
||||
const auto rootBmp = rootSprite.Bmp;
|
||||
|
||||
bmp1Rect.XPosition = rootBmp->XPosition - table->XOffset;
|
||||
bmp1Rect.YPosition = rootBmp->YPosition - table->YOffset;
|
||||
bmp1Rect.Width = rootBmp->Width;
|
||||
bmp1Rect.Height = rootBmp->Height;
|
||||
for (int index = 1; index < ListBitmap->Count(); index++)
|
||||
|
||||
for (auto index = 1u; index < ListBitmap->size(); index++)
|
||||
{
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(index));
|
||||
auto bmp = ListBitmap->at(index).Bmp;
|
||||
tmpRect.XPosition = bmp->XPosition - table->XOffset;
|
||||
tmpRect.YPosition = bmp->YPosition - table->YOffset;
|
||||
tmpRect.Width = bmp->Width;
|
||||
tmpRect.Height = bmp->Height;
|
||||
maths::enclosing_box(&bmp1Rect, &tmpRect, &bmp1Rect);
|
||||
maths::enclosing_box(bmp1Rect, tmpRect, bmp1Rect);
|
||||
}
|
||||
|
||||
RenderSprite = render::create_sprite(
|
||||
visualCount > 0 ? VisualType::Sprite : VisualType::None,
|
||||
RenderSprite = new render_sprite(
|
||||
VisualTypes::Sprite,
|
||||
rootBmp,
|
||||
zMap,
|
||||
rootSprite.ZMap,
|
||||
rootBmp->XPosition - table->XOffset,
|
||||
rootBmp->YPosition - table->YOffset,
|
||||
&bmp1Rect);
|
||||
|
||||
// Sound position = center of root visual, reverse-projected, normalized.
|
||||
auto& rect = RenderSprite->BmpRect;
|
||||
vector2i pos2D{ rect.XPosition + rect.Width / 2, rect.YPosition + rect.Height / 2 };
|
||||
auto pos3D = proj::ReverseXForm(pos2D);
|
||||
auto posNorm = TTableLayer::edge_manager->NormalizeBox(pos3D);
|
||||
VisualPosNormX = posNorm.X;
|
||||
VisualPosNormY = posNorm.Y;
|
||||
}
|
||||
}
|
||||
GroupIndex = groupIndex;
|
||||
}
|
||||
|
||||
|
||||
TPinballComponent::~TPinballComponent()
|
||||
{
|
||||
TPinballTable* table = PinballTable;
|
||||
if (table)
|
||||
table->ComponentList->Delete(this);
|
||||
if (PinballTable)
|
||||
{
|
||||
// ComponentList contains one reference to each component.
|
||||
auto& components = PinballTable->ComponentList;
|
||||
auto position = std::find(components.begin(), components.end(), this);
|
||||
if (position != components.end())
|
||||
components.erase(position);
|
||||
}
|
||||
|
||||
delete ListBitmap;
|
||||
delete ListZMap;
|
||||
}
|
||||
|
||||
|
||||
int TPinballComponent::Message(int code, float value)
|
||||
int TPinballComponent::Message(MessageCode code, float value)
|
||||
{
|
||||
MessageField = code;
|
||||
if (code == 1024)
|
||||
MessageField = static_cast<int>(code);
|
||||
if (code == MessageCode::Reset)
|
||||
MessageField = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -101,21 +111,55 @@ void TPinballComponent::port_draw()
|
||||
{
|
||||
}
|
||||
|
||||
void TPinballComponent::put_scoring(int index, int score)
|
||||
int TPinballComponent::get_scoring(unsigned int index) const
|
||||
{
|
||||
return Control == nullptr || index >= Control->ScoreCount ? 0 : Control->Scores[index];
|
||||
}
|
||||
|
||||
int TPinballComponent::get_scoring(int index)
|
||||
vector2 TPinballComponent::get_coordinates()
|
||||
{
|
||||
return 0;
|
||||
return {VisualPosNormX, VisualPosNormY};
|
||||
}
|
||||
|
||||
void* TPinballComponent::operator new(size_t Size)
|
||||
void TPinballComponent::SpriteSet(int index) const
|
||||
{
|
||||
return calloc(1u, Size);
|
||||
if (!ListBitmap)
|
||||
return;
|
||||
|
||||
int xPos, yPos;
|
||||
gdrv_bitmap8* bmp;
|
||||
zmap_header_type* zMap;
|
||||
if (index >= 0)
|
||||
{
|
||||
auto& spriteData = ListBitmap->at(index);
|
||||
bmp = spriteData.Bmp;
|
||||
zMap = spriteData.ZMap;
|
||||
xPos = bmp->XPosition - PinballTable->XOffset;
|
||||
yPos = bmp->YPosition - PinballTable->YOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
bmp = nullptr;
|
||||
zMap = nullptr;
|
||||
xPos = RenderSprite->BmpRect.XPosition;
|
||||
yPos = RenderSprite->BmpRect.YPosition;
|
||||
}
|
||||
|
||||
RenderSprite->set(bmp, zMap, xPos, yPos);
|
||||
}
|
||||
|
||||
void TPinballComponent::operator delete(void* p)
|
||||
void TPinballComponent::SpriteSetBall(int index, vector2i pos, float depth) const
|
||||
{
|
||||
free(p); /*Original does not have this*/
|
||||
if (ListBitmap)
|
||||
{
|
||||
gdrv_bitmap8* bmp = nullptr;
|
||||
if (index >= 0)
|
||||
{
|
||||
bmp = ListBitmap->at(index).Bmp;
|
||||
pos.X -= bmp->Width / 2;
|
||||
pos.Y -= bmp->Height / 2;
|
||||
}
|
||||
|
||||
RenderSprite->ball_set(bmp, depth, pos.X, pos.Y);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
struct render_sprite_type_struct;
|
||||
struct SpriteData;
|
||||
struct vector2i;
|
||||
struct zmap_header_type;
|
||||
struct gdrv_bitmap8;
|
||||
struct render_sprite;
|
||||
struct component_control;
|
||||
struct vector2;
|
||||
class TPinballTable;
|
||||
class TZmapList;
|
||||
|
||||
enum class message_code
|
||||
|
||||
enum class MessageCode
|
||||
{
|
||||
// Private codes <1000, different meaning for each component
|
||||
TFlipperNull = 0,
|
||||
TFlipperExtend = 1,
|
||||
TFlipperRetract = 2,
|
||||
|
||||
TLightTurnOff = 0,
|
||||
TLightTurnOn = 1,
|
||||
TLightGetLightOnFlag = 2,
|
||||
TLightGetFlasherOnFlag = 3,
|
||||
TLightFlasherStart = 4,
|
||||
TLightApplyMultDelay = 5,
|
||||
TLightApplyDelay = 6,
|
||||
TLightFlasherStartTimed = 7,
|
||||
TLightTurnOffTimed = 8,
|
||||
TLightTurnOnTimed = 9,
|
||||
TLightSetOnStateBmpIndex = 11,
|
||||
TLightIncOnStateBmpIndex = 12,
|
||||
TLightDecOnStateBmpIndex = 13,
|
||||
TLightResetTimed = 14,
|
||||
TLightFlasherStartTimedThenStayOn = 15,
|
||||
TLightFlasherStartTimedThenStayOff = 16,
|
||||
TLightToggleValue = 17,
|
||||
TLightResetAndToggleValue = 18,
|
||||
TLightResetAndTurnOn = 19,
|
||||
TLightResetAndTurnOff = 20,
|
||||
TLightToggle = 21,
|
||||
TLightResetAndToggle = 22,
|
||||
TLightSetMessageField = 23,
|
||||
TLightFtTmpOverrideOn = -24,
|
||||
TLightFtTmpOverrideOff = -25,
|
||||
TLightFtResetOverride = -26,
|
||||
|
||||
TLightGroupNull = 0,
|
||||
TLightGroupStepBackward = 24,
|
||||
TLightGroupStepForward = 25,
|
||||
TLightGroupAnimationBackward = 26,
|
||||
TLightGroupAnimationForward = 27,
|
||||
TLightGroupLightShowAnimation = 28,
|
||||
TLightGroupGameOverAnimation = 29,
|
||||
TLightGroupRandomAnimationSaturation = 30,
|
||||
TLightGroupRandomAnimationDesaturation = 31,
|
||||
TLightGroupOffsetAnimationForward = 32,
|
||||
TLightGroupOffsetAnimationBackward = 33,
|
||||
TLightGroupReset = 34,
|
||||
TLightGroupTurnOnAtIndex = 35,
|
||||
TLightGroupTurnOffAtIndex = 36,
|
||||
TLightGroupGetOnCount = 37,
|
||||
TLightGroupGetLightCount = 38,
|
||||
TLightGroupGetMessage2 = 39,
|
||||
TLightGroupGetAnimationFlag = 40,
|
||||
TLightGroupResetAndTurnOn = 41,
|
||||
TLightGroupResetAndTurnOff = 42,
|
||||
TLightGroupRestartNotifyTimer = 43,
|
||||
TLightGroupFlashWhenOn = 44,
|
||||
TLightGroupToggleSplitIndex = 45,
|
||||
TLightGroupStartFlasher = 46,
|
||||
TLightGroupCountdownEnded = 47,
|
||||
|
||||
TBumperSetBmpIndex = 11,
|
||||
TBumperIncBmpIndex = 12,
|
||||
TBumperDecBmpIndex = 13,
|
||||
|
||||
TComponentGroupResetNotifyTimer = 48,
|
||||
|
||||
TPopupTargetDisable = 49,
|
||||
TPopupTargetEnable = 50,
|
||||
|
||||
TBlockerDisable = 51,
|
||||
TBlockerEnable = 52,
|
||||
TBlockerRestartTimeout = 59,
|
||||
|
||||
TGateDisable = 53,
|
||||
TGateEnable = 54,
|
||||
|
||||
TKickoutRestartTimer = 55,
|
||||
|
||||
TSinkUnknown7 = 7,
|
||||
TSinkResetTimer = 56,
|
||||
|
||||
TSoloTargetDisable = 49,
|
||||
TSoloTargetEnable = 50,
|
||||
|
||||
TTimerResetTimer = 59,
|
||||
|
||||
ControlBallCaptured = 57,
|
||||
ControlBallReleased = 58,
|
||||
ControlTimerExpired = 60,
|
||||
ControlNotifyTimerExpired = 61,
|
||||
ControlSpinnerLoopReset = 62,
|
||||
ControlCollision = 63,
|
||||
ControlEnableMultiplier = 64,
|
||||
ControlDisableMultiplier = 65,
|
||||
ControlMissionComplete = 66,
|
||||
ControlMissionStarted = 67,
|
||||
|
||||
// Public codes 1000+, apply to all components
|
||||
LeftFlipperInputPressed = 1000,
|
||||
LeftFlipperInputReleased = 1001,
|
||||
RightFlipperInputPressed = 1002,
|
||||
RightFlipperInputReleased = 1003,
|
||||
PlungerInputPressed = 1004,
|
||||
PlungerInputReleased = 1005,
|
||||
Pause = 1008,
|
||||
Resume = 1009,
|
||||
LooseFocus = 1010,
|
||||
SetTiltLock = 1011,
|
||||
ClearTiltLock = 1012,
|
||||
StartGamePlayer1 = 1013,
|
||||
NewGame = 1014,
|
||||
PlungerFeedBall = 1015,
|
||||
PlungerStartFeedTimer = 1016,
|
||||
PlungerLaunchBall = 1017,
|
||||
PlungerRelaunchBall = 1018,
|
||||
PlayerChanged = 1020,
|
||||
SwitchToNextPlayer = 1021,
|
||||
GameOver = 1022,
|
||||
Reset = 1024,
|
||||
LightActiveCount = 37,
|
||||
LightTotalCount = 38,
|
||||
LightSetMessageField = 23,
|
||||
};
|
||||
|
||||
class TPinballComponent
|
||||
@@ -18,23 +136,23 @@ class TPinballComponent
|
||||
public:
|
||||
TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals);
|
||||
virtual ~TPinballComponent();
|
||||
virtual int Message(int code, float value);
|
||||
virtual int Message(MessageCode code, float value);
|
||||
virtual void port_draw();
|
||||
virtual void put_scoring(int index, int score);
|
||||
virtual int get_scoring(int index);
|
||||
int get_scoring(unsigned int index) const;
|
||||
virtual vector2 get_coordinates();
|
||||
void SpriteSet(int index) const;
|
||||
void SpriteSetBall(int index, vector2i pos, float depth) const;
|
||||
|
||||
void* operator new(size_t Size);
|
||||
void operator delete(void* p);
|
||||
|
||||
__int8 UnusedBaseFlag;
|
||||
__int8 ActiveFlag;
|
||||
char UnusedBaseFlag;
|
||||
char ActiveFlag;
|
||||
int MessageField;
|
||||
char* GroupName;
|
||||
int Unknown4;
|
||||
component_control* Control;
|
||||
int GroupIndex;
|
||||
render_sprite_type_struct* RenderSprite;
|
||||
render_sprite* RenderSprite;
|
||||
TPinballTable* PinballTable;
|
||||
TZmapList* ListBitmap;
|
||||
TZmapList* ListZMap;
|
||||
std::vector<SpriteData>* ListBitmap;
|
||||
private:
|
||||
float VisualPosNormX;
|
||||
float VisualPosNormY;
|
||||
};
|
||||
@@ -4,12 +4,9 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "objlist_class.h"
|
||||
#include "midi.h"
|
||||
#include "pb.h"
|
||||
#include "pinball.h"
|
||||
#include "render.h"
|
||||
#include "Sound.h"
|
||||
#include "TBall.h"
|
||||
#include "TBlocker.h"
|
||||
#include "TBumper.h"
|
||||
@@ -40,6 +37,7 @@
|
||||
#include "TPlunger.h"
|
||||
#include "TFlipper.h"
|
||||
#include "TDrain.h"
|
||||
#include "translations.h"
|
||||
|
||||
int TPinballTable::score_multipliers[5] = {1, 2, 3, 5, 10};
|
||||
|
||||
@@ -48,25 +46,22 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false)
|
||||
{
|
||||
int shortArrLength;
|
||||
|
||||
ComponentList = new objlist_class(32, 16);
|
||||
BallList = new objlist_class(3, 1);
|
||||
CurScoreStruct = nullptr;
|
||||
ScoreBallcount = nullptr;
|
||||
ScorePlayerNumber1 = nullptr;
|
||||
BallInSink = 0;
|
||||
BallInDrainFlag = 0;
|
||||
ActiveFlag = 1;
|
||||
TiltLockFlag = 0;
|
||||
EndGameTimeoutTimer = 0;
|
||||
LightShowTimer = 0;
|
||||
ReplayTimer = 0;
|
||||
TiltTimeoutTimer = 0;
|
||||
MultiballFlag = 0;
|
||||
MultiballFlag = false;
|
||||
PlayerCount = 0;
|
||||
|
||||
auto ballObj = new TBall(this);
|
||||
BallList->Add(ballObj);
|
||||
if (ballObj)
|
||||
ballObj->ActiveFlag = 0;
|
||||
auto ball = AddBall({0.0f, 0.0f});
|
||||
ball->Disable();
|
||||
|
||||
new TTableLayer(this);
|
||||
LightGroup = new TLightGroup(this, 0);
|
||||
|
||||
@@ -103,7 +98,7 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false)
|
||||
Plunger = new TPlunger(this, groupIndex);
|
||||
break;
|
||||
case 1002:
|
||||
LightGroup->List->Add(new TLight(this, groupIndex));
|
||||
LightGroup->List.push_back(new TLight(this, groupIndex));
|
||||
break;
|
||||
case 1003:
|
||||
FlipperL = new TFlipper(this, groupIndex);
|
||||
@@ -190,87 +185,81 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false)
|
||||
}
|
||||
|
||||
render::build_occlude_list();
|
||||
pinball::InfoTextBox = dynamic_cast<TTextBox*>(find_component("info_text_box"));
|
||||
pinball::MissTextBox = dynamic_cast<TTextBox*>(find_component("mission_text_box"));
|
||||
pb::InfoTextBox = dynamic_cast<TTextBox*>(find_component("info_text_box"));
|
||||
pb::MissTextBox = dynamic_cast<TTextBox*>(find_component("mission_text_box"));
|
||||
control::make_links(this);
|
||||
}
|
||||
|
||||
|
||||
TPinballTable::~TPinballTable()
|
||||
{
|
||||
for (int scoreIndex = 0; scoreIndex < 4; scoreIndex++)
|
||||
for (auto& PlayerScore : PlayerScores)
|
||||
{
|
||||
memory::free(PlayerScores[scoreIndex].ScoreStruct);
|
||||
delete PlayerScore.ScoreStruct;
|
||||
}
|
||||
if (ScorePlayerNumber1)
|
||||
{
|
||||
memory::free(ScorePlayerNumber1);
|
||||
delete ScorePlayerNumber1;
|
||||
ScorePlayerNumber1 = nullptr;
|
||||
}
|
||||
if (ScoreBallcount)
|
||||
{
|
||||
memory::free(ScoreBallcount);
|
||||
delete ScoreBallcount;
|
||||
ScoreBallcount = nullptr;
|
||||
}
|
||||
delete LightGroup;
|
||||
while (ComponentList->Count() > 0)
|
||||
while (!ComponentList.empty())
|
||||
{
|
||||
delete static_cast<TPinballComponent*>(ComponentList->Get(0));
|
||||
// Component destructor removes it from the list.
|
||||
delete ComponentList[0];
|
||||
}
|
||||
delete BallList;
|
||||
delete ComponentList;
|
||||
control::ClearLinks();
|
||||
pb::InfoTextBox = pb::MissTextBox = nullptr;
|
||||
}
|
||||
|
||||
TPinballComponent* TPinballTable::find_component(LPCSTR componentName)
|
||||
{
|
||||
int objCount = ComponentList->Count();
|
||||
if (objCount > 0)
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
for (int index = 0; index < objCount; ++index)
|
||||
const char* groupName = component->GroupName;
|
||||
if (groupName && !strcmp(groupName, componentName))
|
||||
{
|
||||
TPinballComponent* obj = static_cast<TPinballComponent*>(ComponentList->Get(index));
|
||||
const CHAR* groupName = obj->GroupName;
|
||||
if (groupName && !lstrcmpA(groupName, componentName))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
return component;
|
||||
}
|
||||
}
|
||||
MessageBoxA(nullptr, "Table cant find:", componentName, 0x2000u);
|
||||
|
||||
pb::ShowMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find:", componentName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TPinballComponent* TPinballTable::find_component(int groupIndex)
|
||||
{
|
||||
char Buffer[40];
|
||||
int objCount = ComponentList->Count();
|
||||
if (objCount > 0)
|
||||
char Buffer[40]{};
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
for (int index = 0; index < objCount; ++index)
|
||||
{
|
||||
TPinballComponent* obj = static_cast<TPinballComponent*>(ComponentList->Get(index));
|
||||
if (obj->GroupIndex == groupIndex)
|
||||
return obj;
|
||||
}
|
||||
if (component->GroupIndex == groupIndex)
|
||||
return component;
|
||||
}
|
||||
_itoa_s(groupIndex, Buffer, 10);
|
||||
MessageBoxA(nullptr, "Table cant find (lh):", Buffer, 0x2000u);
|
||||
|
||||
snprintf(Buffer, sizeof Buffer, "%d", groupIndex);
|
||||
pb::ShowMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find (lh):", Buffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int TPinballTable::AddScore(int score)
|
||||
{
|
||||
if (ScoreSpecial3Flag)
|
||||
if (JackpotScoreFlag)
|
||||
{
|
||||
ScoreSpecial3 += score;
|
||||
if (ScoreSpecial3 > 5000000)
|
||||
ScoreSpecial3 = 5000000;
|
||||
JackpotScore += score;
|
||||
const auto jackpotLimit = !pb::FullTiltMode ? 5000000 : 10000000;
|
||||
if (JackpotScore > jackpotLimit)
|
||||
JackpotScore = jackpotLimit;
|
||||
}
|
||||
if (ScoreSpecial2Flag)
|
||||
if (BonusScoreFlag)
|
||||
{
|
||||
ScoreSpecial2 += score;
|
||||
if (ScoreSpecial2 > 5000000)
|
||||
ScoreSpecial2 = 5000000;
|
||||
BonusScore += score;
|
||||
if (BonusScore > 5000000)
|
||||
BonusScore = 5000000;
|
||||
}
|
||||
int addedScore = ScoreAdded + score * score_multipliers[ScoreMultiplier];
|
||||
CurScore += addedScore;
|
||||
@@ -299,76 +288,77 @@ void TPinballTable::ChangeBallCount(int count)
|
||||
|
||||
void TPinballTable::tilt(float time)
|
||||
{
|
||||
if (!TiltLockFlag && !BallInSink)
|
||||
if (!TiltLockFlag && !BallInDrainFlag)
|
||||
{
|
||||
pinball::InfoTextBox->Clear();
|
||||
pinball::MissTextBox->Clear();
|
||||
pinball::InfoTextBox->Display(pinball::get_rc_string(35, 0), -1.0);
|
||||
loader::play_sound(SoundIndex3);
|
||||
pb::InfoTextBox->Clear();
|
||||
pb::MissTextBox->Clear();
|
||||
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING136), -1.0);
|
||||
loader::play_sound(SoundIndex3, nullptr, "TPinballTable1");
|
||||
TiltTimeoutTimer = timer::set(30.0, this, tilt_timeout);
|
||||
|
||||
for (int i = 0; i < ComponentList->Count(); i++)
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(ComponentList->Get(i))->Message(1011, time);
|
||||
component->Message(MessageCode::SetTiltLock, time);
|
||||
}
|
||||
LightGroup->Message(8, 0);
|
||||
LightGroup->Message(MessageCode::TLightTurnOffTimed, 0);
|
||||
TiltLockFlag = 1;
|
||||
control::table_control_handler(1011);
|
||||
control::table_control_handler(MessageCode::SetTiltLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TPinballTable::port_draw()
|
||||
{
|
||||
for (int index = ComponentList->Count() - 1; index >= 0; index--)
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(ComponentList->Get(index))->port_draw();
|
||||
component->port_draw();
|
||||
}
|
||||
}
|
||||
|
||||
int TPinballTable::Message(int code, float value)
|
||||
int TPinballTable::Message(MessageCode code, float value)
|
||||
{
|
||||
LPSTR rc_text;
|
||||
const char* rc_text;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 1000:
|
||||
case MessageCode::LeftFlipperInputPressed:
|
||||
if (!TiltLockFlag)
|
||||
{
|
||||
FlipperL->Message(1, value);
|
||||
FlipperL->Message(MessageCode::TFlipperExtend, value);
|
||||
}
|
||||
break;
|
||||
case 1001:
|
||||
case MessageCode::LeftFlipperInputReleased:
|
||||
if (!TiltLockFlag)
|
||||
{
|
||||
FlipperL->Message(2, value);
|
||||
FlipperL->Message(MessageCode::TFlipperRetract, value);
|
||||
}
|
||||
break;
|
||||
case 1002:
|
||||
case MessageCode::RightFlipperInputPressed:
|
||||
if (!TiltLockFlag)
|
||||
{
|
||||
FlipperR->Message(1, value);
|
||||
FlipperR->Message(MessageCode::TFlipperExtend, value);
|
||||
}
|
||||
break;
|
||||
case 1003:
|
||||
case MessageCode::RightFlipperInputReleased:
|
||||
if (!TiltLockFlag)
|
||||
{
|
||||
FlipperR->Message(2, value);
|
||||
FlipperR->Message(MessageCode::TFlipperRetract, value);
|
||||
}
|
||||
break;
|
||||
case 1004:
|
||||
case 1005:
|
||||
case MessageCode::PlungerInputPressed:
|
||||
case MessageCode::PlungerInputReleased:
|
||||
Plunger->Message(code, value);
|
||||
break;
|
||||
case 1008:
|
||||
case 1009:
|
||||
case 1010:
|
||||
for (int i = 0; i < ComponentList->Count(); i++)
|
||||
case MessageCode::Pause:
|
||||
case MessageCode::Resume:
|
||||
case MessageCode::LooseFocus:
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(ComponentList->Get(i))->Message(code, value);
|
||||
component->Message(code, value);
|
||||
}
|
||||
break;
|
||||
case 1012:
|
||||
LightGroup->Message(14, 0.0);
|
||||
case MessageCode::ClearTiltLock:
|
||||
LightGroup->Message(MessageCode::TLightResetTimed, 0.0);
|
||||
if (TiltLockFlag)
|
||||
{
|
||||
TiltLockFlag = 0;
|
||||
@@ -377,36 +367,38 @@ int TPinballTable::Message(int code, float value)
|
||||
TiltTimeoutTimer = 0;
|
||||
}
|
||||
break;
|
||||
case 1013:
|
||||
LightGroup->Message(34, 0.0);
|
||||
LightGroup->Message(20, 0.0);
|
||||
Plunger->Message(1016, 0.0);
|
||||
if (Demo->ActiveFlag)
|
||||
rc_text = pinball::get_rc_string(30, 0);
|
||||
case MessageCode::StartGamePlayer1:
|
||||
LightGroup->Message(MessageCode::TLightGroupReset, 0.0);
|
||||
LightGroup->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||
Plunger->Message(MessageCode::PlungerStartFeedTimer, 0.0);
|
||||
if (Demo && Demo->ActiveFlag)
|
||||
rc_text = pb::get_rc_string(Msg::STRING131);
|
||||
else
|
||||
rc_text = pinball::get_rc_string(26, 0);
|
||||
pinball::InfoTextBox->Display(rc_text, -1.0);
|
||||
rc_text = pb::get_rc_string(Msg::STRING127);
|
||||
pb::InfoTextBox->Display(rc_text, -1.0);
|
||||
if (Demo)
|
||||
Demo->Message(1014, 0.0);
|
||||
Demo->Message(MessageCode::NewGame, 0.0);
|
||||
break;
|
||||
case 1014:
|
||||
case MessageCode::NewGame:
|
||||
if (EndGameTimeoutTimer)
|
||||
{
|
||||
timer::kill(EndGameTimeoutTimer);
|
||||
EndGame_timeout(0, this);
|
||||
pb::mode_change(1);
|
||||
pb::mode_change(GameModes::InGame);
|
||||
}
|
||||
if (LightShowTimer)
|
||||
{
|
||||
timer::kill(LightShowTimer);
|
||||
LightShowTimer = 0;
|
||||
Message(1013, 0.0);
|
||||
Message(MessageCode::StartGamePlayer1, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some of the control cheats persist across games.
|
||||
// Was this loose anti-cheat by design?
|
||||
CheatsUsed = 0;
|
||||
Message(1024, 0.0);
|
||||
auto ball = static_cast<TBall*>(BallList->Get(0));
|
||||
Message(MessageCode::Reset, 0.0);
|
||||
auto ball = BallList[0];
|
||||
ball->Position.Y = 0.0;
|
||||
ball->Position.X = 0.0;
|
||||
ball->Position.Z = -0.8f;
|
||||
@@ -439,50 +431,55 @@ int TPinballTable::Message(int code, float value)
|
||||
scorePtr->BallCount = MaxBallCount;
|
||||
scorePtr->ExtraBalls = ExtraBalls;
|
||||
scorePtr->BallLockedCounter = BallLockedCounter;
|
||||
scorePtr->Unknown2 = ScoreSpecial3;
|
||||
scorePtr->JackpotScore = JackpotScore;
|
||||
}
|
||||
|
||||
BallCount = MaxBallCount;
|
||||
Sound::Idle();
|
||||
ChangeBallCount(BallCount);
|
||||
score::set(ScorePlayerNumber1, CurrentPlayer + 1);
|
||||
score::update(ScorePlayerNumber1);
|
||||
Sound::Idle();
|
||||
|
||||
for (auto scoreIndex = 4 - PlayerCount; scoreIndex > 0; scoreIndex--)
|
||||
{
|
||||
score::set(PlayerScores[scoreIndex].ScoreStruct, -1);
|
||||
}
|
||||
|
||||
Sound::Idle();
|
||||
ScoreSpecial3Flag = 0;
|
||||
ScoreSpecial2Flag = 0;
|
||||
JackpotScoreFlag = false;
|
||||
BonusScoreFlag = false;
|
||||
UnknownP71 = 0;
|
||||
pinball::InfoTextBox->Clear();
|
||||
Sound::Idle();
|
||||
pinball::MissTextBox->Clear();
|
||||
Sound::Idle();
|
||||
LightGroup->Message(28, 0.2f);
|
||||
auto time = loader::play_sound(SoundIndex1);
|
||||
pb::InfoTextBox->Clear();
|
||||
pb::MissTextBox->Clear();
|
||||
LightGroup->Message(MessageCode::TLightGroupLightShowAnimation, 0.2f);
|
||||
auto time = loader::play_sound(SoundIndex1, nullptr, "TPinballTable2");
|
||||
if (time < 0)
|
||||
time = 5.0f;
|
||||
LightShowTimer = timer::set(time, this, LightShow_timeout);
|
||||
}
|
||||
|
||||
if (pb::FullTiltMode)
|
||||
{
|
||||
// Multi-ball is FT exclusive feature, at least for now.
|
||||
MultiballFlag = true;
|
||||
JackpotScore = 500000;
|
||||
}
|
||||
midi::play_track(MidiTracks::Track1, true);
|
||||
break;
|
||||
case 1018:
|
||||
case MessageCode::PlungerRelaunchBall:
|
||||
if (ReplayTimer)
|
||||
timer::kill(ReplayTimer);
|
||||
ReplayTimer = timer::set(floor(value), this, replay_timer_callback);
|
||||
ReplayActiveFlag = 1;
|
||||
break;
|
||||
case 1021:
|
||||
case MessageCode::SwitchToNextPlayer:
|
||||
{
|
||||
if (PlayerCount <= 1)
|
||||
{
|
||||
char* textboxText;
|
||||
const char* textboxText;
|
||||
if (Demo->ActiveFlag)
|
||||
textboxText = pinball::get_rc_string(30, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING131);
|
||||
else
|
||||
textboxText = pinball::get_rc_string(26, 0);
|
||||
pinball::InfoTextBox->Display(textboxText, -1.0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING127);
|
||||
pb::InfoTextBox->Display(textboxText, -1.0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -496,14 +493,14 @@ int TPinballTable::Message(int code, float value)
|
||||
PlayerScores[CurrentPlayer].BallCount = BallCount;
|
||||
PlayerScores[CurrentPlayer].ExtraBalls = ExtraBalls;
|
||||
PlayerScores[CurrentPlayer].BallLockedCounter = BallLockedCounter;
|
||||
PlayerScores[CurrentPlayer].Unknown2 = ScoreSpecial3;
|
||||
PlayerScores[CurrentPlayer].JackpotScore = JackpotScore;
|
||||
|
||||
CurScore = nextScorePtr->Score;
|
||||
CurScoreE9 = nextScorePtr->ScoreE9Part;
|
||||
BallCount = nextScorePtr->BallCount;
|
||||
ExtraBalls = nextScorePtr->ExtraBalls;
|
||||
BallLockedCounter = nextScorePtr->BallLockedCounter;
|
||||
ScoreSpecial3 = nextScorePtr->Unknown2;
|
||||
JackpotScore = nextScorePtr->JackpotScore;
|
||||
|
||||
CurScoreStruct = nextScorePtr->ScoreStruct;
|
||||
score::set(CurScoreStruct, CurScore);
|
||||
@@ -513,60 +510,60 @@ int TPinballTable::Message(int code, float value)
|
||||
score::set(ScorePlayerNumber1, nextPlayer + 1);
|
||||
score::update(ScorePlayerNumber1);
|
||||
|
||||
for (int i = 0; i < ComponentList->Count(); i++)
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(ComponentList->Get(i))->Message(1020, static_cast<float>(nextPlayer));
|
||||
component->Message(MessageCode::PlayerChanged, static_cast<float>(nextPlayer));
|
||||
}
|
||||
|
||||
char* textboxText = nullptr;
|
||||
const char* textboxText = nullptr;
|
||||
switch (nextPlayer)
|
||||
{
|
||||
case 0:
|
||||
if (Demo->ActiveFlag)
|
||||
textboxText = pinball::get_rc_string(30, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING131);
|
||||
else
|
||||
textboxText = pinball::get_rc_string(26, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING127);
|
||||
break;
|
||||
case 1:
|
||||
if (Demo->ActiveFlag)
|
||||
textboxText = pinball::get_rc_string(31, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING132);
|
||||
else
|
||||
textboxText = pinball::get_rc_string(27, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING128);
|
||||
break;
|
||||
case 2:
|
||||
if (Demo->ActiveFlag)
|
||||
textboxText = pinball::get_rc_string(32, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING133);
|
||||
else
|
||||
textboxText = pinball::get_rc_string(28, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING129);
|
||||
break;
|
||||
case 3:
|
||||
if (Demo->ActiveFlag)
|
||||
textboxText = pinball::get_rc_string(33, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING134);
|
||||
else
|
||||
textboxText = pinball::get_rc_string(29, 0);
|
||||
textboxText = pb::get_rc_string(Msg::STRING130);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (textboxText != nullptr)
|
||||
pinball::InfoTextBox->Display(textboxText, -1);
|
||||
ScoreSpecial3Flag = 0;
|
||||
ScoreSpecial2Flag = 0;
|
||||
pb::InfoTextBox->Display(textboxText, -1);
|
||||
JackpotScoreFlag = false;
|
||||
BonusScoreFlag = false;
|
||||
UnknownP71 = 0;
|
||||
CurrentPlayer = nextPlayer;
|
||||
}
|
||||
break;
|
||||
case 1022:
|
||||
loader::play_sound(SoundIndex2);
|
||||
pinball::MissTextBox->Clear();
|
||||
pinball::InfoTextBox->Display(pinball::get_rc_string(34, 0), -1.0);
|
||||
case MessageCode::GameOver:
|
||||
loader::play_sound(SoundIndex2, nullptr, "TPinballTable3");
|
||||
pb::MissTextBox->Clear();
|
||||
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING135), -1.0);
|
||||
EndGameTimeoutTimer = timer::set(3.0, this, EndGame_timeout);
|
||||
break;
|
||||
case 1024:
|
||||
for (int i = 0; i < ComponentList->Count(); i++)
|
||||
case MessageCode::Reset:
|
||||
for (auto component : ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(ComponentList->Get(i))->Message(1024, 0);
|
||||
component->Message(MessageCode::Reset, 0);
|
||||
}
|
||||
if (ReplayTimer)
|
||||
timer::kill(ReplayTimer);
|
||||
@@ -574,21 +571,21 @@ int TPinballTable::Message(int code, float value)
|
||||
if (LightShowTimer)
|
||||
{
|
||||
timer::kill(LightShowTimer);
|
||||
LightGroup->Message(34, 0.0);
|
||||
LightGroup->Message(MessageCode::TLightGroupReset, 0.0);
|
||||
}
|
||||
LightShowTimer = 0;
|
||||
ScoreMultiplier = 0;
|
||||
ScoreAdded = 0;
|
||||
ScoreSpecial1 = 0;
|
||||
ScoreSpecial2 = 10000;
|
||||
ScoreSpecial2Flag = 0;
|
||||
ScoreSpecial3 = 20000;
|
||||
ScoreSpecial3Flag = 0;
|
||||
ReflexShotScore = 0;
|
||||
BonusScore = 10000;
|
||||
BonusScoreFlag = false;
|
||||
JackpotScore = 20000;
|
||||
JackpotScoreFlag = false;
|
||||
UnknownP71 = 0;
|
||||
ExtraBalls = 0;
|
||||
UnknownP75 = 0;
|
||||
MultiballCount = 0;
|
||||
BallLockedCounter = 0;
|
||||
MultiballFlag = 0;
|
||||
MultiballFlag = false;
|
||||
UnknownP78 = 0;
|
||||
ReplayActiveFlag = 0;
|
||||
ReplayTimer = 0;
|
||||
@@ -602,27 +599,96 @@ int TPinballTable::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
TBall* TPinballTable::AddBall(vector2 position)
|
||||
{
|
||||
TBall* ball = nullptr;
|
||||
|
||||
for (auto curBall : BallList)
|
||||
{
|
||||
if (!curBall->ActiveFlag)
|
||||
{
|
||||
ball = curBall;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ball != nullptr)
|
||||
{
|
||||
ball->ActiveFlag = 1;
|
||||
ball->Position.Z = ball->Radius;
|
||||
ball->Direction = {};
|
||||
ball->Speed = 0;
|
||||
ball->TimeDelta = 0;
|
||||
ball->EdgeCollisionCount = 0;
|
||||
ball->CollisionFlag = 0;
|
||||
ball->CollisionMask = 1;
|
||||
ball->CollisionComp = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BallList.size() >= 20)
|
||||
return nullptr;
|
||||
ball = new TBall(this, -1);
|
||||
BallList.push_back(ball);
|
||||
}
|
||||
|
||||
ball->Position.X = position.X;
|
||||
ball->Position.Y = position.Y;
|
||||
ball->PrevPosition = ball->Position;
|
||||
ball->StuckCounter = 0;
|
||||
ball->LastActiveTime = pb::time_ticks;
|
||||
|
||||
return ball;
|
||||
}
|
||||
|
||||
int TPinballTable::BallCountInRect(const RectF& rect)
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto ball : BallList)
|
||||
{
|
||||
if (ball->ActiveFlag &&
|
||||
ball->Position.X >= rect.XMin &&
|
||||
ball->Position.Y >= rect.YMin &&
|
||||
ball->Position.X <= rect.XMax &&
|
||||
ball->Position.Y <= rect.YMax)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int TPinballTable::BallCountInRect(const vector2& pos, float margin)
|
||||
{
|
||||
RectF rect{};
|
||||
rect.XMin = pos.X - margin;
|
||||
rect.XMax = pos.X + margin;
|
||||
rect.YMin = pos.Y - margin;
|
||||
rect.YMax = pos.Y + margin;
|
||||
return BallCountInRect(rect);
|
||||
}
|
||||
|
||||
void TPinballTable::EndGame_timeout(int timerId, void* caller)
|
||||
{
|
||||
auto table = static_cast<TPinballTable*>(caller);
|
||||
table->EndGameTimeoutTimer = 0;
|
||||
pb::end_game();
|
||||
|
||||
for (int i = 0; i < table->ComponentList->Count(); i++)
|
||||
for (auto component : table->ComponentList)
|
||||
{
|
||||
static_cast<TPinballComponent*>(table->ComponentList->Get(i))->Message(1022, 0);
|
||||
component->Message(MessageCode::GameOver, 0);
|
||||
}
|
||||
if (table->Demo)
|
||||
table->Demo->Message(1022, 0.0);
|
||||
control::handler(67, pinball::MissTextBox);
|
||||
pinball::InfoTextBox->Display(pinball::get_rc_string(24, 0), -1.0);
|
||||
table->Demo->Message(MessageCode::GameOver, 0.0);
|
||||
control::handler(MessageCode::ControlMissionStarted, pb::MissTextBox);
|
||||
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING125), -1.0);
|
||||
}
|
||||
|
||||
void TPinballTable::LightShow_timeout(int timerId, void* caller)
|
||||
{
|
||||
auto table = static_cast<TPinballTable*>(caller);
|
||||
table->LightShowTimer = 0;
|
||||
table->Message(1013, 0.0);
|
||||
table->Message(MessageCode::StartGamePlayer1, 0.0);
|
||||
}
|
||||
|
||||
void TPinballTable::replay_timer_callback(int timerId, void* caller)
|
||||
@@ -635,14 +701,14 @@ void TPinballTable::replay_timer_callback(int timerId, void* caller)
|
||||
void TPinballTable::tilt_timeout(int timerId, void* caller)
|
||||
{
|
||||
auto table = static_cast<TPinballTable*>(caller);
|
||||
vector_type vec{};
|
||||
vector2 vec{};
|
||||
|
||||
table->TiltTimeoutTimer = 0;
|
||||
if (table->TiltLockFlag)
|
||||
{
|
||||
for (int i = 0; i < table->BallList->Count(); i++)
|
||||
for (auto ball : table->BallList)
|
||||
{
|
||||
table->Drain->Collision(static_cast<TBall*>(table->BallList->Get(i)), &vec, &vec, 0.0, nullptr);
|
||||
table->Drain->Collision(ball, &vec, &vec, 0.0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "maths.h"
|
||||
#include "TPinballComponent.h"
|
||||
|
||||
class TBall;
|
||||
struct scoreStruct;
|
||||
class TFlipper;
|
||||
class TPlunger;
|
||||
class TDrain;
|
||||
class TDemo;
|
||||
class objlist_class;
|
||||
class TLightGroup;
|
||||
|
||||
struct score_struct_super
|
||||
@@ -15,7 +16,7 @@ struct score_struct_super
|
||||
scoreStruct* ScoreStruct;
|
||||
int Score;
|
||||
int ScoreE9Part;
|
||||
int Unknown2;
|
||||
int JackpotScore;
|
||||
int BallCount;
|
||||
int ExtraBalls;
|
||||
int BallLockedCounter;
|
||||
@@ -33,7 +34,10 @@ public:
|
||||
void ChangeBallCount(int count);
|
||||
void tilt(float time);
|
||||
void port_draw() override;
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
TBall* AddBall(vector2 position);
|
||||
int BallCountInRect(const RectF& rect);
|
||||
int BallCountInRect(const vector2& pos, float margin);
|
||||
|
||||
static void EndGame_timeout(int timerId, void* caller);
|
||||
static void LightShow_timeout(int timerId, void* caller);
|
||||
@@ -45,54 +49,54 @@ public:
|
||||
scoreStruct* CurScoreStruct;
|
||||
scoreStruct* ScoreBallcount;
|
||||
scoreStruct* ScorePlayerNumber1;
|
||||
int CheatsUsed;
|
||||
int SoundIndex1;
|
||||
int SoundIndex2;
|
||||
int SoundIndex3;
|
||||
int BallInSink;
|
||||
int CurScore;
|
||||
int CurScoreE9;
|
||||
int CheatsUsed{};
|
||||
int SoundIndex1{};
|
||||
int SoundIndex2{};
|
||||
int SoundIndex3{};
|
||||
int BallInDrainFlag;
|
||||
int CurScore{};
|
||||
int CurScoreE9{};
|
||||
int LightShowTimer;
|
||||
int EndGameTimeoutTimer;
|
||||
int TiltTimeoutTimer;
|
||||
score_struct_super PlayerScores[4];
|
||||
score_struct_super PlayerScores[4]{};
|
||||
int PlayerCount;
|
||||
int CurrentPlayer;
|
||||
TPlunger* Plunger;
|
||||
TDrain* Drain;
|
||||
TDemo* Demo;
|
||||
int XOffset;
|
||||
int YOffset;
|
||||
int Width;
|
||||
int Height;
|
||||
objlist_class* ComponentList;
|
||||
objlist_class* BallList;
|
||||
TDemo* Demo{};
|
||||
int XOffset{};
|
||||
int YOffset{};
|
||||
int Width{};
|
||||
int Height{};
|
||||
std::vector<TPinballComponent*> ComponentList;
|
||||
std::vector<TBall*> BallList;
|
||||
std::vector<TFlipper*> FlipperList;
|
||||
TLightGroup* LightGroup;
|
||||
float GravityDirVectMult;
|
||||
float GravityAngleX;
|
||||
float GravityAnglY;
|
||||
float CollisionCompOffset;
|
||||
float PlungerPositionX;
|
||||
float PlungerPositionY;
|
||||
int ScoreMultiplier;
|
||||
int ScoreAdded;
|
||||
int ScoreSpecial1;
|
||||
int ScoreSpecial2;
|
||||
int ScoreSpecial2Flag;
|
||||
int ScoreSpecial3;
|
||||
int ScoreSpecial3Flag;
|
||||
int UnknownP71;
|
||||
int BallCount;
|
||||
float GravityDirVectMult{};
|
||||
float GravityAngleX{};
|
||||
float GravityAnglY{};
|
||||
float CollisionCompOffset{};
|
||||
vector2 PlungerPosition{};
|
||||
int ScoreMultiplier{};
|
||||
int ScoreAdded{};
|
||||
int ReflexShotScore{};
|
||||
int BonusScore{};
|
||||
bool BonusScoreFlag{};
|
||||
int JackpotScore{};
|
||||
bool JackpotScoreFlag{};
|
||||
int UnknownP71{};
|
||||
int BallCount{};
|
||||
int MaxBallCount;
|
||||
int ExtraBalls;
|
||||
int UnknownP75;
|
||||
int BallLockedCounter;
|
||||
int MultiballFlag;
|
||||
int UnknownP78;
|
||||
int ReplayActiveFlag;
|
||||
int ExtraBalls{};
|
||||
int MultiballCount{};
|
||||
int BallLockedCounter{};
|
||||
bool MultiballFlag;
|
||||
int UnknownP78{};
|
||||
int ReplayActiveFlag{};
|
||||
int ReplayTimer;
|
||||
int UnknownP81;
|
||||
int UnknownP82;
|
||||
int UnknownP81{};
|
||||
int UnknownP82{};
|
||||
int TiltLockFlag;
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "TBall.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
@@ -24,146 +23,175 @@ TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||
SoundIndexP2 = visual.SoundIndex3;
|
||||
HardHitSoundId = visual.Kicker.HardHitSoundId;
|
||||
Threshold = 1000000000.0;
|
||||
MaxPullback = 100;
|
||||
|
||||
// FT:MaxPullback = 50; 3DPB: MaxPullback = 100, PullbackIncrement is floored.
|
||||
if (pb::FullTiltMode)
|
||||
{
|
||||
MaxPullback = 50;
|
||||
PullbackIncrement = MaxPullback / (ListBitmap->size() * 8.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxPullback = 100;
|
||||
PullbackIncrement = std::floor(MaxPullback / (ListBitmap->size() * 8.0f));
|
||||
}
|
||||
|
||||
Elasticity = 0.5f;
|
||||
Smoothness = 0.5f;
|
||||
PullbackIncrement = static_cast<int>(100.0 / (ListBitmap->Count() * 8.0));
|
||||
Unknown4F = 0.025f;
|
||||
|
||||
PullbackDelay = 0.025f;
|
||||
float* floatArr = loader::query_float_attribute(groupIndex, 0, 601);
|
||||
table->PlungerPositionX = floatArr[0];
|
||||
table->PlungerPositionY = floatArr[1];
|
||||
table->PlungerPosition = {floatArr[0], floatArr[1]};
|
||||
}
|
||||
|
||||
void TPlunger::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TPlunger::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (PinballTable->TiltLockFlag)
|
||||
Message(1017, 0.0);
|
||||
coef = static_cast<float>(rand()) * 0.00003051850947599719f * Boost * 0.1f + Boost;
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, coef);
|
||||
if (PinballTable->TiltLockFlag || SomeCounter > 0)
|
||||
{
|
||||
auto boost = RandFloat() * MaxPullback * 0.1f + MaxPullback;
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 0, boost);
|
||||
if (SomeCounter)
|
||||
SomeCounter--;
|
||||
Message(MessageCode::PlungerInputReleased, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto boost = RandFloat() * Boost * 0.1f + Boost;
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, boost);
|
||||
}
|
||||
}
|
||||
|
||||
int TPlunger::Message(int code, float value)
|
||||
int TPlunger::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 1004:
|
||||
if (!PullbackTimer_)
|
||||
case MessageCode::PlungerInputPressed:
|
||||
if (!PullbackStartedFlag && (!pb::FullTiltMode || PinballTable->MultiballCount > 0 && !PinballTable->
|
||||
TiltLockFlag))
|
||||
{
|
||||
PullbackStartedFlag = true;
|
||||
Boost = 0.0;
|
||||
Threshold = 1000000000.0;
|
||||
loader::play_sound(HardHitSoundId);
|
||||
loader::play_sound(HardHitSoundId, this, "TPlunger1");
|
||||
PullbackTimer(0, this);
|
||||
}
|
||||
return 0;
|
||||
case 1005:
|
||||
case 1009:
|
||||
case 1010:
|
||||
{
|
||||
Threshold = 0.0;
|
||||
if (PullbackTimer_)
|
||||
timer::kill(PullbackTimer_);
|
||||
PullbackTimer_ = 0;
|
||||
if (code == 1005)
|
||||
loader::play_sound(SoundIndexP2);
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
auto zMap = static_cast<zmap_header_type*>(ListZMap->Get(0));
|
||||
render::sprite_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - PinballTable->XOffset,
|
||||
bmp->YPosition - PinballTable->YOffset);
|
||||
|
||||
timer::set(Unknown4F, this, PlungerReleasedTimer);
|
||||
break;
|
||||
}
|
||||
case 1015:
|
||||
{
|
||||
auto ball = static_cast<TBall*>(PinballTable->ComponentList->Get(0));
|
||||
ball->Message(1024, 0.0);
|
||||
ball->Position.X = PinballTable->PlungerPositionX;
|
||||
ball->Position.Y = PinballTable->PlungerPositionY;
|
||||
ball->ActiveFlag = 1;
|
||||
PinballTable->BallInSink = 0;
|
||||
pb::tilt_no_more();
|
||||
control::handler(code, this);
|
||||
return 0;
|
||||
}
|
||||
case 1016:
|
||||
if (BallFeedTimer_)
|
||||
timer::kill(BallFeedTimer_);
|
||||
BallFeedTimer_ = timer::set(0.95999998f, this, BallFeedTimer);
|
||||
loader::play_sound(SoundIndexP1);
|
||||
control::handler(code, this);
|
||||
return 0;
|
||||
case 1017:
|
||||
Threshold = 0.0;
|
||||
Boost = static_cast<float>(MaxPullback);
|
||||
timer::set(0.2f, this, PlungerReleasedTimer);
|
||||
break;
|
||||
case 1024:
|
||||
case MessageCode::PlungerFeedBall:
|
||||
{
|
||||
if (BallFeedTimer_)
|
||||
timer::kill(BallFeedTimer_);
|
||||
BallFeedTimer_ = 0;
|
||||
if (PinballTable->BallCountInRect(PinballTable->PlungerPosition, PinballTable->CollisionCompOffset * 1.2f))
|
||||
{
|
||||
timer::set(1.0f, this, BallFeedTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ball = PinballTable->AddBall(PinballTable->PlungerPosition);
|
||||
assertm(ball, "Failure to create ball in plunger");
|
||||
PinballTable->MultiballCount++;
|
||||
PinballTable->BallInDrainFlag = 0;
|
||||
pb::tilt_no_more();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MessageCode::PlungerStartFeedTimer:
|
||||
timer::set(0.95999998f, this, BallFeedTimer);
|
||||
loader::play_sound(SoundIndexP1, this, "TPlunger2");
|
||||
break;
|
||||
case MessageCode::PlungerLaunchBall:
|
||||
PullbackStartedFlag = true;
|
||||
Boost = MaxPullback;
|
||||
Message(MessageCode::PlungerInputReleased, 0.0f);
|
||||
break;
|
||||
case MessageCode::PlungerRelaunchBall:
|
||||
SomeCounter++;
|
||||
timer::set(value, this, BallFeedTimer);
|
||||
loader::play_sound(SoundIndexP1, this, "TPlunger2_1");
|
||||
PullbackStartedFlag = true;
|
||||
PullbackTimer(0, this);
|
||||
break;
|
||||
case MessageCode::PlayerChanged:
|
||||
PullbackStartedFlag = false;
|
||||
Boost = 0.0f;
|
||||
Threshold = 1000000000.0f;
|
||||
SomeCounter = 0;
|
||||
timer::kill(BallFeedTimer);
|
||||
timer::kill(PullbackTimer);
|
||||
timer::kill(ReleasedTimer);
|
||||
break;
|
||||
case MessageCode::SetTiltLock:
|
||||
SomeCounter = 0;
|
||||
timer::kill(BallFeedTimer);
|
||||
break;
|
||||
case MessageCode::PlungerInputReleased:
|
||||
case MessageCode::Resume:
|
||||
case MessageCode::LooseFocus:
|
||||
if (PullbackStartedFlag && !SomeCounter)
|
||||
{
|
||||
PullbackStartedFlag = false;
|
||||
Threshold = 0.0;
|
||||
if (PullbackTimer_)
|
||||
timer::kill(PullbackTimer_);
|
||||
PullbackTimer_ = 0;
|
||||
if (code == 1005)
|
||||
loader::play_sound(SoundIndexP2);
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
auto zMap = static_cast<zmap_header_type*>(ListZMap->Get(0));
|
||||
render::sprite_set(
|
||||
RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - PinballTable->XOffset,
|
||||
bmp->YPosition - PinballTable->YOffset);
|
||||
|
||||
timer::set(Unknown4F, this, PlungerReleasedTimer);
|
||||
break;
|
||||
loader::play_sound(SoundIndexP2, this, "TPlunger3");
|
||||
SpriteSet(0);
|
||||
timer::set(PullbackDelay, this, ReleasedTimer);
|
||||
}
|
||||
break;
|
||||
case MessageCode::Reset:
|
||||
{
|
||||
PullbackStartedFlag = false;
|
||||
Boost = 0.0f;
|
||||
Threshold = 1000000000.0f;
|
||||
SomeCounter = 0;
|
||||
|
||||
timer::kill(BallFeedTimer);
|
||||
timer::kill(PullbackTimer);
|
||||
timer::kill(ReleasedTimer);
|
||||
|
||||
SpriteSet(0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
control::handler(code, this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TPlunger::BallFeedTimer(int timerId, void* caller)
|
||||
{
|
||||
auto plunger = static_cast<TPlunger*>(caller);
|
||||
plunger->PullbackTimer_ = 0;
|
||||
plunger->Message(1015, 0.0);
|
||||
plunger->Message(MessageCode::PlungerFeedBall, 0.0);
|
||||
}
|
||||
|
||||
void TPlunger::PullbackTimer(int timerId, void* caller)
|
||||
{
|
||||
auto plunger = static_cast<TPlunger*>(caller);
|
||||
plunger->Boost += static_cast<float>(plunger->PullbackIncrement);
|
||||
if (plunger->Boost <= static_cast<float>(plunger->MaxPullback))
|
||||
plunger->Boost += plunger->PullbackIncrement;
|
||||
if (plunger->Boost <= plunger->MaxPullback)
|
||||
{
|
||||
plunger->PullbackTimer_ = timer::set(plunger->Unknown4F, plunger, PullbackTimer);
|
||||
if (plunger->SomeCounter)
|
||||
{
|
||||
plunger->PullbackTimer_ = timer::set(plunger->PullbackDelay / 4.0f, plunger, PullbackTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
plunger->PullbackTimer_ = timer::set(plunger->PullbackDelay, plunger, PullbackTimer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plunger->PullbackTimer_ = 0;
|
||||
plunger->Boost = static_cast<float>(plunger->MaxPullback);
|
||||
plunger->Boost = plunger->MaxPullback;
|
||||
}
|
||||
|
||||
int index = static_cast<int>(floor(
|
||||
static_cast<float>(plunger->ListBitmap->Count() - 1) *
|
||||
(plunger->Boost / static_cast<float>(plunger->MaxPullback))));
|
||||
auto bmp = static_cast<gdrv_bitmap8*>(plunger->ListBitmap->Get(index));
|
||||
auto zMap = static_cast<zmap_header_type*>(plunger->ListZMap->Get(index));
|
||||
render::sprite_set(
|
||||
plunger->RenderSprite,
|
||||
bmp,
|
||||
zMap,
|
||||
bmp->XPosition - plunger->PinballTable->XOffset,
|
||||
bmp->YPosition - plunger->PinballTable->YOffset);
|
||||
static_cast<float>(plunger->ListBitmap->size() - 1) *
|
||||
(plunger->Boost / plunger->MaxPullback)));
|
||||
plunger->SpriteSet(index);
|
||||
}
|
||||
|
||||
void TPlunger::PlungerReleasedTimer(int timerId, void* caller)
|
||||
void TPlunger::ReleasedTimer(int timerId, void* caller)
|
||||
{
|
||||
auto plunger = static_cast<TPlunger*>(caller);
|
||||
plunger->Threshold = 1000000000.0;
|
||||
|
||||
@@ -7,19 +7,21 @@ class TPlunger :
|
||||
public:
|
||||
TPlunger(TPinballTable* table, int groupIndex);
|
||||
~TPlunger() override = default;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
int Message(int code, float value) override;
|
||||
int Message(MessageCode code, float value) override;
|
||||
|
||||
static void BallFeedTimer(int timerId, void* caller);
|
||||
static void PullbackTimer(int timerId, void* caller);
|
||||
static void PlungerReleasedTimer(int timerId, void* caller);
|
||||
static void ReleasedTimer(int timerId, void* caller);
|
||||
|
||||
int PullbackTimer_;
|
||||
int BallFeedTimer_;
|
||||
int MaxPullback;
|
||||
int PullbackIncrement;
|
||||
float Unknown4F;
|
||||
float MaxPullback;
|
||||
float PullbackIncrement;
|
||||
float PullbackDelay;
|
||||
int SoundIndexP1;
|
||||
int SoundIndexP2;
|
||||
bool PullbackStartedFlag{};
|
||||
int SomeCounter{};
|
||||
};
|
||||
@@ -7,42 +7,41 @@
|
||||
#include "render.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TPopupTarget::TPopupTarget(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
{
|
||||
this->Timer = 0;
|
||||
this->TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||
Timer = 0;
|
||||
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||
}
|
||||
|
||||
int TPopupTarget::Message(int code, float value)
|
||||
int TPopupTarget::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 49:
|
||||
this->ActiveFlag = 0;
|
||||
render::sprite_set_bitmap(this->RenderSprite, nullptr);
|
||||
case MessageCode::TPopupTargetDisable:
|
||||
ActiveFlag = 0;
|
||||
SpriteSet(-1);
|
||||
break;
|
||||
case 50:
|
||||
this->Timer = timer::set(this->TimerTime, this, TimerExpired);
|
||||
case MessageCode::TPopupTargetEnable:
|
||||
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||
break;
|
||||
case 1020:
|
||||
this->PlayerMessagefieldBackup[this->PinballTable->CurrentPlayer] = this->MessageField;
|
||||
this->MessageField = this->PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
||||
TPopupTarget::Message(50 - (MessageField != 0), 0.0);
|
||||
case MessageCode::PlayerChanged:
|
||||
PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField;
|
||||
MessageField = PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
||||
TPopupTarget::Message(MessageField ? MessageCode::TPopupTargetDisable : MessageCode::TPopupTargetEnable, 0.0);
|
||||
break;
|
||||
case 1024:
|
||||
{
|
||||
this->MessageField = 0;
|
||||
int* playerPtr = this->PlayerMessagefieldBackup;
|
||||
for (auto index = 0; index < this->PinballTable->PlayerCount; ++index)
|
||||
case MessageCode::Reset:
|
||||
{
|
||||
MessageField = 0;
|
||||
int* playerPtr = PlayerMessagefieldBackup;
|
||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||
{
|
||||
*playerPtr = 0;
|
||||
++playerPtr;
|
||||
}
|
||||
|
||||
if (this->Timer)
|
||||
timer::kill(this->Timer);
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
TimerExpired(0, this);
|
||||
break;
|
||||
}
|
||||
@@ -52,37 +51,26 @@ int TPopupTarget::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TPopupTarget::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 3)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TPopupTarget::get_scoring(int index)
|
||||
{
|
||||
return index < 3 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TPopupTarget::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void TPopupTarget::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge)
|
||||
{
|
||||
if (this->PinballTable->TiltLockFlag)
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
maths::basic_collision(ball, nextPosition, direction, this->Elasticity, this->Smoothness, 1000000000.0, 0.0);
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
||||
}
|
||||
else if (maths::basic_collision(
|
||||
ball,
|
||||
nextPosition,
|
||||
direction,
|
||||
this->Elasticity,
|
||||
this->Smoothness,
|
||||
this->Threshold,
|
||||
this->Boost) > this->Threshold)
|
||||
Elasticity,
|
||||
Smoothness,
|
||||
Threshold,
|
||||
Boost) > Threshold)
|
||||
{
|
||||
if (this->HardHitSoundId)
|
||||
loader::play_sound(this->HardHitSoundId);
|
||||
this->Message(49, 0.0);
|
||||
control::handler(63, this);
|
||||
if (HardHitSoundId)
|
||||
loader::play_sound(HardHitSoundId, this, "TPopupTarget1");
|
||||
Message(MessageCode::TPopupTargetDisable, 0.0);
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +79,10 @@ void TPopupTarget::TimerExpired(int timerId, void* caller)
|
||||
auto target = static_cast<TPopupTarget*>(caller);
|
||||
target->Timer = 0;
|
||||
target->ActiveFlag = 1;
|
||||
render::sprite_set_bitmap(target->RenderSprite, static_cast<gdrv_bitmap8*>(target->ListBitmap->Get(0)));
|
||||
target->SpriteSet(0);
|
||||
if (timerId)
|
||||
{
|
||||
if (target->SoftHitSoundId)
|
||||
loader::play_sound(target->SoftHitSoundId);
|
||||
loader::play_sound(target->SoftHitSoundId, target, "TPopupTarget2");
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,13 @@ class TPopupTarget :
|
||||
{
|
||||
public:
|
||||
TPopupTarget(TPinballTable* table, int groupIndex);
|
||||
int Message(int code, float value) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
|
||||
int Timer;
|
||||
float TimerTime;
|
||||
int Scores[3];
|
||||
int PlayerMessagefieldBackup[4];
|
||||
int PlayerMessagefieldBackup[4]{};
|
||||
};
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "TBall.h"
|
||||
#include "TEdgeSegment.h"
|
||||
#include "TLine.h"
|
||||
@@ -15,76 +14,59 @@
|
||||
TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||
{
|
||||
visualStruct visual{};
|
||||
vector_type end{}, start{}, *end2, *start2, *start3, *end3;
|
||||
vector2 wall1End{}, wall1Start{}, wall2Start{}, wall2End{};
|
||||
|
||||
MessageField = 0;
|
||||
UnusedBaseFlag = 1;
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
CollisionGroup = visual.CollisionGroup;
|
||||
|
||||
auto floatArr1 = loader::query_float_attribute(groupIndex, 0, 701);
|
||||
if (floatArr1)
|
||||
BallFieldMult = *floatArr1;
|
||||
else
|
||||
BallFieldMult = 0.2f;
|
||||
auto floatArr2 = loader::query_float_attribute(groupIndex, 0, 1305);
|
||||
if (floatArr2)
|
||||
RampFlag1 = static_cast<int>(floor(*floatArr2));
|
||||
else
|
||||
RampFlag1 = 0;
|
||||
BallFieldMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
||||
BallZOffsetFlag = static_cast<int>(loader::query_float_attribute(groupIndex, 0, 1305, 0));
|
||||
|
||||
auto floatArr3Plane = loader::query_float_attribute(groupIndex, 0, 1300);
|
||||
RampPlaneCount = static_cast<int>(floor(*floatArr3Plane));
|
||||
RampPlane = reinterpret_cast<ramp_plane_type*>(floatArr3Plane + 1);
|
||||
|
||||
auto floatArr4 = loader::query_float_attribute(groupIndex, 0, 1303);
|
||||
end.X = floatArr4[2];
|
||||
end.Y = floatArr4[3];
|
||||
start.X = floatArr4[4];
|
||||
start.Y = floatArr4[5];
|
||||
Line1 = new TLine(this, &ActiveFlag, 1 << static_cast<int>(floor(floatArr4[0])), &start, &end);
|
||||
EdgeList->Add(Line1);
|
||||
if (Line1)
|
||||
{
|
||||
Line1->WallValue = nullptr;
|
||||
Line1->place_in_grid();
|
||||
}
|
||||
auto wall0Arr = loader::query_float_attribute(groupIndex, 0, 1303);
|
||||
auto wall0CollisionGroup = 1 << static_cast<int>(floor(wall0Arr[0]));
|
||||
auto wall0Pts = reinterpret_cast<wall_point_type*>(wall0Arr + 2);
|
||||
Line1 = new TLine(this, &ActiveFlag, wall0CollisionGroup, wall0Pts->Pt1, wall0Pts->Pt0);
|
||||
Line1->WallValue = nullptr;
|
||||
Line1->place_in_grid(&AABB);
|
||||
EdgeList.push_back(Line1);
|
||||
|
||||
auto floatArr5WallPoint = loader::query_float_attribute(groupIndex, 0, 1301);
|
||||
Wall1PointFirst = 1 << static_cast<int>(floor(floatArr5WallPoint[0]));
|
||||
auto wallPt1_2 = static_cast<int>(floor(floatArr5WallPoint[1]));
|
||||
Wall1PointLast = floatArr5WallPoint[7];
|
||||
auto wall1Arr = loader::query_float_attribute(groupIndex, 0, 1301);
|
||||
Wall1CollisionGroup = 1 << static_cast<int>(floor(wall1Arr[0]));
|
||||
auto wall1Enabled = static_cast<int>(floor(wall1Arr[1]));
|
||||
Wall1BallOffset = wall1Arr[7];
|
||||
maths::find_closest_edge(
|
||||
RampPlane,
|
||||
RampPlaneCount,
|
||||
reinterpret_cast<wall_point_type*>(floatArr5WallPoint + 3),
|
||||
&end2,
|
||||
&start2);
|
||||
Line2 = new TLine(this, &ActiveFlag, CollisionGroup, start2, end2);
|
||||
EdgeList->Add(Line2);
|
||||
if (Line2)
|
||||
{
|
||||
Line2->WallValue = nullptr;
|
||||
Line2->place_in_grid();
|
||||
}
|
||||
reinterpret_cast<wall_point_type*>(wall1Arr + 3),
|
||||
wall1End,
|
||||
wall1Start);
|
||||
|
||||
auto floatArr6WallPoint = loader::query_float_attribute(groupIndex, 0, 1302);
|
||||
auto wall2Pt1_2 = static_cast<int>(floor(floatArr6WallPoint[1]));
|
||||
Wall2PointFirst = 1 << static_cast<int>(floor(floatArr6WallPoint[0]));
|
||||
Wall2PointLast = floatArr6WallPoint[7];
|
||||
Line2 = new TLine(this, &ActiveFlag, CollisionGroup, wall1Start, wall1End);
|
||||
Line2->WallValue = nullptr;
|
||||
Line2->place_in_grid(&AABB);
|
||||
EdgeList.push_back(Line2);
|
||||
|
||||
auto wall2Arr = loader::query_float_attribute(groupIndex, 0, 1302);
|
||||
Wall2CollisionGroup = 1 << static_cast<int>(floor(wall2Arr[0]));
|
||||
auto wall2Enabled = static_cast<int>(floor(wall2Arr[1]));
|
||||
Wall2BallOffset = wall2Arr[7];
|
||||
maths::find_closest_edge(
|
||||
RampPlane,
|
||||
RampPlaneCount,
|
||||
reinterpret_cast<wall_point_type*>(floatArr6WallPoint + 3),
|
||||
&end3,
|
||||
&start3);
|
||||
Line3 = new TLine(this, &ActiveFlag, CollisionGroup, start3, end3);
|
||||
EdgeList->Add(Line3);
|
||||
if (Line3)
|
||||
{
|
||||
Line3->WallValue = nullptr;
|
||||
Line3->place_in_grid();
|
||||
}
|
||||
reinterpret_cast<wall_point_type*>(wall2Arr + 3),
|
||||
wall2End,
|
||||
wall2Start);
|
||||
|
||||
Line3 = new TLine(this, &ActiveFlag, CollisionGroup, wall2Start, wall2End);
|
||||
Line3->WallValue = nullptr;
|
||||
Line3->place_in_grid(&AABB);
|
||||
EdgeList.push_back(Line3);
|
||||
|
||||
|
||||
auto xMin = 1000000000.0f;
|
||||
@@ -93,57 +75,50 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
auto xMax = -1000000000.0f;
|
||||
for (auto index = 0; index < RampPlaneCount; index++)
|
||||
{
|
||||
auto plane = &RampPlane[index];
|
||||
auto pVec1 = reinterpret_cast<vector_type*>(&plane->V1);
|
||||
auto pVec2 = reinterpret_cast<vector_type*>(&plane->V2);
|
||||
auto pVec3 = reinterpret_cast<vector_type*>(&plane->V3);
|
||||
auto& plane = RampPlane[index];
|
||||
vector2* pointOrder[4] = { &plane.V1, &plane.V2, &plane.V3, &plane.V1 };
|
||||
|
||||
xMin = min(min(min(plane->V3.X, plane->V1.X), plane->V2.X), xMin);
|
||||
yMin = min(min(min(plane->V3.Y, plane->V1.Y), plane->V2.Y), xMin); // Sic
|
||||
xMax = max(max(max(plane->V3.X, plane->V1.X), plane->V2.X), xMin);
|
||||
yMax = max(max(max(plane->V3.Y, plane->V1.Y), plane->V2.Y), xMin);
|
||||
|
||||
vector_type* pointOrder[4] = {pVec1, pVec2, pVec3, pVec1};
|
||||
xMin = std::min(std::min(std::min(plane.V3.X, plane.V1.X), plane.V2.X), xMin);
|
||||
yMin = std::min(std::min(std::min(plane.V3.Y, plane.V1.Y), plane.V2.Y), xMin); // Sic
|
||||
xMax = std::max(std::max(std::max(plane.V3.X, plane.V1.X), plane.V2.X), xMin);
|
||||
yMax = std::max(std::max(std::max(plane.V3.Y, plane.V1.Y), plane.V2.Y), xMin);
|
||||
|
||||
for (auto pt = 0; pt < 3; pt++)
|
||||
{
|
||||
auto point1 = pointOrder[pt], point2 = pointOrder[pt + 1];
|
||||
auto& point1 = *pointOrder[pt], point2 = *pointOrder[pt + 1];
|
||||
auto collisionGroup = 0;
|
||||
if (point1 != end2 || point2 != start2)
|
||||
|
||||
if (point1 == wall1End && point2 == wall1Start)
|
||||
{
|
||||
if (point1 != end3 || point2 != start3)
|
||||
{
|
||||
collisionGroup = visual.CollisionGroup;
|
||||
}
|
||||
else if (wall2Pt1_2)
|
||||
{
|
||||
collisionGroup = Wall2PointFirst;
|
||||
}
|
||||
if (wall1Enabled)
|
||||
collisionGroup = Wall1CollisionGroup;
|
||||
}
|
||||
else if (wallPt1_2)
|
||||
else if (point1 == wall2End && point2 == wall2Start)
|
||||
{
|
||||
collisionGroup = Wall1PointFirst;
|
||||
if (wall2Enabled)
|
||||
collisionGroup = Wall2CollisionGroup;
|
||||
}
|
||||
else
|
||||
collisionGroup = visual.CollisionGroup;
|
||||
|
||||
if (collisionGroup)
|
||||
{
|
||||
auto line = new TLine(this, &ActiveFlag, collisionGroup, point1, point2);
|
||||
EdgeList->Add(line);
|
||||
if (line)
|
||||
{
|
||||
line->WallValue = plane;
|
||||
line->place_in_grid();
|
||||
}
|
||||
line->WallValue = &plane;
|
||||
line->place_in_grid(&AABB);
|
||||
EdgeList.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
plane->FieldForce.X = cos(plane->GravityAngle2) * sin(plane->GravityAngle1) *
|
||||
plane.FieldForce.X = cos(plane.GravityAngle2) * sin(plane.GravityAngle1) *
|
||||
PinballTable->GravityDirVectMult;
|
||||
plane->FieldForce.Y = sin(plane->GravityAngle2) * sin(plane->GravityAngle1) *
|
||||
plane.FieldForce.Y = sin(plane.GravityAngle2) * sin(plane.GravityAngle1) *
|
||||
PinballTable->GravityDirVectMult;
|
||||
}
|
||||
|
||||
Field.Flag2Ptr = &ActiveFlag;
|
||||
Field.ActiveFlag = &ActiveFlag;
|
||||
Field.CollisionComp = this;
|
||||
Field.Mask = visual.CollisionGroup;
|
||||
Field.CollisionGroup = visual.CollisionGroup;
|
||||
|
||||
auto x1 = xMax;
|
||||
auto y1 = yMax;
|
||||
@@ -152,23 +127,12 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
TTableLayer::edges_insert_square(y0, x0, y1, x1, nullptr, &Field);
|
||||
}
|
||||
|
||||
void TRamp::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 4)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TRamp::get_scoring(int index)
|
||||
{
|
||||
return index < 4 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
void TRamp::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
ball->not_again(edge);
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= coef;
|
||||
ball->RayMaxDistance -= distance;
|
||||
|
||||
auto plane = static_cast<ramp_plane_type*>(edge->WallValue);
|
||||
if (plane)
|
||||
@@ -180,8 +144,8 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||
ball->RampFieldForce.X = plane->FieldForce.X;
|
||||
ball->RampFieldForce.Y = plane->FieldForce.Y;
|
||||
ball->Position.Z = ball->Position.X * ball->CollisionOffset.X + ball->Position.Y * ball->CollisionOffset.Y +
|
||||
ball->Offset + ball->CollisionOffset.Z;
|
||||
ball->FieldFlag = CollisionGroup;
|
||||
ball->Radius + ball->CollisionOffset.Z;
|
||||
ball->CollisionMask = CollisionGroup;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,8 +153,8 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||
{
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
control::handler(63, this);
|
||||
loader::play_sound(SoftHitSoundId, ball, "TRamp");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -198,25 +162,23 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||
ball->CollisionFlag = 0;
|
||||
if (edge == Line2)
|
||||
{
|
||||
ball->FieldFlag = Wall1PointFirst;
|
||||
if (!RampFlag1)
|
||||
return;
|
||||
ball->Position.Z = ball->Offset + Wall1PointLast;
|
||||
ball->CollisionMask = Wall1CollisionGroup;
|
||||
if (BallZOffsetFlag)
|
||||
ball->Position.Z = ball->Radius + Wall1BallOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
ball->FieldFlag = Wall2PointFirst;
|
||||
if (!RampFlag1)
|
||||
return;
|
||||
ball->Position.Z = ball->Offset + Wall2PointLast;
|
||||
ball->CollisionMask = Wall2CollisionGroup;
|
||||
if (BallZOffsetFlag)
|
||||
ball->Position.Z = ball->Radius + Wall2BallOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TRamp::FieldEffect(TBall* ball, vector_type* vecDst)
|
||||
int TRamp::FieldEffect(TBall* ball, vector2* vecDst)
|
||||
{
|
||||
vecDst->X = ball->RampFieldForce.X - ball->Acceleration.X * ball->Speed * BallFieldMult;
|
||||
vecDst->Y = ball->RampFieldForce.Y - ball->Acceleration.Y * ball->Speed * BallFieldMult;
|
||||
vecDst->X = ball->RampFieldForce.X - ball->Direction.X * ball->Speed * BallFieldMult;
|
||||
vecDst->Y = ball->RampFieldForce.Y - ball->Direction.Y * ball->Speed * BallFieldMult;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,25 +10,22 @@ class TRamp :
|
||||
{
|
||||
public:
|
||||
TRamp(TPinballTable* table, int groupIndex);
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
int FieldEffect(TBall* ball, vector_type* vecDst) override;
|
||||
int FieldEffect(TBall* ball, vector2* vecDst) override;
|
||||
void port_draw() override;
|
||||
|
||||
int Scores[4];
|
||||
field_effect_type Field;
|
||||
field_effect_type Field{};
|
||||
int CollisionGroup;
|
||||
int RampFlag1;
|
||||
bool BallZOffsetFlag;
|
||||
int RampPlaneCount;
|
||||
float BallFieldMult;
|
||||
ramp_plane_type* RampPlane;
|
||||
TEdgeSegment* Line2;
|
||||
TEdgeSegment* Line3;
|
||||
TEdgeSegment* Line1;
|
||||
int Wall1PointFirst;
|
||||
int Wall2PointFirst;
|
||||
float Wall1PointLast;
|
||||
float Wall2PointLast;
|
||||
int Wall1CollisionGroup;
|
||||
int Wall2CollisionGroup;
|
||||
float Wall1BallOffset;
|
||||
float Wall2BallOffset;
|
||||
};
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "TEdgeSegment.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TZmapList.h"
|
||||
|
||||
TRollover::TRollover(TPinballTable* table, int groupIndex, bool createWall) : TCollisionComponent(
|
||||
table, groupIndex, createWall)
|
||||
@@ -20,33 +19,30 @@ TRollover::TRollover(TPinballTable* table, int groupIndex, bool createWall) : TC
|
||||
|
||||
TRollover::TRollover(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||
{
|
||||
RolloverFlag = 0;
|
||||
if (ListBitmap)
|
||||
render::sprite_set_bitmap(RenderSprite, static_cast<gdrv_bitmap8*>(ListBitmap->Get(0)));
|
||||
SpriteSet(0);
|
||||
build_walls(groupIndex);
|
||||
}
|
||||
|
||||
|
||||
int TRollover::Message(int code, float value)
|
||||
int TRollover::Message(MessageCode code, float value)
|
||||
{
|
||||
if (code == 1024)
|
||||
if (code == MessageCode::Reset)
|
||||
{
|
||||
this->ActiveFlag = 1;
|
||||
this->RolloverFlag = 0;
|
||||
if (this->ListBitmap)
|
||||
render::sprite_set_bitmap(this->RenderSprite, static_cast<gdrv_bitmap8*>(this->ListBitmap->Get(0)));
|
||||
ActiveFlag = 1;
|
||||
RolloverFlag = 0;
|
||||
SpriteSet(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TRollover::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
void TRollover::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge)
|
||||
{
|
||||
ball->Position.X = nextPosition->X;
|
||||
ball->Position.Y = nextPosition->Y;
|
||||
ball->RayMaxDistance -= coef;
|
||||
ball->RayMaxDistance -= distance;
|
||||
ball->not_again(edge);
|
||||
gdrv_bitmap8* bmp = nullptr;
|
||||
|
||||
if (!PinballTable->TiltLockFlag)
|
||||
{
|
||||
if (RolloverFlag)
|
||||
@@ -56,31 +52,14 @@ void TRollover::Collision(TBall* ball, vector_type* nextPosition, vector_type* d
|
||||
}
|
||||
else
|
||||
{
|
||||
loader::play_sound(SoftHitSoundId);
|
||||
control::handler(63, this);
|
||||
loader::play_sound(SoftHitSoundId, ball, "TRollover");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
RolloverFlag = RolloverFlag == 0;
|
||||
if (ListBitmap)
|
||||
{
|
||||
if (!RolloverFlag)
|
||||
bmp = static_cast<gdrv_bitmap8*>(ListBitmap->Get(0));
|
||||
render::sprite_set_bitmap(RenderSprite, bmp);
|
||||
}
|
||||
SpriteSet(RolloverFlag ? -1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TRollover::put_scoring(int index, int score)
|
||||
{
|
||||
if (index < 2)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TRollover::get_scoring(int index)
|
||||
{
|
||||
return index < 2 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
|
||||
void TRollover::build_walls(int groupIndex)
|
||||
{
|
||||
visualStruct visual{};
|
||||
|
||||
@@ -9,15 +9,12 @@ protected:
|
||||
public:
|
||||
TRollover(TPinballTable* table, int groupIndex);
|
||||
~TRollover() override = default;
|
||||
int Message(int code, float value) override;
|
||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
||||
int Message(MessageCode code, float value) override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
void put_scoring(int index, int score) override;
|
||||
int get_scoring(int index) override;
|
||||
void build_walls(int groupIndex);
|
||||
|
||||
static void TimerExpired(int timerId, void* caller);
|
||||
|
||||
char RolloverFlag;
|
||||
int Scores[2];
|
||||
char RolloverFlag{};
|
||||
};
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "loader.h"
|
||||
#include "objlist_class.h"
|
||||
#include "render.h"
|
||||
#include "TPinballTable.h"
|
||||
#include "TBall.h"
|
||||
#include "TDrain.h"
|
||||
#include "timer.h"
|
||||
|
||||
TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||
@@ -15,13 +15,12 @@ TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
visualStruct visual{};
|
||||
|
||||
MessageField = 0;
|
||||
Timer = 0;
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
BallAcceleration = visual.Kicker.ThrowBallAcceleration;
|
||||
loader::query_visual(groupIndex, 0, &visual);
|
||||
BallThrowDirection = visual.Kicker.ThrowBallDirection;
|
||||
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
||||
ThrowSpeedMult1 = visual.Kicker.Boost;
|
||||
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
||||
SoundIndex4 = visual.SoundIndex4;
|
||||
SoundIndex4 = visual.SoundIndex4;
|
||||
SoundIndex3 = visual.SoundIndex3;
|
||||
auto floatArr = loader::query_float_attribute(groupIndex, 0, 601);
|
||||
BallPosition.X = floatArr[0];
|
||||
@@ -29,34 +28,26 @@ TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||
}
|
||||
|
||||
int TSink::Message(int code, float value)
|
||||
int TSink::Message(MessageCode code, float value)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 56:
|
||||
if (value < 0.0)
|
||||
case MessageCode::TSinkResetTimer:
|
||||
if (value < 0.0f)
|
||||
value = TimerTime;
|
||||
Timer = timer::set(value, this, TimerExpired);
|
||||
timer::set(value, this, TimerExpired);
|
||||
break;
|
||||
case 1020:
|
||||
case MessageCode::PlayerChanged:
|
||||
timer::kill(TimerExpired);
|
||||
PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField;
|
||||
MessageField = PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
||||
break;
|
||||
case 1024:
|
||||
case MessageCode::Reset:
|
||||
{
|
||||
if (Timer)
|
||||
timer::kill(Timer);
|
||||
Timer = 0;
|
||||
timer::kill(TimerExpired);
|
||||
MessageField = 0;
|
||||
|
||||
auto playerPtr = PlayerMessagefieldBackup;
|
||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||
{
|
||||
*playerPtr = 0;
|
||||
|
||||
++playerPtr;
|
||||
}
|
||||
|
||||
for (auto &msgBackup : PlayerMessagefieldBackup)
|
||||
msgBackup = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -65,44 +56,36 @@ int TSink::Message(int code, float value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TSink::put_scoring(int index, int score)
|
||||
void TSink::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||
{
|
||||
if (index < 3)
|
||||
Scores[index] = score;
|
||||
}
|
||||
|
||||
int TSink::get_scoring(int index)
|
||||
{
|
||||
return index < 3 ? Scores[index] : 0;
|
||||
}
|
||||
|
||||
void TSink::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
||||
{
|
||||
Timer = 0;
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
||||
PinballTable->Drain->Collision(ball, nextPosition, direction, distance, edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
ball->ActiveFlag = 0;
|
||||
render::sprite_set_bitmap(ball->RenderSprite, nullptr);
|
||||
loader::play_sound(SoundIndex4);
|
||||
control::handler(63, this);
|
||||
ball->Disable();
|
||||
loader::play_sound(SoundIndex4, ball, "TSink1");
|
||||
control::handler(MessageCode::ControlCollision, this);
|
||||
}
|
||||
}
|
||||
|
||||
void TSink::TimerExpired(int timerId, void* caller)
|
||||
{
|
||||
auto sink = static_cast<TSink*>(caller);
|
||||
auto ball = static_cast<TBall*>(sink->PinballTable->BallList->Get(0));
|
||||
ball->CollisionComp = nullptr;
|
||||
ball->ActiveFlag = 1;
|
||||
ball->Position.X = sink->BallPosition.X;
|
||||
ball->Position.Y = sink->BallPosition.Y;
|
||||
TBall::throw_ball(ball, &sink->BallAcceleration, sink->ThrowAngleMult, sink->ThrowSpeedMult1,
|
||||
sink->ThrowSpeedMult2);
|
||||
if (sink->SoundIndex3)
|
||||
loader::play_sound(sink->SoundIndex3);
|
||||
sink->Timer = 0;
|
||||
auto table = sink->PinballTable;
|
||||
if (table->BallCountInRect(sink->BallPosition, table->CollisionCompOffset * 2.0f))
|
||||
{
|
||||
timer::set(0.5f, sink, TimerExpired);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ball = table->AddBall(sink->BallPosition);
|
||||
assertm(ball, "Failure to create ball in sink");
|
||||
ball->CollisionDisabledFlag = true;
|
||||
ball->throw_ball(&sink->BallThrowDirection, sink->ThrowAngleMult, sink->ThrowSpeedMult1,
|
||||
sink->ThrowSpeedMult2);
|
||||
if (sink->SoundIndex3)
|
||||
loader::play_sound(sink->SoundIndex3, ball, "TSink2");
|
||||
}
|
||||
}
|
||||