Compare commits
102 Commits
Release_2.
...
master
Author | SHA1 | Date |
---|---|---|
Daeho Ro | cb9b7b8862 | |
Orazio | 182bd93648 | |
Headshotnoby | cef716e5cc | |
Muzychenko Andrey | 52126feb40 | |
Muzychenko Andrey | 73262ae207 | |
Muzychenko Andrey | 75d2d98a46 | |
Muzychenko Andrey | 6a30ccbef1 | |
Muzychenko Andrey | 350651dae6 | |
Orazio | 6ab7b3e772 | |
Muzychenko Andrey | e2f3ae66f8 | |
Muzychenko Andrey | 18c80a0ff8 | |
Adam | 62e20b1cf9 | |
Muzychenko Andrey | 93d8e12782 | |
Muzychenko Andrey | 8c774722eb | |
Muzychenko Andrey | a937ff5c80 | |
Muzychenko Andrey | e466bbacb8 | |
Muzychenko Andrey | 34a1e32843 | |
Muzychenko Andrey | 43e2ab896b | |
Muzychenko Andrey | e0424bed65 | |
Muzychenko Andrey | c5acdcd524 | |
Muzychenko Andrey | f521a03322 | |
Muzychenko Andrey | 4b86fe2aa7 | |
Muzychenko Andrey | ba470e8727 | |
Muzychenko Andrey | 466c875f8a | |
Muzychenko Andrey | 1749a2ba09 | |
Muzychenko Andrey | 831c3f49bf | |
Muzychenko Andrey | 9454e11fd9 | |
Muzychenko Andrey | 215599684c | |
Muzychenko Andrey | 8df996f452 | |
Muzychenko Andrey | d99fbb092e | |
Muzychenko Andrey | 4192b12c29 | |
Muzychenko Andrey | 10ff1143cc | |
Muzychenko Andrey | 2d6f2c14e5 | |
Muzychenko Andrey | 17f11bd428 | |
Muzychenko Andrey | 6aa6472667 | |
Muzychenko Andrey | ab3f3bd12b | |
Muzychenko Andrey | 3109a8ea75 | |
Muzychenko Andrey | 2162cac977 | |
Muzychenko Andrey | 8e43d06e84 | |
Muzychenko Andrey | 31530bef18 | |
Muzychenko Andrey | 3be26282b3 | |
Low-power | f561cadf63 | |
Muzychenko Andrey | 1391eeba81 | |
Muzychenko Andrey | ea32c08c4f | |
Muzychenko Andrey | 80947888a0 | |
Low-power | 6486589c4a | |
Muzychenko Andrey | 3c6e1c9d47 | |
Muzychenko Andrey | cfaab257ed | |
Muzychenko Andrey | 4ec30cf472 | |
Natty | e7ddebd16c | |
Muzychenko Andrey | 7003b01e5d | |
Muzychenko Andrey | 9f0ae0434e | |
Harmann Gabrielian | 1e43bdd5fa | |
Muzychenko Andrey | 40672845e4 | |
Muzychenko Andrey | 22603aa126 | |
Muzychenko Andrey | dfe1665ba1 | |
Muzychenko Andrey | e80010e3c6 | |
Muzychenko Andrey | 803ca14ef2 | |
Muzychenko Andrey | 44d5fd5097 | |
Harmann Gabrielian | 69fd91f003 | |
Muzychenko Andrey | 42226a14c9 | |
Muzychenko Andrey | 88f835d068 | |
Alexis Murzeau | 66a868083a | |
Muzychenko Andrey | c1c74878df | |
Muzychenko Andrey | 14a8d64b67 | |
Muzychenko Andrey | acd1ad34b2 | |
Muzychenko Andrey | 7feba1e947 | |
stech11 (SoftOrange Tech) | e9a4791322 | |
Alexis Murzeau | a2567c1fea | |
Alexis Murzeau | 367f4538a3 | |
Muzychenko Andrey | 54a217c27b | |
sasodoma | 6f00b57eb9 | |
Federico Matteoni | 1a610ba831 | |
Muzychenko Andrey | eed3662592 | |
Muzychenko Andrey | 5e42f37fba | |
Muzychenko Andrey | 8017734de4 | |
Muzychenko Andrey | c93e11ee6b | |
Muzychenko Andrey | 5d7d7c0822 | |
Patrice Levesque | a4c6165094 | |
Muzychenko Andrey | cfe2691892 | |
Muzychenko Andrey | 4183e7f0bf | |
Muzychenko Andrey | e283a643b3 | |
Muzychenko Andrey | 97aea20586 | |
Muzychenko Andrey | 5461483bb5 | |
Muzychenko Andrey | 0cb75ecf7f | |
Muzychenko Andrey | 2d2ca0ab2a | |
Muzychenko Andrey | fdf1f6c9f1 | |
Muzychenko Andrey | 2d0da712e3 | |
Muzychenko Andrey | d23444b983 | |
guijan | 3f7526ba12 | |
guijan | cdf0216136 | |
Muzychenko Andrey | cc06d35bc7 | |
Stefan | 0f88e43ba2 | |
Muzychenko Andrey | b20e13ee97 | |
Muzychenko Andrey | a626572da3 | |
Stefan | 29c84c37c8 | |
林博仁(Buo-ren, Lin) | 6039f843a7 | |
Muzychenko Andrey | 8f34829b1e | |
Muzychenko Andrey | 0a2d6847ba | |
Muzychenko Andrey | 43af97127b | |
Nicola Smaniotto | d8ee1b9bfe | |
Kowalski Dragon | 583262d221 |
|
@ -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
|
|
@ -266,7 +266,7 @@ __pycache__/
|
||||||
/Export
|
/Export
|
||||||
/DrMem
|
/DrMem
|
||||||
/Doc private
|
/Doc private
|
||||||
# Windows local libraries
|
# Windows and macOS local libraries
|
||||||
/Libs
|
/Libs
|
||||||
|
|
||||||
#CMake generated
|
#CMake generated
|
||||||
|
@ -298,3 +298,5 @@ _deps
|
||||||
.ninja_deps
|
.ninja_deps
|
||||||
.ninja_log
|
.ninja_log
|
||||||
build.ninja
|
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
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0...3.5)
|
||||||
project(SpaceCadetPinball)
|
project(SpaceCadetPinball)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
@ -19,6 +19,16 @@ if(MINGW)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
||||||
endif()
|
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
|
# SDL2main is not needed
|
||||||
set(SDL2_BUILDING_LIBRARY ON)
|
set(SDL2_BUILDING_LIBRARY ON)
|
||||||
|
|
||||||
|
@ -38,6 +48,8 @@ set(SOURCE_FILES
|
||||||
SpaceCadetPinball/control.h
|
SpaceCadetPinball/control.h
|
||||||
SpaceCadetPinball/EmbeddedData.cpp
|
SpaceCadetPinball/EmbeddedData.cpp
|
||||||
SpaceCadetPinball/EmbeddedData.h
|
SpaceCadetPinball/EmbeddedData.h
|
||||||
|
SpaceCadetPinball/font_selection.cpp
|
||||||
|
SpaceCadetPinball/font_selection.h
|
||||||
SpaceCadetPinball/fullscrn.cpp
|
SpaceCadetPinball/fullscrn.cpp
|
||||||
SpaceCadetPinball/fullscrn.h
|
SpaceCadetPinball/fullscrn.h
|
||||||
SpaceCadetPinball/gdrv.cpp
|
SpaceCadetPinball/gdrv.cpp
|
||||||
|
@ -61,8 +73,6 @@ set(SOURCE_FILES
|
||||||
SpaceCadetPinball/pb.cpp
|
SpaceCadetPinball/pb.cpp
|
||||||
SpaceCadetPinball/pb.h
|
SpaceCadetPinball/pb.h
|
||||||
SpaceCadetPinball/pch.h
|
SpaceCadetPinball/pch.h
|
||||||
SpaceCadetPinball/pinball.cpp
|
|
||||||
SpaceCadetPinball/pinball.h
|
|
||||||
SpaceCadetPinball/proj.cpp
|
SpaceCadetPinball/proj.cpp
|
||||||
SpaceCadetPinball/proj.h
|
SpaceCadetPinball/proj.h
|
||||||
SpaceCadetPinball/render.cpp
|
SpaceCadetPinball/render.cpp
|
||||||
|
@ -131,6 +141,8 @@ set(SOURCE_FILES
|
||||||
SpaceCadetPinball/TPopupTarget.h
|
SpaceCadetPinball/TPopupTarget.h
|
||||||
SpaceCadetPinball/TRamp.cpp
|
SpaceCadetPinball/TRamp.cpp
|
||||||
SpaceCadetPinball/TRamp.h
|
SpaceCadetPinball/TRamp.h
|
||||||
|
SpaceCadetPinball/translations.cpp
|
||||||
|
SpaceCadetPinball/translations.h
|
||||||
SpaceCadetPinball/TRollover.cpp
|
SpaceCadetPinball/TRollover.cpp
|
||||||
SpaceCadetPinball/TRollover.h
|
SpaceCadetPinball/TRollover.h
|
||||||
SpaceCadetPinball/TSink.cpp
|
SpaceCadetPinball/TSink.cpp
|
||||||
|
@ -167,9 +179,13 @@ set(SOURCE_FILES
|
||||||
SpaceCadetPinball/imgui_demo.cpp
|
SpaceCadetPinball/imgui_demo.cpp
|
||||||
SpaceCadetPinball/imgui_impl_sdl.cpp
|
SpaceCadetPinball/imgui_impl_sdl.cpp
|
||||||
SpaceCadetPinball/imgui_impl_sdl.h
|
SpaceCadetPinball/imgui_impl_sdl.h
|
||||||
|
SpaceCadetPinball/imgui_impl_sdlrenderer.h
|
||||||
|
SpaceCadetPinball/imgui_impl_sdlrenderer.cpp
|
||||||
SpaceCadetPinball/imstb_textedit.h
|
SpaceCadetPinball/imstb_textedit.h
|
||||||
SpaceCadetPinball/imstb_rectpack.h
|
SpaceCadetPinball/imstb_rectpack.h
|
||||||
SpaceCadetPinball/imstb_truetype.h
|
SpaceCadetPinball/imstb_truetype.h
|
||||||
|
SpaceCadetPinball/DebugOverlay.cpp
|
||||||
|
SpaceCadetPinball/DebugOverlay.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# On Windows, include resource file with the icon
|
# On Windows, include resource file with the icon
|
||||||
|
@ -189,6 +205,7 @@ set_source_files_properties(
|
||||||
SpaceCadetPinball/imgui_tables.cpp
|
SpaceCadetPinball/imgui_tables.cpp
|
||||||
SpaceCadetPinball/imgui_demo.cpp
|
SpaceCadetPinball/imgui_demo.cpp
|
||||||
SpaceCadetPinball/imgui_impl_sdl.cpp
|
SpaceCadetPinball/imgui_impl_sdl.cpp
|
||||||
|
SpaceCadetPinball/imgui_impl_sdlrenderer.cpp
|
||||||
PROPERTIES SKIP_PRECOMPILE_HEADERS 1
|
PROPERTIES SKIP_PRECOMPILE_HEADERS 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -221,10 +238,10 @@ endif()
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||||
install(FILES "/Platform/Linux/${PROJECT_NAME}.desktop" DESTINATION "share/applications")
|
install(FILES "${CMAKE_SOURCE_DIR}/Platform/Linux/${PROJECT_NAME}.desktop" DESTINATION "share/applications")
|
||||||
install(FILES "/Platform/Linux/${PROJECT_NAME}.metainfo.xml" DESTINATION "share/metainfo")
|
install(FILES "${CMAKE_SOURCE_DIR}/Platform/Linux/${PROJECT_NAME}.metainfo.xml" DESTINATION "share/metainfo")
|
||||||
foreach(S 16 32 48 128 192)
|
foreach(S 16 32 48 128 192)
|
||||||
install(FILES "${PROJECT_NAME}/Icon_${S}x${S}.png" DESTINATION
|
install(FILES "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Icon_${S}x${S}.png" DESTINATION
|
||||||
"share/icons/hicolor/${S}x${S}/apps" RENAME "${PROJECT_NAME}.png")
|
"share/icons/hicolor/${S}x${S}/apps" RENAME "${PROJECT_NAME}.png")
|
||||||
endforeach(S)
|
endforeach(S)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -56,6 +56,19 @@
|
||||||
"buildCommandArgs": "",
|
"buildCommandArgs": "",
|
||||||
"ctestCommandArgs": "",
|
"ctestCommandArgs": "",
|
||||||
"inheritEnvironments": [ "msvc_x86" ]
|
"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}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -7,5 +7,5 @@ No source ports in main repository.\
|
||||||
I have no way to test and maintain most of them.\
|
I have no way to test and maintain most of them.\
|
||||||
The best I can do is to add a link.
|
The best I can do is to add a link.
|
||||||
|
|
||||||
There is no guaranty that any particular PR will be accepted.\
|
There is no guarantee that any particular PR will be accepted.\
|
||||||
If you are unsure, ask first, make PR second.
|
If you are unsure, ask first, make PR second.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<id>com.github.k4zmu2a.spacecadetpinball</id>
|
<id>com.github.k4zmu2a.spacecadetpinball</id>
|
||||||
<name>Space Cadet Pinball</name>
|
<name>Space Cadet Pinball</name>
|
||||||
<display_name>Space Cadet Pinball</display_name>
|
<display_name>Space Cadet Pinball</display_name>
|
||||||
<summary>Reverse engineering of 3D Pinball for Windows - Space Cadet, a game bundled with Windows.</summary>
|
<summary>Game engine for Space Cadet Pinball</summary>
|
||||||
<url type="homepage">https://github.com/k4zmu2a/SpaceCadetPinball</url>
|
<url type="homepage">https://github.com/k4zmu2a/SpaceCadetPinball</url>
|
||||||
<url type="bugtracker">https://github.com/k4zmu2a/SpaceCadetPinball/issues</url>
|
<url type="bugtracker">https://github.com/k4zmu2a/SpaceCadetPinball/issues</url>
|
||||||
<description>
|
<description>
|
||||||
|
@ -28,6 +28,35 @@
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<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">
|
<release version="2.0.0" date="2021-10-30">
|
||||||
<description>
|
<description>
|
||||||
<p>First release of cross-platform port.</p>
|
<p>First release of cross-platform port.</p>
|
||||||
|
|
|
@ -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>
|
Binary file not shown.
36
README.md
36
README.md
|
@ -22,7 +22,13 @@ Supports data files from Windows and Full Tilt versions of the game.
|
||||||
| Android (WIP) | Iscle | https://github.com/Iscle/SpaceCadetPinball |
|
| Android (WIP) | Iscle | https://github.com/Iscle/SpaceCadetPinball |
|
||||||
| Nintendo Wii | MaikelChan | https://github.com/MaikelChan/SpaceCadetPinball |
|
| Nintendo Wii | MaikelChan | https://github.com/MaikelChan/SpaceCadetPinball |
|
||||||
| Nintendo 3DS | MaikelChan | https://github.com/MaikelChan/SpaceCadetPinball/tree/3ds |
|
| 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 |
|
| 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.
|
Platforms covered by this project: desktop Windows, Linux and macOS.
|
||||||
|
|
||||||
|
@ -71,6 +77,21 @@ This project is available as Flatpak on [Flathub](https://flathub.org/apps/detai
|
||||||
|
|
||||||
### On macOS
|
### 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.
|
* **Homebrew**: Install the `SDL2`, `SDL2_mixer` homebrew packages.
|
||||||
* **MacPorts**: Install the `libSDL2`, `libSDL2_mixer` macports packages.
|
* **MacPorts**: Install the `libSDL2`, `libSDL2_mixer` macports packages.
|
||||||
|
|
||||||
|
@ -78,17 +99,22 @@ Compile with CMake. Ensure that `CMAKE_OSX_ARCHITECTURES` variable is set for ei
|
||||||
|
|
||||||
Tested with: macOS Big Sur (Intel) with Xcode 13 & macOS Montery Beta (Apple Silicon) with Xcode 13.
|
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
|
## Plans
|
||||||
|
|
||||||
* ~~Decompile original game~~
|
* ~~Decompile original game~~
|
||||||
* ~~Resizable window, scaled graphics~~
|
* ~~Resizable window, scaled graphics~~
|
||||||
* ~~Loader for high-res sprites from CADET.DAT~~
|
* ~~Loader for high-res sprites from CADET.DAT~~
|
||||||
* ~~Cross-platform port using SDL2, SDL2_mixer, ImGui~~
|
* ~~Cross-platform port using SDL2, SDL2_mixer, ImGui~~
|
||||||
* Misc features of Full Tilt: 3 music tracks, multiball, centered textboxes, etc.
|
* Full Tilt Cadet features
|
||||||
* Maybe: Text translations
|
* Localization support
|
||||||
* Maybe: Android port
|
* Maybe: Support for the other two tables - Dragon and Pirate
|
||||||
* Maybe x2: support for other two tables
|
* Maybe: Game data editor
|
||||||
* Table specific BL (control interactions and missions) is hardcoded, othere parts might be also patched
|
|
||||||
|
|
||||||
## On 64-bit bug that killed the game
|
## On 64-bit bug that killed the game
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -5,4 +5,6 @@ class EmbeddedData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const char PB_MSGFT_bin_compressed_data_base85[5380 + 1];
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "fullscrn.h"
|
#include "fullscrn.h"
|
||||||
#include "gdrv.h"
|
#include "gdrv.h"
|
||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "pinball.h"
|
|
||||||
#include "zdrv.h"
|
#include "zdrv.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ void GroupData::AddEntry(EntryData* entry)
|
||||||
if (srcBmp->BitmapType == BitmapTypes::Spliced)
|
if (srcBmp->BitmapType == BitmapTypes::Spliced)
|
||||||
{
|
{
|
||||||
// Get rid of spliced bitmap early on, to simplify render pipeline
|
// Get rid of spliced bitmap early on, to simplify render pipeline
|
||||||
auto bmp = new gdrv_bitmap8(srcBmp->Width, srcBmp->Height, srcBmp->Width);
|
auto bmp = new gdrv_bitmap8(srcBmp->Width, srcBmp->Height, true);
|
||||||
auto zMap = new zmap_header_type(srcBmp->Width, srcBmp->Height, srcBmp->Width);
|
auto zMap = new zmap_header_type(srcBmp->Width, srcBmp->Height, srcBmp->Width);
|
||||||
SplitSplicedBitmap(*srcBmp, *bmp, *zMap);
|
SplitSplicedBitmap(*srcBmp, *bmp, *zMap);
|
||||||
|
|
||||||
|
@ -246,7 +245,7 @@ int DatFile::field_size(int groupIndex, FieldTypes targetEntryType)
|
||||||
int DatFile::record_labeled(LPCSTR targetGroupName)
|
int DatFile::record_labeled(LPCSTR targetGroupName)
|
||||||
{
|
{
|
||||||
auto targetLength = strlen(targetGroupName);
|
auto targetLength = strlen(targetGroupName);
|
||||||
for (int groupIndex = Groups.size() - 1; groupIndex >= 0; --groupIndex)
|
for (int groupIndex = static_cast<int>(Groups.size()) - 1; groupIndex >= 0; --groupIndex)
|
||||||
{
|
{
|
||||||
auto groupName = field(groupIndex, FieldTypes::GroupName);
|
auto groupName = field(groupIndex, FieldTypes::GroupName);
|
||||||
if (!groupName)
|
if (!groupName)
|
||||||
|
@ -296,7 +295,7 @@ void DatFile::Finalize()
|
||||||
|
|
||||||
// PINBALL2.MID is an alternative font provided in 3DPB data
|
// PINBALL2.MID is an alternative font provided in 3DPB data
|
||||||
// Scaled down because it is too large for top text box
|
// Scaled down because it is too large for top text box
|
||||||
/*auto file = pinball::make_path_name("PINBALL2.MID");
|
/*auto file = pb::make_path_name("PINBALL2.MID");
|
||||||
auto fileHandle = fopenu(file.c_str(), "rb");
|
auto fileHandle = fopenu(file.c_str(), "rb");
|
||||||
fseek(fileHandle, 0, SEEK_END);
|
fseek(fileHandle, 0, SEEK_END);
|
||||||
auto fileSize = static_cast<uint32_t>(ftell(fileHandle));
|
auto fileSize = static_cast<uint32_t>(ftell(fileHandle));
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 86 KiB |
|
@ -10,4 +10,7 @@
|
||||||
<Item Name="[Color]" ExcludeView="simple">Color</Item>
|
<Item Name="[Color]" ExcludeView="simple">Color</Item>
|
||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</Type>
|
||||||
|
<Type Name="vector3">
|
||||||
|
<DisplayString>{{ X={X} Y={Y} Z={Z} }}</DisplayString>
|
||||||
|
</Type>
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
|
@ -1,62 +1,125 @@
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include "options.h"
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
#include "maths.h"
|
||||||
|
|
||||||
int Sound::num_channels;
|
int Sound::num_channels;
|
||||||
bool Sound::enabled_flag = false;
|
bool Sound::enabled_flag = false;
|
||||||
int* Sound::TimeStamps = nullptr;
|
std::vector<ChannelInfo> Sound::Channels{};
|
||||||
|
int Sound::Volume = MIX_MAX_VOLUME;
|
||||||
|
bool Sound::MixOpen = false;
|
||||||
|
|
||||||
bool Sound::Init(int channels, bool enableFlag)
|
void Sound::Init(bool mixOpen, int channels, bool enableFlag, int volume)
|
||||||
{
|
{
|
||||||
Mix_Init(MIX_INIT_MID_Proxy);
|
MixOpen = mixOpen;
|
||||||
auto result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024);
|
Volume = volume;
|
||||||
SetChannels(channels);
|
SetChannels(channels);
|
||||||
Enable(enableFlag);
|
Enable(enableFlag);
|
||||||
return !result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::Enable(bool enableFlag)
|
void Sound::Enable(bool enableFlag)
|
||||||
{
|
{
|
||||||
enabled_flag = enableFlag;
|
enabled_flag = enableFlag;
|
||||||
if (!enableFlag)
|
if (MixOpen && !enableFlag)
|
||||||
Mix_HaltChannel(-1);
|
Mix_HaltChannel(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::Activate()
|
void Sound::Activate()
|
||||||
{
|
{
|
||||||
|
if (MixOpen)
|
||||||
Mix_Resume(-1);
|
Mix_Resume(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::Deactivate()
|
void Sound::Deactivate()
|
||||||
{
|
{
|
||||||
|
if (MixOpen)
|
||||||
Mix_Pause(-1);
|
Mix_Pause(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::Close()
|
void Sound::Close()
|
||||||
{
|
{
|
||||||
delete[] TimeStamps;
|
Enable(false);
|
||||||
TimeStamps = nullptr;
|
Channels.clear();
|
||||||
Mix_CloseAudio();
|
|
||||||
Mix_Quit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::PlaySound(Mix_Chunk* wavePtr, int time)
|
void Sound::PlaySound(Mix_Chunk* wavePtr, int time, TPinballComponent* soundSource, const char* info)
|
||||||
{
|
{
|
||||||
if (wavePtr && enabled_flag)
|
if (MixOpen && wavePtr && enabled_flag)
|
||||||
{
|
{
|
||||||
if (Mix_Playing(-1) == num_channels)
|
if (Mix_Playing(-1) == num_channels)
|
||||||
{
|
{
|
||||||
auto oldestChannel = std::min_element(TimeStamps, TimeStamps + num_channels) - TimeStamps;
|
auto cmp = [](const ChannelInfo& a, const ChannelInfo& b)
|
||||||
|
{
|
||||||
|
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);
|
Mix_HaltChannel(oldestChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto channel = Mix_PlayChannel(-1, wavePtr, 0);
|
auto channel = Mix_PlayChannel(-1, wavePtr, 0);
|
||||||
if (channel != -1)
|
if (channel != -1)
|
||||||
TimeStamps[channel] = time;
|
{
|
||||||
|
Channels[channel].TimeStamp = time;
|
||||||
|
if (options::Options.SoundStereo)
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mix_Chunk* Sound::LoadWaveFile(const std::string& lpName)
|
Mix_Chunk* Sound::LoadWaveFile(const std::string& lpName)
|
||||||
{
|
{
|
||||||
|
if (!MixOpen)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
auto wavFile = fopenu(lpName.c_str(), "r");
|
auto wavFile = fopenu(lpName.c_str(), "r");
|
||||||
if (!wavFile)
|
if (!wavFile)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -67,7 +130,7 @@ Mix_Chunk* Sound::LoadWaveFile(const std::string& lpName)
|
||||||
|
|
||||||
void Sound::FreeSound(Mix_Chunk* wave)
|
void Sound::FreeSound(Mix_Chunk* wave)
|
||||||
{
|
{
|
||||||
if (wave)
|
if (MixOpen && wave)
|
||||||
Mix_FreeChunk(wave);
|
Mix_FreeChunk(wave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +140,15 @@ void Sound::SetChannels(int channels)
|
||||||
channels = 8;
|
channels = 8;
|
||||||
|
|
||||||
num_channels = channels;
|
num_channels = channels;
|
||||||
delete[] TimeStamps;
|
Channels.resize(num_channels);
|
||||||
TimeStamps = new int[num_channels]();
|
if (MixOpen)
|
||||||
Mix_AllocateChannels(num_channels);
|
Mix_AllocateChannels(num_channels);
|
||||||
|
SetVolume(Volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::SetVolume(int volume)
|
||||||
|
{
|
||||||
|
Volume = volume;
|
||||||
|
if (MixOpen)
|
||||||
|
Mix_Volume(-1, volume);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "maths.h"
|
||||||
|
#include "TPinballComponent.h"
|
||||||
|
|
||||||
|
struct ChannelInfo
|
||||||
|
{
|
||||||
|
int TimeStamp;
|
||||||
|
vector2 Position;
|
||||||
|
};
|
||||||
|
|
||||||
class Sound
|
class Sound
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool Init(int channels, bool enableFlag);
|
static std::vector<ChannelInfo> Channels;
|
||||||
|
|
||||||
|
static void Init(bool mixOpen, int channels, bool enableFlag, int volume);
|
||||||
static void Enable(bool enableFlag);
|
static void Enable(bool enableFlag);
|
||||||
static void Activate();
|
static void Activate();
|
||||||
static void Deactivate();
|
static void Deactivate();
|
||||||
static void Close();
|
static void Close();
|
||||||
static void PlaySound(Mix_Chunk* wavePtr, int time);
|
static void PlaySound(Mix_Chunk* wavePtr, int time, TPinballComponent *soundSource, const char* info);
|
||||||
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
|
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
|
||||||
static void FreeSound(Mix_Chunk* wave);
|
static void FreeSound(Mix_Chunk* wave);
|
||||||
static void SetChannels(int channels);
|
static void SetChannels(int channels);
|
||||||
|
static void SetVolume(int volume);
|
||||||
private:
|
private:
|
||||||
static int num_channels;
|
static int num_channels;
|
||||||
static bool enabled_flag;
|
static bool enabled_flag;
|
||||||
static int* TimeStamps;
|
static int Volume;
|
||||||
|
static bool MixOpen;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,17 @@
|
||||||
// SpaceCadetPinball.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
#include "winmain.h"
|
#include "winmain.h"
|
||||||
|
|
||||||
int MainActual(LPCSTR lpCmdLine)
|
|
||||||
{
|
|
||||||
// Todo: get rid of restart to change resolution.
|
|
||||||
int returnCode;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
returnCode = winmain::WinMain(lpCmdLine);
|
|
||||||
}
|
|
||||||
while (winmain::RestartRequested());
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
std::string cmdLine;
|
std::string cmdLine;
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (i > 1)
|
||||||
|
cmdLine += " ";
|
||||||
cmdLine += argv[i];
|
cmdLine += argv[i];
|
||||||
|
}
|
||||||
|
|
||||||
return MainActual(cmdLine.c_str());
|
return winmain::WinMain(cmdLine.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -32,7 +20,7 @@ int main(int argc, char* argv[])
|
||||||
// Windows subsystem main
|
// Windows subsystem main
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||||
{
|
{
|
||||||
return MainActual(lpCmdLine);
|
return winmain::WinMain(lpCmdLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fopen to _wfopen adapter, for UTF-8 paths
|
// fopen to _wfopen adapter, for UTF-8 paths
|
||||||
|
|
|
@ -9,133 +9,189 @@
|
||||||
#include "proj.h"
|
#include "proj.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.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{};
|
visualStruct visual{};
|
||||||
char ballGroupName[10]{"ball"};
|
char ballGroupName[10]{"ball"};
|
||||||
|
|
||||||
TimeNow = 0.0;
|
|
||||||
RayMaxDistance = 0.0;
|
RayMaxDistance = 0.0;
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
CollisionComp = nullptr;
|
CollisionComp = nullptr;
|
||||||
EdgeCollisionCount = 0;
|
EdgeCollisionCount = 0;
|
||||||
TimeDelta = 0.0;
|
TimeDelta = 0.0;
|
||||||
FieldFlag = 1;
|
|
||||||
CollisionFlag = 0;
|
CollisionFlag = 0;
|
||||||
Speed = 0.0;
|
Speed = 0.0;
|
||||||
Acceleration.Y = 0.0;
|
Direction.Y = 0.0;
|
||||||
Acceleration.X = 0.0;
|
Direction.X = 0.0;
|
||||||
InvAcceleration.Y = 1000000000.0;
|
ListBitmap = new std::vector<SpriteData>();
|
||||||
InvAcceleration.X = 1000000000.0;
|
|
||||||
Position.X = 0.0;
|
|
||||||
Position.Y = 0.0;
|
|
||||||
|
|
||||||
ListBitmap = new std::vector<gdrv_bitmap8*>();
|
if (groupIndex == -1)
|
||||||
|
{
|
||||||
|
HasGroupFlag = false;
|
||||||
|
Position = {0, 0, 0};
|
||||||
|
CollisionMask = 1;
|
||||||
|
}
|
||||||
|
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*/
|
/*Full tilt: ball is ballN, where N[0,2] resolution*/
|
||||||
auto groupIndex = loader::query_handle(ballGroupName);
|
groupIndex = loader::query_handle(ballGroupName);
|
||||||
if (groupIndex < 0)
|
if (groupIndex < 0)
|
||||||
{
|
{
|
||||||
ballGroupName[4] = '0' + fullscrn::GetResolution();
|
ballGroupName[4] = '0' + fullscrn::GetResolution();
|
||||||
groupIndex = loader::query_handle(ballGroupName);
|
groupIndex = loader::query_handle(ballGroupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Offset = *loader::query_float_attribute(groupIndex, 0, 500);
|
Radius = *loader::query_float_attribute(groupIndex, 0, 500);
|
||||||
|
|
||||||
auto visualCount = loader::query_visual_states(groupIndex);
|
auto visualCount = loader::query_visual_states(groupIndex);
|
||||||
for (auto index = 0; index < visualCount; ++index)
|
for (auto index = 0; index < visualCount; ++index)
|
||||||
{
|
{
|
||||||
loader::query_visual(groupIndex, index, &visual);
|
loader::query_visual(groupIndex, index, &visual);
|
||||||
if (ListBitmap)
|
|
||||||
ListBitmap->push_back(visual.Bitmap);
|
ListBitmap->push_back(visual.Bitmap);
|
||||||
auto visVec = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, index, 501));
|
auto visVec = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, index, 501));
|
||||||
auto zDepth = proj::z_distance(visVec);
|
auto zDepth = proj::z_distance(*visVec);
|
||||||
VisualZArray[index] = zDepth;
|
VisualZArray[index] = zDepth;
|
||||||
}
|
}
|
||||||
RenderSprite = render::create_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr);
|
RenderSprite = new render_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr);
|
||||||
PinballTable->CollisionCompOffset = Offset;
|
PinballTable->CollisionCompOffset = Radius;
|
||||||
Position.Z = Offset;
|
Position.Z = Radius;
|
||||||
|
GroupIndex = groupIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TBall::Repaint()
|
void TBall::Repaint()
|
||||||
{
|
{
|
||||||
int pos2D[2];
|
|
||||||
|
|
||||||
if (CollisionFlag)
|
if (CollisionFlag)
|
||||||
{
|
{
|
||||||
Position.Z =
|
Position.Z =
|
||||||
CollisionOffset.X * Position.X +
|
CollisionOffset.X * Position.X +
|
||||||
CollisionOffset.Y * Position.Y +
|
CollisionOffset.Y * Position.Y +
|
||||||
Offset + CollisionOffset.Z;
|
Radius + CollisionOffset.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
proj::xform_to_2d(&Position, pos2D);
|
auto pos2D = proj::xform_to_2d(Position);
|
||||||
auto zDepth = proj::z_distance(&Position);
|
auto zDepth = proj::z_distance(Position);
|
||||||
|
|
||||||
auto zArrPtr = VisualZArray;
|
|
||||||
auto index = 0u;
|
auto index = 0u;
|
||||||
for (; index < ListBitmap->size() - 1; ++index, zArrPtr++)
|
for (; index < ListBitmap->size() - 1; ++index)
|
||||||
{
|
{
|
||||||
if (*zArrPtr <= zDepth) break;
|
if (VisualZArray[index] <= zDepth) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bmp = ListBitmap->at(index);
|
SpriteSetBall(index, pos2D, zDepth);
|
||||||
render::ball_set(
|
|
||||||
RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zDepth,
|
|
||||||
pos2D[0] - bmp->Width / 2,
|
|
||||||
pos2D[1] - bmp->Height / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TBall::not_again(TEdgeSegment* edge)
|
void TBall::not_again(TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
if (EdgeCollisionCount < 5)
|
if (EdgeCollisionCount < 16)
|
||||||
{
|
{
|
||||||
Collisions[EdgeCollisionCount] = edge;
|
Collisions[EdgeCollisionCount] = edge;
|
||||||
++EdgeCollisionCount;
|
++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++)
|
for (int i = 0; i < EdgeCollisionCount; i++)
|
||||||
{
|
{
|
||||||
if (Collisions[i] == edge)
|
if (Collisions[i] == &edge)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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;
|
Position.X = 0.0;
|
||||||
CollisionComp = nullptr;
|
CollisionComp = nullptr;
|
||||||
Position.Y = 0.0;
|
Position.Y = 0.0;
|
||||||
ActiveFlag = 0;
|
ActiveFlag = 0;
|
||||||
CollisionFlag = 0;
|
CollisionFlag = 0;
|
||||||
FieldFlag = 1;
|
CollisionMask = 1;
|
||||||
Acceleration.Y = 0.0;
|
Direction.Y = 0.0;
|
||||||
Position.Z = Offset;
|
Position.Z = Radius;
|
||||||
Acceleration.X = 0.0;
|
Direction.X = 0.0;
|
||||||
Speed = 0.0;
|
Speed = 0.0;
|
||||||
RayMaxDistance = 0.0;
|
RayMaxDistance = 0.0;
|
||||||
}
|
}
|
||||||
return 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;
|
CollisionComp = nullptr;
|
||||||
ball->Acceleration = *acceleration;
|
Direction = *direction;
|
||||||
float rnd = RandFloat();
|
float rnd = RandFloat();
|
||||||
float angle = (1.0f - (rnd + rnd)) * angleMult;
|
float angle = (1.0f - (rnd + rnd)) * angleMult;
|
||||||
maths::RotateVector(&ball->Acceleration, angle);
|
maths::RotateVector(Direction, angle);
|
||||||
rnd = RandFloat();
|
rnd = RandFloat();
|
||||||
ball->Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
|
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,36 +1,41 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "maths.h"
|
#include "maths.h"
|
||||||
#include "TPinballComponent.h"
|
#include "TCollisionComponent.h"
|
||||||
|
#include "TEdgeSegment.h"
|
||||||
|
|
||||||
class TCollisionComponent;
|
class TBall : public TCollisionComponent, public TEdgeSegment
|
||||||
class TEdgeSegment;
|
|
||||||
|
|
||||||
class TBall : public TPinballComponent
|
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
TBall(TPinballTable* table);
|
TBall(TPinballTable* table, int groupIndex);
|
||||||
void Repaint();
|
void Repaint();
|
||||||
void not_again(TEdgeSegment* edge);
|
void not_again(TEdgeSegment* edge);
|
||||||
bool already_hit(TEdgeSegment* edge);
|
bool already_hit(const TEdgeSegment& edge) const;
|
||||||
int Message(int code, float value) override;
|
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,
|
vector3 Position{};
|
||||||
float speedMult2);
|
vector3 PrevPosition{};
|
||||||
|
vector3 Direction{};
|
||||||
vector_type Position{};
|
|
||||||
vector_type Acceleration{};
|
|
||||||
float Speed;
|
float Speed;
|
||||||
float RayMaxDistance;
|
float RayMaxDistance;
|
||||||
float TimeDelta;
|
float TimeDelta;
|
||||||
float TimeNow;
|
vector2 RampFieldForce{};
|
||||||
vector_type InvAcceleration{};
|
|
||||||
vector_type RampFieldForce{};
|
|
||||||
TCollisionComponent* CollisionComp;
|
TCollisionComponent* CollisionComp;
|
||||||
int FieldFlag;
|
int CollisionMask;
|
||||||
TEdgeSegment* Collisions[5]{};
|
TEdgeSegment* Collisions[16]{};
|
||||||
int EdgeCollisionCount;
|
int EdgeCollisionCount;
|
||||||
vector_type CollisionOffset{};
|
bool EdgeCollisionResetFlag{};
|
||||||
|
vector3 CollisionOffset{};
|
||||||
int CollisionFlag;
|
int CollisionFlag;
|
||||||
float Offset;
|
float Radius;
|
||||||
|
bool HasGroupFlag;
|
||||||
|
int StuckCounter = 0;
|
||||||
|
int LastActiveTime{};
|
||||||
float VisualZArray[50]{};
|
float VisualZArray[50]{};
|
||||||
|
bool CollisionDisabledFlag{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,23 +14,23 @@ TBlocker::TBlocker(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
SoundIndex4 = visual.SoundIndex4;
|
SoundIndex4 = visual.SoundIndex4;
|
||||||
SoundIndex3 = visual.SoundIndex3;
|
SoundIndex3 = visual.SoundIndex3;
|
||||||
TurnOnMsgValue = 55;
|
InitialDuration = 55;
|
||||||
TurnOffMsgValue = 5;
|
ExtendedDuration = 5;
|
||||||
Threshold = 1000000000.0f;
|
Threshold = 1000000000.0f;
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
ActiveFlag = 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)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1011:
|
case MessageCode::SetTiltLock:
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
case 51:
|
case MessageCode::TBlockerDisable:
|
||||||
if (Timer)
|
if (Timer)
|
||||||
{
|
{
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
|
@ -38,29 +38,29 @@ int TBlocker::Message(int code, float value)
|
||||||
}
|
}
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
ActiveFlag = 0;
|
ActiveFlag = 0;
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
SpriteSet(-1);
|
||||||
if (code == 51)
|
if (code == MessageCode::TBlockerDisable)
|
||||||
loader::play_sound(SoundIndex3);
|
loader::play_sound(SoundIndex3, this, "TBlocker1");
|
||||||
return 0;
|
break;
|
||||||
case 52:
|
case MessageCode::TBlockerEnable:
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
loader::play_sound(SoundIndex4);
|
loader::play_sound(SoundIndex4, this, "TBlocker2");
|
||||||
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
|
SpriteSet(0);
|
||||||
break;
|
|
||||||
case 59:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(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:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
float timerTime;
|
|
||||||
if (value <= 0.0f)
|
|
||||||
timerTime = 0.0;
|
|
||||||
else
|
|
||||||
timerTime = value;
|
|
||||||
Timer = timer::set(timerTime, this, TimerExpired);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,5 +68,5 @@ void TBlocker::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto blocker = static_cast<TBlocker*>(caller);
|
auto blocker = static_cast<TBlocker*>(caller);
|
||||||
blocker->Timer = 0;
|
blocker->Timer = 0;
|
||||||
control::handler(60, blocker);
|
control::handler(MessageCode::ControlTimerExpired, blocker);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@ class TBlocker :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TBlocker(TPinballTable* table, int groupIndex);
|
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);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int TurnOnMsgValue;
|
int InitialDuration;
|
||||||
int TurnOffMsgValue;
|
int ExtendedDuration;
|
||||||
int Timer;
|
int Timer;
|
||||||
int SoundIndex4;
|
int SoundIndex4;
|
||||||
int SoundIndex3;
|
int SoundIndex3;
|
||||||
|
|
|
@ -21,11 +21,11 @@ TBumper::TBumper(TPinballTable* table, int groupIndex) : TCollisionComponent(tab
|
||||||
OriginalThreshold = Threshold;
|
OriginalThreshold = Threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TBumper::Message(int code, float value)
|
int TBumper::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 11:
|
case MessageCode::TBumperSetBmpIndex:
|
||||||
{
|
{
|
||||||
auto nextBmp = static_cast<int>(floor(value));
|
auto nextBmp = static_cast<int>(floor(value));
|
||||||
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
||||||
|
@ -36,33 +36,33 @@ int TBumper::Message(int code, float value)
|
||||||
if (nextBmp != BmpIndex)
|
if (nextBmp != BmpIndex)
|
||||||
{
|
{
|
||||||
if (nextBmp >= BmpIndex)
|
if (nextBmp >= BmpIndex)
|
||||||
loader::play_sound(SoundIndex4);
|
loader::play_sound(SoundIndex4, this, "TBumper1");
|
||||||
if (nextBmp < BmpIndex)
|
if (nextBmp < BmpIndex)
|
||||||
loader::play_sound(SoundIndex3);
|
loader::play_sound(SoundIndex3, this, "TBumper2");
|
||||||
BmpIndex = nextBmp;
|
BmpIndex = nextBmp;
|
||||||
Fire();
|
Fire();
|
||||||
control::handler(11, this);
|
control::handler(MessageCode::TBumperSetBmpIndex, this);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 12:
|
case MessageCode::TBumperIncBmpIndex:
|
||||||
{
|
{
|
||||||
auto nextBmp = BmpIndex + 1;
|
auto nextBmp = BmpIndex + 1;
|
||||||
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
auto maxBmp = static_cast<int>(ListBitmap->size()) - 1;
|
||||||
if (2 * nextBmp > maxBmp)
|
if (2 * nextBmp > maxBmp)
|
||||||
nextBmp = maxBmp / 2;
|
nextBmp = maxBmp / 2;
|
||||||
TBumper::Message(11, static_cast<float>(nextBmp));
|
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(nextBmp));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 13:
|
case MessageCode::TBumperDecBmpIndex:
|
||||||
{
|
{
|
||||||
auto nextBmp = BmpIndex - 1;
|
auto nextBmp = BmpIndex - 1;
|
||||||
if (nextBmp < 0)
|
if (nextBmp < 0)
|
||||||
nextBmp = 0;
|
nextBmp = 0;
|
||||||
TBumper::Message(11, static_cast<float>(nextBmp));
|
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(nextBmp));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
{
|
{
|
||||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||||
playerPtr->BmpIndex = BmpIndex;
|
playerPtr->BmpIndex = BmpIndex;
|
||||||
|
@ -71,10 +71,10 @@ int TBumper::Message(int code, float value)
|
||||||
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
||||||
BmpIndex = playerPtr->BmpIndex;
|
BmpIndex = playerPtr->BmpIndex;
|
||||||
MessageField = playerPtr->MessageField;
|
MessageField = playerPtr->MessageField;
|
||||||
TBumper::Message(11, static_cast<float>(BmpIndex));
|
TBumper::Message(MessageCode::TBumperSetBmpIndex, static_cast<float>(BmpIndex));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
{
|
{
|
||||||
if (Timer)
|
if (Timer)
|
||||||
{
|
{
|
||||||
|
@ -83,12 +83,10 @@ int TBumper::Message(int code, float value)
|
||||||
}
|
}
|
||||||
BmpIndex = 0;
|
BmpIndex = 0;
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
auto playerPtr = PlayerData;
|
for (auto& playerPtr : PlayerData)
|
||||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
|
||||||
{
|
{
|
||||||
playerPtr->BmpIndex = 0;
|
playerPtr.BmpIndex = 0;
|
||||||
playerPtr->MessageField = 0;
|
playerPtr.MessageField = 0;
|
||||||
++playerPtr;
|
|
||||||
}
|
}
|
||||||
TimerExpired(0, this);
|
TimerExpired(0, this);
|
||||||
break;
|
break;
|
||||||
|
@ -100,53 +98,26 @@ int TBumper::Message(int code, float value)
|
||||||
return 0;
|
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))
|
if (DefaultCollision(ball, nextPosition, direction))
|
||||||
{
|
{
|
||||||
Fire();
|
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)
|
void TBumper::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto bump = static_cast<TBumper*>(caller);
|
auto bump = static_cast<TBumper*>(caller);
|
||||||
auto bmp = bump->ListBitmap->at(bump->BmpIndex * 2);
|
bump->SpriteSet(bump->BmpIndex * 2);
|
||||||
auto zMap = bump->ListZMap->at(bump->BmpIndex * 2);
|
|
||||||
bump->Timer = 0;
|
bump->Timer = 0;
|
||||||
render::sprite_set(
|
|
||||||
bump->RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - bump->PinballTable->XOffset,
|
|
||||||
bmp->YPosition - bump->PinballTable->YOffset);
|
|
||||||
bump->Threshold = bump->OriginalThreshold;
|
bump->Threshold = bump->OriginalThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TBumper::Fire()
|
void TBumper::Fire()
|
||||||
{
|
{
|
||||||
int bmpIndex = 2 * BmpIndex + 1;
|
SpriteSet(2 * BmpIndex + 1);
|
||||||
auto bmp = ListBitmap->at(bmpIndex);
|
|
||||||
auto zMap = ListZMap->at(bmpIndex);
|
|
||||||
render::sprite_set(
|
|
||||||
RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - PinballTable->XOffset,
|
|
||||||
bmp->YPosition - PinballTable->YOffset);
|
|
||||||
Timer = timer::set(TimerTime, this, TimerExpired);
|
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1000000000.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,9 @@ class TBumper :
|
||||||
public:
|
public:
|
||||||
TBumper(TPinballTable* table, int groupIndex);
|
TBumper(TPinballTable* table, int groupIndex);
|
||||||
~TBumper() override = default;
|
~TBumper() override = default;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
void put_scoring(int index, int score) override;
|
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Fire();
|
void Fire();
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
@ -28,6 +26,5 @@ public:
|
||||||
float OriginalThreshold;
|
float OriginalThreshold;
|
||||||
int SoundIndex4;
|
int SoundIndex4;
|
||||||
int SoundIndex3;
|
int SoundIndex3;
|
||||||
int Scores[4]{};
|
|
||||||
TBumper_player_backup PlayerData[4]{};
|
TBumper_player_backup PlayerData[4]{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,31 +5,40 @@
|
||||||
#include "TCollisionComponent.h"
|
#include "TCollisionComponent.h"
|
||||||
#include "TTableLayer.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)
|
float radius): TEdgeSegment(collComp, activeFlag, collisionGroup)
|
||||||
{
|
{
|
||||||
Circle.RadiusSq = radius * radius;
|
Circle.RadiusSq = radius * radius;
|
||||||
Circle.Center = *center;
|
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.X = distance * ball->Direction.X + ball->Position.X;
|
||||||
nextPosition.Y = coef * ball->Acceleration.Y + ball->Position.Y;
|
nextPosition.Y = distance * ball->Direction.Y + ball->Position.Y;
|
||||||
direction.X = nextPosition.X - Circle.Center.X;
|
direction.X = nextPosition.X - Circle.Center.X;
|
||||||
direction.Y = nextPosition.Y - Circle.Center.Y;
|
direction.Y = nextPosition.Y - Circle.Center.Y;
|
||||||
maths::normalize_2d(&direction);
|
maths::normalize_2d(direction);
|
||||||
CollisionComponent->Collision(ball, &nextPosition, &direction, coef, this);
|
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);
|
TTableLayer::edges_insert_circle(&Circle, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ class TCircle :
|
||||||
public:
|
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 radius);
|
||||||
float FindCollisionDistance(ray_type* ray) override;
|
float FindCollisionDistance(const ray_type& ray) override;
|
||||||
void EdgeCollision(TBall* ball, float coef) override;
|
void EdgeCollision(TBall* ball, float distance) override;
|
||||||
void place_in_grid() override;
|
void place_in_grid(RectF* aabb) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "maths.h"
|
#include "maths.h"
|
||||||
#include "TEdgeSegment.h"
|
#include "TEdgeSegment.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.h"
|
||||||
|
#include "TBall.h"
|
||||||
|
|
||||||
|
|
||||||
TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall) :
|
TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall) :
|
||||||
|
@ -12,6 +13,7 @@ TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, b
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
|
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
|
AABB = { -10000, -10000, 10000, 10000 };
|
||||||
if (GroupName != nullptr)
|
if (GroupName != nullptr)
|
||||||
UnusedBaseFlag = 1;
|
UnusedBaseFlag = 1;
|
||||||
if (groupIndex <= 0)
|
if (groupIndex <= 0)
|
||||||
|
@ -51,38 +53,36 @@ void TCollisionComponent::port_draw()
|
||||||
edge->port_draw();
|
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)
|
if (PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
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);
|
auto projSpeed = maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, Boost);
|
||||||
if (projSpeed <= Threshold)
|
if (projSpeed > Threshold)
|
||||||
{
|
{
|
||||||
if (projSpeed > 0.2f)
|
loader::play_sound(HardHitSoundId, ball, "TCollisionComponent1");
|
||||||
{
|
collision = true;
|
||||||
if (SoftHitSoundId)
|
|
||||||
loader::play_sound(SoftHitSoundId);
|
|
||||||
}
|
}
|
||||||
return 0;
|
else if (projSpeed > 0.2f)
|
||||||
}
|
loader::play_sound(SoftHitSoundId, ball, "TCollisionComponent2");
|
||||||
if (HardHitSoundId)
|
|
||||||
loader::play_sound(HardHitSoundId);
|
return collision;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCollisionComponent::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction,
|
void TCollisionComponent::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
|
||||||
float coef, TEdgeSegment* edge)
|
float distance, TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
int soundIndex;
|
|
||||||
|
|
||||||
if (PinballTable->TiltLockFlag)
|
if (PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto projSpeed = maths::basic_collision(
|
auto projSpeed = maths::basic_collision(
|
||||||
ball,
|
ball,
|
||||||
nextPosition,
|
nextPosition,
|
||||||
|
@ -91,21 +91,13 @@ void TCollisionComponent::Collision(TBall* ball, vector_type* nextPosition, vect
|
||||||
Smoothness,
|
Smoothness,
|
||||||
Threshold,
|
Threshold,
|
||||||
Boost);
|
Boost);
|
||||||
if (projSpeed <= Threshold)
|
if (projSpeed > Threshold)
|
||||||
{
|
loader::play_sound(HardHitSoundId, ball, "TCollisionComponent3");
|
||||||
if (projSpeed <= 0.2f)
|
else if (projSpeed > 0.2f)
|
||||||
return;
|
loader::play_sound(SoftHitSoundId, ball, "TCollisionComponent4");
|
||||||
soundIndex = SoftHitSoundId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
soundIndex = HardHitSoundId;
|
|
||||||
}
|
|
||||||
if (soundIndex)
|
|
||||||
loader::play_sound(soundIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TCollisionComponent::FieldEffect(TBall* ball, vector_type* vecDst)
|
int TCollisionComponent::FieldEffect(TBall* ball, vector2* vecDst)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "maths.h"
|
||||||
#include "TPinballComponent.h"
|
#include "TPinballComponent.h"
|
||||||
|
|
||||||
struct vector_type;
|
struct vector2;
|
||||||
class TEdgeSegment;
|
class TEdgeSegment;
|
||||||
class TBall;
|
class TBall;
|
||||||
|
|
||||||
|
@ -15,12 +16,13 @@ public:
|
||||||
float Threshold;
|
float Threshold;
|
||||||
int SoftHitSoundId;
|
int SoftHitSoundId;
|
||||||
int HardHitSoundId;
|
int HardHitSoundId;
|
||||||
|
RectF AABB;
|
||||||
|
|
||||||
TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall);
|
TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall);
|
||||||
~TCollisionComponent() override;
|
~TCollisionComponent() override;
|
||||||
void port_draw() 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);
|
TEdgeSegment* edge);
|
||||||
virtual int FieldEffect(TBall* ball, vector_type* vecDst);
|
virtual int FieldEffect(TBall* ball, vector2* vecDst);
|
||||||
int DefaultCollision(TBall* ball, vector_type* nextPosition, vector_type* direction);
|
bool DefaultCollision(TBall* ball, vector2* nextPosition, vector2* direction);
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,9 +33,9 @@ TComponentGroup::~TComponentGroup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TComponentGroup::Message(int code, float value)
|
int TComponentGroup::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
if (code == 48)
|
if (code == MessageCode::TComponentGroupResetNotifyTimer)
|
||||||
{
|
{
|
||||||
if (this->Timer)
|
if (this->Timer)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,8 @@ int TComponentGroup::Message(int code, float value)
|
||||||
if (value > 0.0f)
|
if (value > 0.0f)
|
||||||
this->Timer = timer::set(value, this, NotifyTimerExpired);
|
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 (auto component : List)
|
for (auto component : List)
|
||||||
{
|
{
|
||||||
|
@ -59,5 +60,5 @@ void TComponentGroup::NotifyTimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto compGroup = static_cast<TComponentGroup*>(caller);
|
auto compGroup = static_cast<TComponentGroup*>(caller);
|
||||||
compGroup->Timer = 0;
|
compGroup->Timer = 0;
|
||||||
control::handler(61, compGroup);
|
control::handler(MessageCode::ControlNotifyTimerExpired, compGroup);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ class TComponentGroup :
|
||||||
public:
|
public:
|
||||||
TComponentGroup(TPinballTable* table, int groupIndex);
|
TComponentGroup(TPinballTable* table, int groupIndex);
|
||||||
~TComponentGroup() override;
|
~TComponentGroup() override;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
static void NotifyTimerExpired(int timerId, void* caller);
|
static void NotifyTimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
std::vector<TPinballComponent*> List;
|
std::vector<TPinballComponent*> List;
|
||||||
|
|
|
@ -56,23 +56,23 @@ TDemo::TDemo(TPinballTable* table, int groupIndex)
|
||||||
Edge3 = TEdgeSegment::install_wall(v9, this, &ActiveFlag, visual.CollisionGroup, table->CollisionCompOffset, 1404);
|
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)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1014:
|
case MessageCode::NewGame:
|
||||||
if (RestartGameTimer)
|
if (RestartGameTimer)
|
||||||
timer::kill(RestartGameTimer);
|
timer::kill(RestartGameTimer);
|
||||||
RestartGameTimer = 0;
|
RestartGameTimer = 0;
|
||||||
break;
|
break;
|
||||||
case 1022:
|
case MessageCode::GameOver:
|
||||||
if (RestartGameTimer)
|
if (RestartGameTimer)
|
||||||
timer::kill(RestartGameTimer);
|
timer::kill(RestartGameTimer);
|
||||||
RestartGameTimer = 0;
|
RestartGameTimer = 0;
|
||||||
if (ActiveFlag != 0)
|
if (ActiveFlag != 0)
|
||||||
RestartGameTimer = timer::set(5.0, this, NewGameRestartTimer);
|
RestartGameTimer = timer::set(5.0, this, NewGameRestartTimer);
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
if (FlipLeftTimer)
|
if (FlipLeftTimer)
|
||||||
timer::kill(FlipLeftTimer);
|
timer::kill(FlipLeftTimer);
|
||||||
FlipLeftTimer = 0;
|
FlipLeftTimer = 0;
|
||||||
|
@ -93,12 +93,12 @@ int TDemo::Message(int code, float value)
|
||||||
return 0;
|
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->not_again(edge);
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
|
|
||||||
switch (reinterpret_cast<size_t>(edge->WallValue))
|
switch (reinterpret_cast<size_t>(edge->WallValue))
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ void TDemo::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||||
case 1404:
|
case 1404:
|
||||||
if (!PlungerFlag)
|
if (!PlungerFlag)
|
||||||
{
|
{
|
||||||
PinballTable->Message(1004, ball->TimeNow);
|
PinballTable->Message(MessageCode::PlungerInputPressed, 0);
|
||||||
float time = RandFloat() + 2.0f;
|
float time = RandFloat() + 2.0f;
|
||||||
PlungerFlag = timer::set(time, this, PlungerRelease);
|
PlungerFlag = timer::set(time, this, PlungerRelease);
|
||||||
}
|
}
|
||||||
|
@ -139,14 +139,14 @@ void TDemo::PlungerRelease(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto demo = static_cast<TDemo*>(caller);
|
auto demo = static_cast<TDemo*>(caller);
|
||||||
demo->PlungerFlag = 0;
|
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)
|
void TDemo::UnFlipRight(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto demo = static_cast<TDemo*>(caller);
|
auto demo = static_cast<TDemo*>(caller);
|
||||||
if (demo->FlipRightFlag)
|
if (demo->FlipRightFlag)
|
||||||
demo->PinballTable->Message(1003, pb::time_next);
|
demo->PinballTable->Message(MessageCode::RightFlipperInputReleased, pb::time_next);
|
||||||
demo->FlipRightFlag = 0;
|
demo->FlipRightFlag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ void TDemo::UnFlipLeft(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto demo = static_cast<TDemo*>(caller);
|
auto demo = static_cast<TDemo*>(caller);
|
||||||
if (demo->FlipLeftFlag)
|
if (demo->FlipLeftFlag)
|
||||||
demo->PinballTable->Message(1001, pb::time_next);
|
demo->PinballTable->Message(MessageCode::LeftFlipperInputReleased, pb::time_next);
|
||||||
demo->FlipLeftFlag = 0;
|
demo->FlipLeftFlag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ void TDemo::FlipRight(int timerId, void* caller)
|
||||||
timer::kill(demo->FlipRightTimer);
|
timer::kill(demo->FlipRightTimer);
|
||||||
demo->FlipRightTimer = 0;
|
demo->FlipRightTimer = 0;
|
||||||
}
|
}
|
||||||
demo->PinballTable->Message(1002, pb::time_next);
|
demo->PinballTable->Message(MessageCode::RightFlipperInputPressed, pb::time_next);
|
||||||
demo->FlipRightFlag = 1;
|
demo->FlipRightFlag = 1;
|
||||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
||||||
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||||
|
@ -186,7 +186,7 @@ void TDemo::FlipLeft(int timerId, void* caller)
|
||||||
timer::kill(demo->FlipLeftTimer);
|
timer::kill(demo->FlipLeftTimer);
|
||||||
demo->FlipLeftTimer = 0;
|
demo->FlipLeftTimer = 0;
|
||||||
}
|
}
|
||||||
demo->PinballTable->Message(1000, pb::time_next);
|
demo->PinballTable->Message(MessageCode::LeftFlipperInputPressed, pb::time_next);
|
||||||
demo->FlipLeftFlag = 1;
|
demo->FlipLeftFlag = 1;
|
||||||
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
float time = demo->UnFlipTimerTime1 + demo->UnFlipTimerTime2 - RandFloat() *
|
||||||
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
(demo->UnFlipTimerTime2 + demo->UnFlipTimerTime2);
|
||||||
|
@ -197,7 +197,7 @@ void TDemo::FlipLeft(int timerId, void* caller)
|
||||||
void TDemo::NewGameRestartTimer(int timerId, void* caller)
|
void TDemo::NewGameRestartTimer(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto demo = static_cast<TDemo*>(caller);
|
auto demo = static_cast<TDemo*>(caller);
|
||||||
pb::replay_level(1);
|
pb::replay_level(true);
|
||||||
demo->PinballTable->Message(1014, static_cast<float>(demo->PinballTable->PlayerCount));
|
demo->PinballTable->Message(MessageCode::NewGame, static_cast<float>(demo->PinballTable->PlayerCount));
|
||||||
demo->RestartGameTimer = 0;
|
demo->RestartGameTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ class TDemo :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TDemo(TPinballTable* table, int groupIndex);
|
TDemo(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void PlungerRelease(int timerId, void* caller);
|
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);
|
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)
|
if (Timer)
|
||||||
{
|
{
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
PinballTable->BallInSink = 0;
|
PinballTable->BallInDrainFlag = 0;
|
||||||
}
|
}
|
||||||
return 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);
|
ball->Disable();
|
||||||
PinballTable->BallInSink = 1;
|
--PinballTable->MultiballCount;
|
||||||
|
if (PinballTable->MultiballCount <= 0)
|
||||||
|
{
|
||||||
|
PinballTable->MultiballCount = 0;
|
||||||
|
PinballTable->BallInDrainFlag = 1;
|
||||||
Timer = timer::set(TimerTime, this, TimerCallback);
|
Timer = timer::set(TimerTime, this, TimerCallback);
|
||||||
control::handler(63, this);
|
}
|
||||||
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TDrain::TimerCallback(int timerId, void* caller)
|
void TDrain::TimerCallback(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto drain = static_cast<TDrain*>(caller);
|
auto drain = static_cast<TDrain*>(caller);
|
||||||
control::handler(60, drain);
|
control::handler(MessageCode::ControlTimerExpired, drain);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ class TDrain :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TDrain(TPinballTable* table, int groupIndex);
|
TDrain(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void TimerCallback(int timerId, void* caller);
|
static void TimerCallback(int timerId, void* caller);
|
||||||
|
|
|
@ -8,17 +8,19 @@
|
||||||
#include "TEdgeSegment.h"
|
#include "TEdgeSegment.h"
|
||||||
#include "TTableLayer.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;
|
Width = width;
|
||||||
Y = posY;
|
Height = height;
|
||||||
|
MinX = xMin;
|
||||||
|
MinY = yMin;
|
||||||
|
MaxX = MinX + width;
|
||||||
|
MaxY = MinY + height;
|
||||||
MaxBoxX = 10;
|
MaxBoxX = 10;
|
||||||
MaxBoxY = 15;
|
MaxBoxY = 15;
|
||||||
AdvanceX = width / 10.0f;
|
AdvanceX = width / static_cast<float>(MaxBoxX);
|
||||||
AdvanceY = height / 15.0f;
|
AdvanceY = height / static_cast<float>(MaxBoxY);
|
||||||
AdvanceXInv = 1.0f / AdvanceX;
|
BoxArray = new TEdgeBox[MaxBoxX * MaxBoxY];
|
||||||
AdvanceYInv = 1.0f / AdvanceY;
|
|
||||||
BoxArray = new TEdgeBox[150];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEdgeManager::~TEdgeManager()
|
TEdgeManager::~TEdgeManager()
|
||||||
|
@ -28,12 +30,12 @@ TEdgeManager::~TEdgeManager()
|
||||||
|
|
||||||
int TEdgeManager::box_x(float x)
|
int TEdgeManager::box_x(float x)
|
||||||
{
|
{
|
||||||
return std::max(0, std::min(static_cast<int>(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)
|
int TEdgeManager::box_y(float y)
|
||||||
{
|
{
|
||||||
return std::max(0, std::min(static_cast<int>(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)
|
int TEdgeManager::increment_box_x(int x)
|
||||||
|
@ -48,33 +50,41 @@ int TEdgeManager::increment_box_y(int y)
|
||||||
|
|
||||||
void TEdgeManager::add_edge_to_box(int x, int y, TEdgeSegment* edge)
|
void TEdgeManager::add_edge_to_box(int x, int y, TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
BoxArray[x + y * MaxBoxX].EdgeList.push_back(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)
|
void TEdgeManager::add_field_to_box(int x, int y, field_effect_type* field)
|
||||||
{
|
{
|
||||||
BoxArray[x + y * MaxBoxX].FieldList.push_back(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 TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeDst, ray_type* ray, TBall* ball,
|
||||||
int edgeIndex)
|
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];
|
TEdgeBox* edgeBox = &BoxArray[x + y * MaxBoxX];
|
||||||
TEdgeSegment** edgePtr = &EdgeArray[edgeIndex];
|
TEdgeSegment** edgePtr = &EdgeArray[edgeIndex];
|
||||||
for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it)
|
for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto edge = *it;
|
auto edge = *it;
|
||||||
if (!edge->ProcessedFlag && *edge->ActiveFlag && (edge->CollisionGroup & ray->FieldFlag))
|
if (!edge->ProcessedFlag && *edge->ActiveFlagPtr && (edge->CollisionGroup & ray->CollisionMask) != 0)
|
||||||
{
|
{
|
||||||
if (!ball->already_hit(edge))
|
if (!ball->already_hit(*edge))
|
||||||
{
|
{
|
||||||
++edgeIndex;
|
++edgeIndex;
|
||||||
*edgePtr = edge;
|
*edgePtr = edge;
|
||||||
++edgePtr;
|
++edgePtr;
|
||||||
edge->ProcessedFlag = 1;
|
edge->ProcessedFlag = 1;
|
||||||
auto dist = edge->FindCollisionDistance(ray);
|
auto dist = edge->FindCollisionDistance(*ray);
|
||||||
if (dist < *distPtr)
|
if (dist < *distPtr)
|
||||||
{
|
{
|
||||||
*distPtr = dist;
|
*distPtr = dist;
|
||||||
|
@ -87,20 +97,20 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD
|
||||||
return edgeIndex;
|
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) *
|
TEdgeBox* edgeBox = &BoxArray[box_x(ball->Position.X) + box_y(ball->Position.Y) *
|
||||||
MaxBoxX];
|
MaxBoxX];
|
||||||
|
|
||||||
for (auto it = edgeBox->FieldList.rbegin(); it != edgeBox->FieldList.rend(); ++it)
|
for (auto it = edgeBox->FieldList.rbegin(); it != edgeBox->FieldList.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto field = *it;
|
auto field = *it;
|
||||||
if (*field->Flag2Ptr && ball->FieldFlag & field->Mask)
|
if (*field->ActiveFlag && ball->CollisionMask & field->CollisionGroup)
|
||||||
{
|
{
|
||||||
if (field->CollisionComp->FieldEffect(ball, &vec))
|
if (field->CollisionComp->FieldEffect(ball, &vec))
|
||||||
{
|
{
|
||||||
maths::vector_add(dstVec, &vec);
|
maths::vector_add(*dstVec, vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,158 +121,84 @@ float TEdgeManager::FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegme
|
||||||
auto distance = 1000000000.0f;
|
auto distance = 1000000000.0f;
|
||||||
auto edgeIndex = 0;
|
auto edgeIndex = 0;
|
||||||
|
|
||||||
auto rayX = ray->Origin.X;
|
auto x0 = ray->Origin.X;
|
||||||
auto rayY = ray->Origin.Y;
|
auto y0 = ray->Origin.Y;
|
||||||
auto rayBoxX = box_x(rayX);
|
auto x1 = ray->Direction.X * ray->MaxDistance + ray->Origin.X;
|
||||||
auto rayBoxY = box_y(rayY);
|
auto y1 = ray->Direction.Y * ray->MaxDistance + ray->Origin.Y;
|
||||||
|
|
||||||
auto rayEndX = ray->Direction.X * ray->MaxDistance + ray->Origin.X;
|
auto xBox0 = box_x(x0);
|
||||||
auto rayEndY = ray->Direction.Y * ray->MaxDistance + ray->Origin.Y;
|
auto yBox0 = box_y(y0);
|
||||||
auto rayEndBoxX = box_x(rayEndX);
|
auto xBox1 = box_x(x1);
|
||||||
auto rayEndBoxY = box_y(rayEndY);
|
auto yBox1 = box_y(y1);
|
||||||
|
|
||||||
auto rayDirX = rayX >= rayEndX ? -1 : 1;
|
auto dirX = x0 >= x1 ? -1 : 1;
|
||||||
auto rayDirY = rayY >= rayEndY ? -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
|
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
|
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
|
else
|
||||||
{
|
{
|
||||||
auto rayDyDX = (rayY - rayEndY) / (rayX - rayEndX);
|
edgeIndex = TestGridBox(xBox0, yBox0, &distance, edge, ray, ball, 0);
|
||||||
auto indexX = rayBoxX;
|
|
||||||
auto indexY = rayBoxY;
|
// Bresenham line formula: y = dYdX * (x - x0) + y0; dYdX = (y0 - y1) / (x0 - x1)
|
||||||
auto bresIndexX = rayBoxX + 1;
|
auto dyDx = (y0 - y1) / (x0 - x1);
|
||||||
auto bresIndexY = rayBoxY + 1;
|
// Precompute constant part: dYdX * (-x0) + y0
|
||||||
auto bresXAdd = rayY - rayDyDX * rayX;
|
auto precomp = -x0 * dyDx + y0;
|
||||||
edgeIndex = TestGridBox(rayBoxX, rayBoxY, &distance, edge, ray, ball, 0);
|
// X and Y indexes are offset by one when going forwards, not sure why
|
||||||
if (rayDirX == 1)
|
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
|
// Advance indexY when discrete value is ahead/behind
|
||||||
{
|
// Advance indexX when discrete value matches linear value
|
||||||
auto yCoord = bresIndexY * AdvanceY + Y;
|
indexY += dirY;
|
||||||
auto xCoord = (bresIndexX * AdvanceX + X) * rayDyDX + bresXAdd;
|
if (ylinear == yDiscrete)
|
||||||
if (xCoord >= yCoord)
|
indexX += dirX;
|
||||||
{
|
|
||||||
if (xCoord == yCoord)
|
|
||||||
{
|
|
||||||
++indexX;
|
|
||||||
++bresIndexX;
|
|
||||||
}
|
|
||||||
++indexY;
|
|
||||||
++bresIndexY;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++indexX;
|
// Advance indexX otherwise
|
||||||
++bresIndexX;
|
indexX += dirX;
|
||||||
}
|
}
|
||||||
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex);
|
||||||
}
|
}
|
||||||
while (indexX < rayEndBoxX || indexY < rayEndBoxY);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,3 +209,23 @@ float TEdgeManager::FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegme
|
||||||
|
|
||||||
return distance;
|
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
|
struct field_effect_type
|
||||||
{
|
{
|
||||||
char* Flag2Ptr;
|
char* ActiveFlag;
|
||||||
int Mask;
|
int CollisionGroup;
|
||||||
TCollisionComponent* CollisionComp;
|
TCollisionComponent* CollisionComp;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TEdgeManager
|
class TEdgeManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TEdgeManager(float posX, float posY, float width, float height);
|
TEdgeManager(float xMin, float yMin, float width, float height);
|
||||||
~TEdgeManager();
|
~TEdgeManager();
|
||||||
void FieldEffects(TBall* ball, struct vector_type* dstVec);
|
void FieldEffects(TBall* ball, struct vector2* dstVec);
|
||||||
int box_x(float x);
|
int box_x(float x);
|
||||||
int box_y(float y);
|
int box_y(float y);
|
||||||
int increment_box_x(int x);
|
int increment_box_x(int x);
|
||||||
|
@ -25,15 +25,19 @@ public:
|
||||||
void add_field_to_box(int x, int y, field_effect_type* field);
|
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);
|
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);
|
float FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegment** edge);
|
||||||
|
vector2 NormalizeBox(vector2 pt) const;
|
||||||
|
vector2 DeNormalizeBox(vector2 pt) const;
|
||||||
|
|
||||||
float AdvanceX;
|
float AdvanceX;
|
||||||
float AdvanceY;
|
float AdvanceY;
|
||||||
float AdvanceXInv;
|
|
||||||
float AdvanceYInv;
|
|
||||||
int MaxBoxX;
|
int MaxBoxX;
|
||||||
int MaxBoxY;
|
int MaxBoxY;
|
||||||
float X;
|
float MinX;
|
||||||
float Y;
|
float MinY;
|
||||||
|
float MaxX;
|
||||||
|
float MaxY;
|
||||||
|
float Width;
|
||||||
|
float Height;
|
||||||
TEdgeBox* BoxArray;
|
TEdgeBox* BoxArray;
|
||||||
TEdgeSegment* EdgeArray[1000]{};
|
TEdgeSegment* EdgeArray[1000]{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup)
|
TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup)
|
||||||
{
|
{
|
||||||
CollisionComponent = collComp;
|
CollisionComponent = collComp;
|
||||||
ActiveFlag = activeFlag;
|
ActiveFlagPtr = activeFlag;
|
||||||
CollisionGroup = collisionGroup;
|
CollisionGroup = collisionGroup;
|
||||||
ProcessedFlag = 0;
|
ProcessedFlag = 0;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@ void TEdgeSegment::port_draw()
|
||||||
TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
||||||
unsigned int collisionGroup, float offset, size_t 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;
|
TEdgeSegment* edge = nullptr;
|
||||||
|
|
||||||
wall_type wallType = static_cast<wall_type>(static_cast<int>(floor(*floatArr) - 1.0f));
|
wall_type wallType = static_cast<wall_type>(static_cast<int>(floor(*floatArr) - 1.0f));
|
||||||
|
@ -32,15 +33,10 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||||
center.Y = floatArr[2];
|
center.Y = floatArr[2];
|
||||||
auto radius = offset + floatArr[3];
|
auto radius = offset + floatArr[3];
|
||||||
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
||||||
edge = circle;
|
|
||||||
|
|
||||||
if (circle)
|
|
||||||
{
|
|
||||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||||
circle->place_in_grid();
|
circle->place_in_grid(&collComp->AABB);
|
||||||
}
|
|
||||||
|
|
||||||
collComp->EdgeList.push_back(circle);
|
collComp->EdgeList.push_back(circle);
|
||||||
|
edge = circle;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case wall_type::Line:
|
case wall_type::Line:
|
||||||
|
@ -49,16 +45,12 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||||
start.Y = floatArr[2];
|
start.Y = floatArr[2];
|
||||||
end.X = floatArr[3];
|
end.X = floatArr[3];
|
||||||
end.Y = floatArr[4];
|
end.Y = floatArr[4];
|
||||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, &start, &end);
|
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, start, end);
|
||||||
edge = line;
|
|
||||||
|
|
||||||
if (line)
|
|
||||||
{
|
|
||||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||||
line->Offset(offset);
|
line->Offset(offset);
|
||||||
line->place_in_grid();
|
line->place_in_grid(&collComp->AABB);
|
||||||
collComp->EdgeList.push_back(line);
|
collComp->EdgeList.push_back(line);
|
||||||
}
|
edge = line;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -92,37 +84,29 @@ TEdgeSegment* TEdgeSegment::install_wall(float* floatArr, TCollisionComponent* c
|
||||||
vec1.Y = center.Y - prevCenter.Y;
|
vec1.Y = center.Y - prevCenter.Y;
|
||||||
vec2.X = centerX2 - centerX1;
|
vec2.X = centerX2 - centerX1;
|
||||||
vec2.Y = centerY2 - center.Y;
|
vec2.Y = centerY2 - center.Y;
|
||||||
maths::cross(&vec1, &vec2, &dstVec);
|
maths::cross(vec1, vec2, dstVec);
|
||||||
if ((dstVec.Z > 0.0f && offset > 0.0f) ||
|
if ((dstVec.Z > 0.0f && offset > 0.0f) ||
|
||||||
(dstVec.Z < 0.0f && offset < 0.0f))
|
(dstVec.Z < 0.0f && offset < 0.0f))
|
||||||
{
|
{
|
||||||
float radius = offset * 1.001f;
|
float radius = offset * 1.001f;
|
||||||
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
auto circle = new TCircle(collComp, activeFlagPtr, collisionGroup, ¢er, radius);
|
||||||
|
|
||||||
if (circle)
|
|
||||||
{
|
|
||||||
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
circle->WallValue = reinterpret_cast<void*>(wallValue);
|
||||||
circle->place_in_grid();
|
circle->place_in_grid(&collComp->AABB);
|
||||||
collComp->EdgeList.push_back(circle);
|
collComp->EdgeList.push_back(circle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
start.X = floatArrPtr[0];
|
start.X = floatArrPtr[0];
|
||||||
start.Y = floatArrPtr[1];
|
start.Y = floatArrPtr[1];
|
||||||
end.X = floatArrPtr[2];
|
end.X = floatArrPtr[2];
|
||||||
end.Y = floatArrPtr[3];
|
end.Y = floatArrPtr[3];
|
||||||
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, &start, &end);
|
auto line = new TLine(collComp, activeFlagPtr, collisionGroup, start, end);
|
||||||
edge = line;
|
|
||||||
|
|
||||||
if (line)
|
|
||||||
{
|
|
||||||
line->WallValue = reinterpret_cast<void*>(wallValue);
|
line->WallValue = reinterpret_cast<void*>(wallValue);
|
||||||
line->Offset(offset);
|
line->Offset(offset);
|
||||||
line->place_in_grid();
|
line->place_in_grid(&collComp->AABB);
|
||||||
collComp->EdgeList.push_back(line);
|
collComp->EdgeList.push_back(line);
|
||||||
}
|
|
||||||
|
|
||||||
|
edge = line;
|
||||||
prevCenter = center;
|
prevCenter = center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class TBall;
|
class TBall;
|
||||||
class TCollisionComponent;
|
class TCollisionComponent;
|
||||||
struct ray_type;
|
struct ray_type;
|
||||||
|
struct RectF;
|
||||||
|
|
||||||
enum class wall_type : int
|
enum class wall_type : int
|
||||||
{
|
{
|
||||||
|
@ -14,7 +15,7 @@ class TEdgeSegment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TCollisionComponent* CollisionComponent;
|
TCollisionComponent* CollisionComponent;
|
||||||
char* ActiveFlag;
|
char* ActiveFlagPtr;
|
||||||
char ProcessedFlag;
|
char ProcessedFlag;
|
||||||
void* WallValue{};
|
void* WallValue{};
|
||||||
unsigned int CollisionGroup;
|
unsigned int CollisionGroup;
|
||||||
|
@ -22,10 +23,10 @@ public:
|
||||||
TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup);
|
TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup);
|
||||||
virtual ~TEdgeSegment() = default;
|
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 port_draw();
|
||||||
virtual void place_in_grid() = 0;
|
virtual void place_in_grid(RectF* aabb) = 0;
|
||||||
virtual float FindCollisionDistance(ray_type* ray) = 0;
|
virtual float FindCollisionDistance(const ray_type& ray) = 0;
|
||||||
|
|
||||||
static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
|
||||||
unsigned int collisionGroup, float offset, size_t wallValue);
|
unsigned int collisionGroup, float offset, size_t wallValue);
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||||
{
|
{
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
vector_type end{}, start{};
|
vector2 end{}, start{};
|
||||||
|
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
|
@ -21,18 +21,18 @@ TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionCom
|
||||||
end.Y = visual.FloatArr[1];
|
end.Y = visual.FloatArr[1];
|
||||||
start.X = visual.FloatArr[2];
|
start.X = visual.FloatArr[2];
|
||||||
start.Y = visual.FloatArr[3];
|
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)
|
if (line)
|
||||||
{
|
{
|
||||||
line->place_in_grid();
|
line->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(line);
|
EdgeList.push_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &end, &start);
|
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, end, start);
|
||||||
PrevCollider = line;
|
PrevCollider = line;
|
||||||
if (line)
|
if (line)
|
||||||
{
|
{
|
||||||
line->place_in_grid();
|
line->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(line);
|
EdgeList.push_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ TFlagSpinner::TFlagSpinner(TPinballTable* table, int groupIndex) : TCollisionCom
|
||||||
MinSpeed = *minSpeed;
|
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)
|
if (Timer)
|
||||||
{
|
{
|
||||||
|
@ -60,24 +60,17 @@ int TFlagSpinner::Message(int code, float value)
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
BmpIndex = 0;
|
BmpIndex = 0;
|
||||||
auto bmp = ListBitmap->at(0);
|
SpriteSet(BmpIndex);
|
||||||
auto zMap = ListZMap->at(0);
|
|
||||||
render::sprite_set(
|
|
||||||
RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - PinballTable->XOffset,
|
|
||||||
bmp->YPosition - PinballTable->YOffset);
|
|
||||||
}
|
}
|
||||||
return 0;
|
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)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance = ball->RayMaxDistance - coef;
|
ball->RayMaxDistance = ball->RayMaxDistance - distance;
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
|
|
||||||
SpinDirection = 2 * (PrevCollider != edge) - 1;
|
SpinDirection = 2 * (PrevCollider != edge) - 1;
|
||||||
|
@ -92,22 +85,11 @@ void TFlagSpinner::Collision(TBall* ball, vector_type* nextPosition, vector_type
|
||||||
NextFrame();
|
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()
|
void TFlagSpinner::NextFrame()
|
||||||
{
|
{
|
||||||
BmpIndex += SpinDirection;
|
BmpIndex += SpinDirection;
|
||||||
int bmpIndex = BmpIndex;
|
int bmpIndex = BmpIndex;
|
||||||
int bmpCount = ListBitmap->size();
|
int bmpCount = static_cast<int>(ListBitmap->size());
|
||||||
if (bmpIndex >= bmpCount)
|
if (bmpIndex >= bmpCount)
|
||||||
BmpIndex = 0;
|
BmpIndex = 0;
|
||||||
else if (bmpIndex < 0)
|
else if (bmpIndex < 0)
|
||||||
|
@ -115,22 +97,14 @@ void TFlagSpinner::NextFrame()
|
||||||
|
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
if (SoftHitSoundId)
|
if (SoftHitSoundId)
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, this, "TFlagSpinner");
|
||||||
if (!BmpIndex)
|
if (!BmpIndex)
|
||||||
control::handler(62, this);
|
control::handler(MessageCode::ControlSpinnerLoopReset, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bmp = ListBitmap->at(BmpIndex);
|
SpriteSet(BmpIndex);
|
||||||
auto zMap = ListZMap->at(BmpIndex);
|
|
||||||
render::sprite_set(
|
|
||||||
RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - PinballTable->XOffset,
|
|
||||||
bmp->YPosition - PinballTable->YOffset);
|
|
||||||
|
|
||||||
Speed *= SpeedDecrement;
|
Speed *= SpeedDecrement;
|
||||||
if (Speed >= MinSpeed)
|
if (Speed >= MinSpeed)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,11 +6,9 @@ class TFlagSpinner :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TFlagSpinner(TPinballTable* table, int groupIndex);
|
TFlagSpinner(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
void put_scoring(int index, int score) override;
|
|
||||||
int get_scoring(int index) override;
|
|
||||||
void NextFrame();
|
void NextFrame();
|
||||||
|
|
||||||
static void SpinTimer(int timerId, void* caller);
|
static void SpinTimer(int timerId, void* caller);
|
||||||
|
@ -23,6 +21,5 @@ public:
|
||||||
int BmpIndex{};
|
int BmpIndex{};
|
||||||
int Timer;
|
int Timer;
|
||||||
TEdgeSegment* PrevCollider;
|
TEdgeSegment* PrevCollider;
|
||||||
int Scores[2]{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
#include "TBall.h"
|
||||||
#include "TFlipperEdge.h"
|
#include "TFlipperEdge.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.h"
|
||||||
|
@ -18,22 +19,14 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||||
HardHitSoundId = visual.SoundIndex4;
|
HardHitSoundId = visual.SoundIndex4;
|
||||||
SoftHitSoundId = visual.SoundIndex3;
|
SoftHitSoundId = visual.SoundIndex3;
|
||||||
Elasticity = visual.Elasticity;
|
Elasticity = visual.Elasticity;
|
||||||
Timer = 0;
|
|
||||||
Smoothness = visual.Smoothness;
|
Smoothness = visual.Smoothness;
|
||||||
|
|
||||||
auto collMult = *loader::query_float_attribute(groupIndex, 0, 803);
|
auto collMult = *loader::query_float_attribute(groupIndex, 0, 803);
|
||||||
auto retractTime = *loader::query_float_attribute(groupIndex, 0, 805);
|
auto retractTime = *loader::query_float_attribute(groupIndex, 0, 805);
|
||||||
auto extendTime = *loader::query_float_attribute(groupIndex, 0, 804);
|
auto extendTime = *loader::query_float_attribute(groupIndex, 0, 804);
|
||||||
|
auto vecT2 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 802));
|
||||||
/*Full tilt hack: different flipper speed*/
|
auto vecT1 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 801));
|
||||||
if (pb::FullTiltMode)
|
auto origin = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 800));
|
||||||
{
|
|
||||||
retractTime = 0.08f;
|
|
||||||
extendTime = 0.04f;
|
|
||||||
}
|
|
||||||
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 flipperEdge = new TFlipperEdge(
|
auto flipperEdge = new TFlipperEdge(
|
||||||
this,
|
this,
|
||||||
&ActiveFlag,
|
&ActiveFlag,
|
||||||
|
@ -47,79 +40,67 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||||
collMult,
|
collMult,
|
||||||
Elasticity,
|
Elasticity,
|
||||||
Smoothness);
|
Smoothness);
|
||||||
|
flipperEdge->place_in_grid(&AABB);
|
||||||
|
|
||||||
FlipperEdge = flipperEdge;
|
FlipperEdge = flipperEdge;
|
||||||
if (flipperEdge)
|
|
||||||
{
|
|
||||||
ExtendAnimationFrameTime = flipperEdge->ExtendTime / static_cast<float>(ListBitmap->size() - 1);
|
|
||||||
RetractAnimationFrameTime = flipperEdge->RetractTime / static_cast<float>(ListBitmap->size() - 1);
|
|
||||||
}
|
|
||||||
BmpIndex = 0;
|
BmpIndex = 0;
|
||||||
InputTime = 0.0;
|
if (table)
|
||||||
|
table->FlipperList.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
TFlipper::~TFlipper()
|
TFlipper::~TFlipper()
|
||||||
{
|
{
|
||||||
delete FlipperEdge;
|
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;
|
case MessageCode::TFlipperExtend:
|
||||||
int command = code;
|
case MessageCode::TFlipperRetract:
|
||||||
if (code == 1)
|
case MessageCode::Resume:
|
||||||
|
case MessageCode::LooseFocus:
|
||||||
|
case MessageCode::SetTiltLock:
|
||||||
|
case MessageCode::GameOver:
|
||||||
|
if (code == MessageCode::TFlipperExtend)
|
||||||
{
|
{
|
||||||
control::handler(1, this);
|
control::handler(MessageCode::TFlipperExtend, this);
|
||||||
TimerTime = ExtendAnimationFrameTime;
|
loader::play_sound(HardHitSoundId, this, "TFlipper1");
|
||||||
loader::play_sound(HardHitSoundId);
|
|
||||||
}
|
}
|
||||||
else if (code == 2)
|
else if (code == MessageCode::TFlipperRetract)
|
||||||
{
|
{
|
||||||
TimerTime = RetractAnimationFrameTime;
|
loader::play_sound(SoftHitSoundId, this, "TFlipper2");
|
||||||
loader::play_sound(SoftHitSoundId);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Retract for all non-input messages
|
// Retract for all non-input messages
|
||||||
command = 2;
|
code = MessageCode::TFlipperRetract;
|
||||||
TimerTime = RetractAnimationFrameTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageField = FlipperEdge->SetMotion(code);
|
||||||
|
break;
|
||||||
|
case MessageCode::PlayerChanged:
|
||||||
|
case MessageCode::Reset:
|
||||||
if (MessageField)
|
if (MessageField)
|
||||||
{
|
{
|
||||||
// Message arrived before animation is finished
|
FlipperEdge->CurrentAngle = 0;
|
||||||
auto inputDt = value - FlipperEdge->InputTime;
|
FlipperEdge->set_control_points(0);
|
||||||
timerTime = inputDt - floor(inputDt / TimerTime) * TimerTime;
|
MessageField = 0;
|
||||||
if (timerTime < 0.0f)
|
FlipperEdge->SetMotion(MessageCode::Reset);
|
||||||
timerTime = 0.0;
|
UpdateSprite();
|
||||||
}
|
}
|
||||||
else
|
break;
|
||||||
{
|
default: break;
|
||||||
timerTime = TimerTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageField = command;
|
|
||||||
InputTime = value;
|
|
||||||
if (Timer)
|
|
||||||
timer::kill(Timer);
|
|
||||||
Timer = timer::set(timerTime, this, TimerExpired);
|
|
||||||
FlipperEdge->SetMotion(command, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,57 +109,95 @@ void TFlipper::port_draw()
|
||||||
FlipperEdge->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);
|
auto bmpCountSub1 = static_cast<int>(ListBitmap->size()) - 1;
|
||||||
int bmpCountSub1 = flip->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;
|
||||||
|
|
||||||
auto newBmpIndex = static_cast<int>(floor((pb::time_now - flip->InputTime) / flip->TimerTime));
|
BmpIndex = newBmpIndex;
|
||||||
if (newBmpIndex > bmpCountSub1)
|
SpriteSet(BmpIndex);
|
||||||
newBmpIndex = bmpCountSub1;
|
}
|
||||||
if (newBmpIndex < 0)
|
|
||||||
newBmpIndex = 0;
|
|
||||||
|
|
||||||
bool bmpIndexOutOfBounds = false;
|
int TFlipper::GetFlipperStepAngle(float dt, float* dst) const
|
||||||
if (flip->MessageField == 1)
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
flip->BmpIndex = newBmpIndex;
|
*dst = deltaAngle / step;
|
||||||
if (flip->BmpIndex >= bmpCountSub1)
|
return static_cast<int>(step);
|
||||||
{
|
|
||||||
flip->BmpIndex = bmpCountSub1;
|
|
||||||
bmpIndexOutOfBounds = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
|
||||||
{
|
|
||||||
flip->BmpIndex = bmpCountSub1 - newBmpIndex;
|
|
||||||
if (flip->BmpIndex <= 0)
|
|
||||||
{
|
|
||||||
flip->BmpIndex = 0;
|
|
||||||
bmpIndexOutOfBounds = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bmpIndexOutOfBounds)
|
if (collisionFlag)
|
||||||
{
|
{
|
||||||
flip->MessageField = 0;
|
auto angleAdvance = deltaAngle / (std::fabs(FlipperEdge->MoveSpeed) * 5.0f);
|
||||||
flip->Timer = 0;
|
FlipperEdge->CurrentAngle -= angleAdvance;
|
||||||
|
FlipperEdge->AngleRemainder += std::fabs(angleAdvance);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
flip->Timer = timer::set(flip->TimerTime, flip, TimerExpired);
|
FlipperEdge->CurrentAngle += deltaAngle;
|
||||||
|
FlipperEdge->AngleRemainder -= std::fabs(deltaAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bmp = flip->ListBitmap->at(flip->BmpIndex);
|
if (FlipperEdge->AngleRemainder <= 0.0001f)
|
||||||
auto zMap = flip->ListZMap->at(flip->BmpIndex);
|
{
|
||||||
render::sprite_set(
|
FlipperEdge->CurrentAngle = FlipperEdge->AngleDst;
|
||||||
flip->RenderSprite,
|
FlipperEdge->FlipperFlag = MessageCode::TFlipperNull;
|
||||||
bmp,
|
MessageField = 0;
|
||||||
zMap,
|
}
|
||||||
bmp->XPosition - flip->PinballTable->XOffset,
|
FlipperEdge->ControlPointDirtyFlag = true;
|
||||||
bmp->YPosition - flip->PinballTable->YOffset);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,14 @@ class TFlipper :
|
||||||
public:
|
public:
|
||||||
TFlipper(TPinballTable* table, int groupIndex);
|
TFlipper(TPinballTable* table, int groupIndex);
|
||||||
~TFlipper() override;
|
~TFlipper() override;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void port_draw() 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;
|
TEdgeSegment* edge) override;
|
||||||
|
void UpdateSprite();
|
||||||
static void TimerExpired(int timerId, void* caller);
|
int GetFlipperStepAngle(float dt, float* dst) const;
|
||||||
|
void FlipperCollision(float deltaAngle);
|
||||||
|
|
||||||
int BmpIndex;
|
int BmpIndex;
|
||||||
TFlipperEdge* FlipperEdge;
|
TFlipperEdge* FlipperEdge;
|
||||||
int Timer;
|
|
||||||
float ExtendAnimationFrameTime{};
|
|
||||||
float RetractAnimationFrameTime{};
|
|
||||||
float TimerTime{};
|
|
||||||
float InputTime;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,29 +2,26 @@
|
||||||
#include "TFlipperEdge.h"
|
#include "TFlipperEdge.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
#include "TLine.h"
|
#include "TLine.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.h"
|
||||||
#include "TTableLayer.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,
|
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup,
|
||||||
vector_type* origin, vector_type* vecT1, vector_type* vecT2, float extendTime, float retractTime,
|
TPinballTable* table,
|
||||||
float collMult, float elasticity, float smoothness): TEdgeSegment(collComp, activeFlag, collisionGroup)
|
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;
|
Elasticity = elasticity;
|
||||||
Smoothness = smoothness;
|
Smoothness = smoothness;
|
||||||
ExtendTime = extendTime;
|
|
||||||
RetractTime = retractTime;
|
|
||||||
CollisionMult = collMult;
|
CollisionMult = collMult;
|
||||||
|
|
||||||
T1Src = *vecT1;
|
T1Src = static_cast<vector2>(*vecT1);
|
||||||
T2Src = *vecT2;
|
T2Src = static_cast<vector2>(*vecT2);
|
||||||
RotOrigin.X = origin->X;
|
RotOrigin.X = origin->X;
|
||||||
RotOrigin.Y = origin->Y;
|
RotOrigin.Y = origin->Y;
|
||||||
|
|
||||||
|
@ -36,293 +33,146 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
|
||||||
CircleT1RadiusMSq = CircleT1Radius * 1.01f * (CircleT1Radius * 1.01f);
|
CircleT1RadiusMSq = CircleT1Radius * 1.01f * (CircleT1Radius * 1.01f);
|
||||||
CircleT1RadiusSq = CircleT1Radius * CircleT1Radius;
|
CircleT1RadiusSq = CircleT1Radius * CircleT1Radius;
|
||||||
|
|
||||||
vecDir1.X = vecT1->X - origin->X;
|
vecOriginT1.X = vecT1->X - origin->X;
|
||||||
vecDir1.Y = vecT1->Y - origin->Y;
|
vecOriginT1.Y = vecT1->Y - origin->Y;
|
||||||
vecDir1.Z = 0.0;
|
vecOriginT1.Z = 0.0;
|
||||||
maths::normalize_2d(&vecDir1);
|
maths::normalize_2d(vecOriginT1);
|
||||||
|
|
||||||
vecDir2.X = vecT2->X - origin->X;
|
vecOriginT2.X = vecT2->X - origin->X;
|
||||||
vecDir2.Y = vecT2->Y - origin->Y;
|
vecOriginT2.Y = vecT2->Y - origin->Y;
|
||||||
vecDir2.Z = 0.0;
|
vecOriginT2.Z = 0.0;
|
||||||
maths::normalize_2d(&vecDir2);
|
maths::normalize_2d(vecOriginT2);
|
||||||
|
|
||||||
AngleMax = acos(maths::DotProduct(&vecDir1, &vecDir2));
|
AngleMax = acos(maths::DotProduct(vecOriginT1, vecOriginT2));
|
||||||
maths::cross(&vecDir1, &vecDir2, &crossProd);
|
maths::cross(vecOriginT1, vecOriginT2, crossProd);
|
||||||
if (crossProd.Z < 0.0f)
|
if (crossProd.Z < 0.0f)
|
||||||
AngleMax = -AngleMax;
|
AngleMax = -AngleMax;
|
||||||
FlipperFlag = 0;
|
|
||||||
Angle1 = 0.0;
|
|
||||||
|
|
||||||
auto dirX1 = vecDir1.X;
|
FlipperFlag = MessageCode::TFlipperNull;
|
||||||
auto dirY1 = -vecDir1.Y;
|
AngleDst = 0.0;
|
||||||
A2Src.X = dirY1 * CirclebaseRadius + origin->X;
|
|
||||||
A2Src.Y = dirX1 * CirclebaseRadius + origin->Y;
|
|
||||||
A1Src.X = dirY1 * CircleT1Radius + vecT1->X;
|
|
||||||
A1Src.Y = dirX1 * CircleT1Radius + vecT1->Y;
|
|
||||||
|
|
||||||
dirX1 = -dirX1;
|
// 3DPB and FT have different formats for flipper speed:
|
||||||
dirY1 = -dirY1;
|
// 3DPB: Time it takes for flipper to go from source to destination, in sec.
|
||||||
B1Src.X = dirY1 * CirclebaseRadius + origin->X;
|
// FT: Flipper movement speed, in radians per sec.
|
||||||
B1Src.Y = dirX1 * CirclebaseRadius + origin->Y;
|
if (!pb::FullTiltMode)
|
||||||
B2Src.X = dirY1 * CircleT1Radius + vecT1->X;
|
{
|
||||||
B2Src.Y = dirX1 * CircleT1Radius + vecT1->Y;
|
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)
|
if (AngleMax < 0.0f)
|
||||||
{
|
{
|
||||||
maths::vswap(&A1Src, &B1Src);
|
std::swap(A1Src, B1Src);
|
||||||
maths::vswap(&A2Src, &B2Src);
|
std::swap(A2Src, B2Src);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dx = vecT1->X - RotOrigin.X;
|
auto dx = vecT1->X - RotOrigin.X;
|
||||||
auto dy = vecT1->Y - RotOrigin.Y;
|
auto dy = vecT1->Y - RotOrigin.Y;
|
||||||
auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z;
|
auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z;
|
||||||
|
DistanceDiv = distance1;
|
||||||
DistanceDivSq = distance1 * distance1;
|
DistanceDivSq = distance1 * distance1;
|
||||||
|
InvT1Radius = 1.0f / CircleT1Radius * 1.5f;
|
||||||
|
|
||||||
float minMoveTime = std::min(ExtendTime, RetractTime);
|
if (AngleMax <= 0.0f)
|
||||||
auto distance = maths::Distance(vecT1, vecT2);
|
{
|
||||||
CollisionTimeAdvance = minMoveTime / (distance / CircleT1Radius + distance / CircleT1Radius);
|
ExtendSpeed = -ExtendSpeed;
|
||||||
|
}
|
||||||
TFlipperEdge::place_in_grid();
|
else
|
||||||
EdgeCollisionFlag = 0;
|
{
|
||||||
InputTime = 0.0;
|
RetractSpeed = -RetractSpeed;
|
||||||
CollisionFlag1 = 0;
|
}
|
||||||
AngleStopTime = 0.0;
|
set_control_points(CurrentAngle);
|
||||||
AngleMult = 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFlipperEdge::port_draw()
|
void TFlipperEdge::port_draw()
|
||||||
{
|
{
|
||||||
set_control_points(InputTime);
|
set_control_points(CurrentAngle);
|
||||||
build_edges_in_motion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float TFlipperEdge::FindCollisionDistance(ray_type* ray)
|
float TFlipperEdge::FindCollisionDistance(const ray_type& ray)
|
||||||
{
|
{
|
||||||
auto ogRay = ray;
|
ray_type dstRay{};
|
||||||
ray_type dstRay{}, srcRay{};
|
if (ControlPointDirtyFlag)
|
||||||
|
set_control_points(CurrentAngle);
|
||||||
|
auto distance = maths::distance_to_flipper(this, ray, dstRay);
|
||||||
|
if (distance >= 1e9f)
|
||||||
|
return 1e9f;
|
||||||
|
|
||||||
if (ogRay->TimeNow > AngleStopTime)
|
|
||||||
{
|
|
||||||
FlipperFlag = 0;
|
|
||||||
}
|
|
||||||
if (EdgeCollisionFlag == 0)
|
|
||||||
{
|
|
||||||
if (FlipperFlag == 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.0f)
|
|
||||||
{
|
|
||||||
NextBallPosition = dstRay.Origin;
|
NextBallPosition = dstRay.Origin;
|
||||||
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
|
|
||||||
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NextBallPosition = dstRay.Origin;
|
|
||||||
}
|
|
||||||
CollisionDirection = dstRay.Direction;
|
CollisionDirection = dstRay.Direction;
|
||||||
return distance;
|
return distance;
|
||||||
}
|
|
||||||
|
|
||||||
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+09f)
|
|
||||||
{
|
|
||||||
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+09f)
|
|
||||||
{
|
|
||||||
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+09f)
|
|
||||||
{
|
|
||||||
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+09f)
|
|
||||||
{
|
|
||||||
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+09f)
|
|
||||||
{
|
|
||||||
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.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CollisionFlag1 = AngleMax > 0.0f;
|
|
||||||
linePtr = &lineA.PerpendicularL;
|
|
||||||
}
|
|
||||||
CollisionLinePerp = *linePtr;
|
|
||||||
CollisionDirection = dstRay.Direction;
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
timeNow = timeNow + CollisionTimeAdvance;
|
|
||||||
posX = posX + posXAdvance;
|
|
||||||
posY = posY + posYAdvance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EdgeCollisionFlag = 0;
|
|
||||||
}
|
|
||||||
return 1e+09;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFlipperEdge::EdgeCollision(TBall* ball, float coef)
|
void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
|
||||||
{
|
{
|
||||||
EdgeCollisionFlag = 1;
|
if (FlipperFlag == MessageCode::TFlipperNull)
|
||||||
if (!FlipperFlag || !CollisionFlag2 || CollisionFlag1)
|
|
||||||
{
|
{
|
||||||
float boost = 0.0;
|
maths::basic_collision(
|
||||||
if (CollisionFlag1)
|
ball,
|
||||||
{
|
&NextBallPosition,
|
||||||
float dx = NextBallPosition.X - RotOrigin.X;
|
&CollisionDirection,
|
||||||
float dy = NextBallPosition.Y - RotOrigin.Y;
|
Elasticity,
|
||||||
float distance = dy * dy + dx * dx;
|
Smoothness,
|
||||||
if (circlebase.RadiusSq * 1.01f < distance)
|
1e9f,
|
||||||
{
|
0);
|
||||||
float v11;
|
return;
|
||||||
float v20 = sqrt(distance / DistanceDivSq) * (fabs(AngleMax) / AngleMult);
|
|
||||||
float dot1 = maths::DotProduct(&CollisionLinePerp, &CollisionDirection);
|
|
||||||
if (dot1 >= 0.0f)
|
|
||||||
v11 = dot1 * v20;
|
|
||||||
else
|
|
||||||
v11 = 0.0;
|
|
||||||
boost = v11 * CollisionMult;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float threshold = boost <= 0.0f ? 1000000000.0f : -1.0f;
|
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 (AngleMax > 0)
|
||||||
|
frontCollision = true;
|
||||||
|
}
|
||||||
|
else if (AngleMax <= 0)
|
||||||
|
{
|
||||||
|
frontCollision = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FlipperFlag == MessageCode::TFlipperRetract)
|
||||||
|
{
|
||||||
|
frontCollision ^= true;
|
||||||
|
CollisionLinePerp = LineB.PerpendicularC;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CollisionLinePerp = LineA.PerpendicularC;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dx = NextBallPosition.X - RotOrigin.X;
|
||||||
|
auto dy = NextBallPosition.Y - RotOrigin.Y;
|
||||||
|
auto distanceSq = dy * dy + dx * dx;
|
||||||
|
if (frontCollision)
|
||||||
|
{
|
||||||
|
float boost = 0;
|
||||||
|
if (circlebase.RadiusSq * 1.01f < distanceSq)
|
||||||
|
{
|
||||||
|
auto v21 = std::fabs(MoveSpeed) * std::sqrt(distanceSq / DistanceDivSq);
|
||||||
|
auto dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
|
||||||
|
if (dot1 >= 0)
|
||||||
|
boost = CollisionMult * dot1 * v21;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto threshold = boost <= 0.0f ? 1e9f : -1.0f;
|
||||||
maths::basic_collision(
|
maths::basic_collision(
|
||||||
ball,
|
ball,
|
||||||
&NextBallPosition,
|
&NextBallPosition,
|
||||||
|
@ -331,159 +181,93 @@ void TFlipperEdge::EdgeCollision(TBall* ball, float coef)
|
||||||
Smoothness,
|
Smoothness,
|
||||||
threshold,
|
threshold,
|
||||||
boost);
|
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;
|
TTableLayer::edges_insert_square(yMin, xMin, yMax, xMax, this, nullptr);
|
||||||
float dx = NextBallPosition.X - RotOrigin.X;
|
|
||||||
float dy = NextBallPosition.Y - RotOrigin.Y;
|
auto offset = 1.0f / InvT1Radius + pb::BallHalfRadius;
|
||||||
float distance = dy * dy + dx * dx;
|
XMin = xMin - offset;
|
||||||
if (circlebase.RadiusSq * 1.01f < distance)
|
YMin = yMin - offset;
|
||||||
elasticity = (1.0f - sqrt(distance / DistanceDivSq)) * Elasticity;
|
XMax = xMax + offset;
|
||||||
else
|
YMax = yMax + offset;
|
||||||
elasticity = Elasticity;
|
|
||||||
maths::basic_collision(ball, &NextBallPosition, &CollisionDirection, elasticity, Smoothness, 1000000000.0, 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFlipperEdge::place_in_grid()
|
void TFlipperEdge::set_control_points(float angle)
|
||||||
{
|
{
|
||||||
float x0 = RotOrigin.X - CirclebaseRadius;
|
float sin, cos;
|
||||||
float y0 = RotOrigin.Y - CirclebaseRadius;
|
maths::SinCos(angle, sin, cos);
|
||||||
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);
|
|
||||||
A1 = A1Src;
|
A1 = A1Src;
|
||||||
A2 = A2Src;
|
A2 = A2Src;
|
||||||
B1 = B1Src;
|
B1 = B1Src;
|
||||||
B2 = B2Src;
|
B2 = B2Src;
|
||||||
T1 = T1Src;
|
T1 = T1Src;
|
||||||
maths::RotatePt(&A1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
maths::RotatePt(A1, sin, cos, RotOrigin);
|
||||||
maths::RotatePt(&A2, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
maths::RotatePt(A2, sin, cos, RotOrigin);
|
||||||
maths::RotatePt(&T1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
maths::RotatePt(T1, sin, cos, RotOrigin);
|
||||||
maths::RotatePt(&B1, flipper_sin_angle, flipper_cos_angle, &RotOrigin);
|
maths::RotatePt(B1, sin, cos, RotOrigin);
|
||||||
maths::RotatePt(&B2, flipper_sin_angle, flipper_cos_angle, &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);
|
if (FlipperFlag == MessageCode::TFlipperNull)
|
||||||
maths::line_init(&lineB, B1.X, B1.Y, B2.X, B2.Y);
|
return 0.0f;
|
||||||
circlebase.RadiusSq = CirclebaseRadiusSq;
|
|
||||||
circlebase.Center.X = RotOrigin.X;
|
const auto deltaAngle = MoveSpeed * timeDelta;
|
||||||
circlebase.Center.Y = RotOrigin.Y;
|
if (std::fabs(deltaAngle) > AngleRemainder)
|
||||||
circleT1.RadiusSq = CircleT1RadiusSq;
|
return AngleDst - CurrentAngle;
|
||||||
circleT1.Center.X = T1.X;
|
return deltaAngle;
|
||||||
circleT1.Center.Y = T1.Y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float TFlipperEdge::flipper_angle(float timeNow)
|
int TFlipperEdge::SetMotion(MessageCode code)
|
||||||
{
|
|
||||||
if (!FlipperFlag)
|
|
||||||
return Angle1;
|
|
||||||
float angle = (Angle1 - Angle2) / AngleMax * AngleMult;
|
|
||||||
if (angle < 0.0f)
|
|
||||||
angle = -angle;
|
|
||||||
|
|
||||||
if (angle >= 0.0000001f)
|
|
||||||
angle = (timeNow - InputTime) / angle;
|
|
||||||
else
|
|
||||||
angle = 1.0;
|
|
||||||
|
|
||||||
angle = std::min(1.0f, std::max(angle, 0.0f));
|
|
||||||
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.0f &&
|
|
||||||
(B1.X - A2.X) * (y - A2.Y) - (B1.Y - A2.Y) * (x - A2.X) >= 0.0f &&
|
|
||||||
(B2.X - B1.X) * (y - B1.Y) - (B2.Y - B1.Y) * (x - B1.X) >= 0.0f &&
|
|
||||||
(A1.X - B2.X) * (y - B2.Y) - (A1.Y - B2.Y) * (x - B2.X) >= 0.0f) ||
|
|
||||||
dy * dy + dx * dx <= CirclebaseRadiusSq ||
|
|
||||||
(T1.Y - y) * (T1.Y - y) + (T1.X - x) * (T1.X - x) < CircleT1RadiusSq)
|
|
||||||
{
|
|
||||||
float flipperLR = AngleMax < 0.0f ? -1.0f : 1.0f;
|
|
||||||
if (FlipperFlag == 1)
|
|
||||||
testPoint = AngleMax < 0.0f ? B1 : B2;
|
|
||||||
else if (FlipperFlag == 2)
|
|
||||||
testPoint = AngleMax < 0.0f ? A2 : A1;
|
|
||||||
else
|
|
||||||
testPoint = T1;
|
|
||||||
|
|
||||||
if (((y - testPoint.Y) * (RotOrigin.X - testPoint.X) -
|
|
||||||
(x - testPoint.X) * (RotOrigin.Y - testPoint.Y)) * flipperLR < 0.0f)
|
|
||||||
return 4;
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TFlipperEdge::SetMotion(int code, float value)
|
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1:
|
case MessageCode::TFlipperExtend:
|
||||||
Angle2 = flipper_angle(value);
|
AngleRemainder = std::fabs(AngleMax - CurrentAngle);
|
||||||
Angle1 = AngleMax;
|
AngleDst = AngleMax;
|
||||||
AngleMult = ExtendTime;
|
MoveSpeed = ExtendSpeed;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case MessageCode::TFlipperRetract:
|
||||||
Angle2 = flipper_angle(value);
|
AngleRemainder = std::fabs(CurrentAngle);
|
||||||
Angle1 = 0.0;
|
AngleDst = 0.0f;
|
||||||
AngleMult = RetractTime;
|
MoveSpeed = RetractSpeed;
|
||||||
|
break;
|
||||||
|
case MessageCode::Reset:
|
||||||
|
AngleRemainder = 0.0f;
|
||||||
|
AngleDst = 0.0f;
|
||||||
break;
|
break;
|
||||||
case 1024:
|
|
||||||
FlipperFlag = 0;
|
|
||||||
Angle1 = 0.0;
|
|
||||||
return;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FlipperFlag)
|
if (AngleRemainder == 0.0f)
|
||||||
InputTime = value;
|
code = MessageCode::TFlipperNull;
|
||||||
|
|
||||||
FlipperFlag = code;
|
FlipperFlag = code;
|
||||||
AngleStopTime = AngleMult + InputTime;
|
return static_cast<int>(code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "maths.h"
|
#include "maths.h"
|
||||||
#include "TEdgeSegment.h"
|
#include "TEdgeSegment.h"
|
||||||
|
#include "TPinballComponent.h"
|
||||||
|
|
||||||
class TPinballTable;
|
class TPinballTable;
|
||||||
|
|
||||||
|
@ -8,22 +9,20 @@ class TFlipperEdge : public TEdgeSegment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
|
TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
|
||||||
vector_type* origin, vector_type* vecT1, vector_type* vecT2, float extendTime, float retractTime, float collMult,
|
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult,
|
||||||
float elasticity, float smoothness);
|
float elasticity, float smoothness);
|
||||||
void port_draw() override;
|
void port_draw() override;
|
||||||
float FindCollisionDistance(ray_type* ray) override;
|
float FindCollisionDistance(const ray_type& ray) override;
|
||||||
void EdgeCollision(TBall* ball, float coef) override;
|
void EdgeCollision(TBall* ball, float distance) override;
|
||||||
void place_in_grid() override;
|
void place_in_grid(RectF* aabb) override;
|
||||||
void set_control_points(float timeNow);
|
void set_control_points(float angle);
|
||||||
void build_edges_in_motion();
|
float flipper_angle_delta(float timeDelta);
|
||||||
float flipper_angle(float timeNow);
|
int SetMotion(MessageCode code);
|
||||||
int is_ball_inside(float x, float y);
|
|
||||||
void SetMotion(int code, float value);
|
|
||||||
|
|
||||||
int FlipperFlag;
|
MessageCode FlipperFlag{};
|
||||||
float Elasticity;
|
float Elasticity;
|
||||||
float Smoothness;
|
float Smoothness;
|
||||||
vector_type RotOrigin{};
|
vector2 RotOrigin{};
|
||||||
float CirclebaseRadius;
|
float CirclebaseRadius;
|
||||||
float CircleT1Radius;
|
float CircleT1Radius;
|
||||||
float CirclebaseRadiusSq;
|
float CirclebaseRadiusSq;
|
||||||
|
@ -31,31 +30,27 @@ public:
|
||||||
float CirclebaseRadiusMSq;
|
float CirclebaseRadiusMSq;
|
||||||
float CircleT1RadiusMSq;
|
float CircleT1RadiusMSq;
|
||||||
float AngleMax;
|
float AngleMax;
|
||||||
float Angle2{};
|
float AngleRemainder{};
|
||||||
float Angle1;
|
float AngleDst;
|
||||||
int CollisionFlag1;
|
float CurrentAngle{};
|
||||||
int CollisionFlag2{};
|
vector2 CollisionLinePerp{};
|
||||||
vector_type CollisionLinePerp{};
|
vector2 A1Src{};
|
||||||
vector_type A1Src{};
|
vector2 A2Src{};
|
||||||
vector_type A2Src{};
|
vector2 B1Src{};
|
||||||
vector_type B1Src{};
|
vector2 B2Src{};
|
||||||
vector_type B2Src{};
|
|
||||||
float CollisionMult;
|
float CollisionMult;
|
||||||
vector_type T1Src{};
|
vector2 T1Src{};
|
||||||
vector_type T2Src{};
|
vector2 T2Src{};
|
||||||
float DistanceDivSq;
|
float DistanceDiv, DistanceDivSq;
|
||||||
float CollisionTimeAdvance;
|
vector2 CollisionDirection{};
|
||||||
vector_type CollisionDirection{};
|
float ExtendSpeed;
|
||||||
int EdgeCollisionFlag;
|
float RetractSpeed;
|
||||||
float InputTime;
|
float MoveSpeed;
|
||||||
float AngleStopTime;
|
vector2 NextBallPosition{};
|
||||||
float AngleMult;
|
vector2 A1, A2, B1, B2, T1;
|
||||||
float ExtendTime;
|
line_type LineA, LineB;
|
||||||
float RetractTime;
|
circle_type circlebase, circleT1;
|
||||||
vector_type NextBallPosition{};
|
float InvT1Radius;
|
||||||
|
float YMin, YMax, XMin, XMax;
|
||||||
static float flipper_sin_angle, flipper_cos_angle;
|
bool ControlPointDirtyFlag{};
|
||||||
static vector_type A1, A2, B1, B2, T1;
|
|
||||||
static line_type lineA, lineB;
|
|
||||||
static circle_type circlebase, circleT1;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,28 +14,29 @@ TGate::TGate(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
SoundIndex4 = visual.SoundIndex4;
|
SoundIndex4 = visual.SoundIndex4;
|
||||||
SoundIndex3 = visual.SoundIndex3;
|
SoundIndex3 = visual.SoundIndex3;
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
|
SpriteSet(0);
|
||||||
control::handler(1024, this);
|
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)
|
|
||||||
{
|
{
|
||||||
|
case MessageCode::TGateDisable:
|
||||||
ActiveFlag = 0;
|
ActiveFlag = 0;
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
SpriteSet(-1);
|
||||||
loader::play_sound(SoundIndex3);
|
loader::play_sound(SoundIndex3, this, "TGate1");
|
||||||
}
|
break;
|
||||||
else if (code == 54 || code == 1024)
|
case MessageCode::Reset:
|
||||||
{
|
case MessageCode::TGateEnable:
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
|
SpriteSet(0);
|
||||||
if (code == 54)
|
if (code == MessageCode::TGateEnable)
|
||||||
loader::play_sound(SoundIndex4);
|
loader::play_sound(SoundIndex4, this, "TGate2");
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
control::handler(code, this);
|
control::handler(code, this);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ class TGate :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TGate(TPinballTable* table, int groupIndex);
|
TGate(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
|
|
||||||
int SoundIndex4;
|
int SoundIndex4;
|
||||||
int SoundIndex3;
|
int SoundIndex3;
|
||||||
|
|
|
@ -18,7 +18,7 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
Unknown4 = 0.050000001f;
|
Unknown4 = 0.050000001f;
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
BallCapturedFlag = 0;
|
BallCapturedFlag = false;
|
||||||
Unknown3 = loader::query_float_attribute(groupIndex, 0, 407, 0.25f);
|
Unknown3 = loader::query_float_attribute(groupIndex, 0, 407, 0.25f);
|
||||||
GravityMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
GravityMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
||||||
GravityPull = *loader::query_float_attribute(groupIndex, 0, 305);
|
GravityPull = *loader::query_float_attribute(groupIndex, 0, 305);
|
||||||
|
@ -31,56 +31,53 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
Circle.RadiusSq = 0.001f;
|
Circle.RadiusSq = 0.001f;
|
||||||
|
|
||||||
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
|
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
|
||||||
reinterpret_cast<vector_type*>(visual.FloatArr),
|
reinterpret_cast<vector3*>(visual.FloatArr),
|
||||||
Circle.RadiusSq);
|
Circle.RadiusSq);
|
||||||
if (tCircle)
|
tCircle->place_in_grid(&AABB);
|
||||||
{
|
|
||||||
tCircle->place_in_grid();
|
|
||||||
EdgeList.push_back(tCircle);
|
EdgeList.push_back(tCircle);
|
||||||
}
|
|
||||||
|
|
||||||
ZSetValue = loader::query_float_attribute(groupIndex, 0, 408)[2];
|
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)));
|
||||||
|
|
||||||
/*Full tilt hack - FieldFlag should be on*/
|
// CollisionMask difference: 3DPB - value given as is, FT: mask = 1 << value
|
||||||
if (pb::FullTiltMode)
|
if (pb::FullTiltMode)
|
||||||
FieldFlag = 1;
|
CollisionMask = 1 << CollisionMask;
|
||||||
|
|
||||||
Circle.RadiusSq = visual.FloatArr[2] * visual.FloatArr[2];
|
Circle.RadiusSq = visual.FloatArr[2] * visual.FloatArr[2];
|
||||||
circle.RadiusSq = Circle.RadiusSq;
|
circle.RadiusSq = Circle.RadiusSq;
|
||||||
circle.Center.X = Circle.Center.X;
|
circle.Center.X = Circle.Center.X;
|
||||||
circle.Center.Y = Circle.Center.Y;
|
circle.Center.Y = Circle.Center.Y;
|
||||||
circle.Center.Z = Circle.Center.Z;
|
|
||||||
|
|
||||||
Field.Flag2Ptr = &ActiveFlag;
|
Field.ActiveFlag = &ActiveFlag;
|
||||||
Field.CollisionComp = this;
|
Field.CollisionComp = this;
|
||||||
Field.Mask = visual.CollisionGroup;
|
Field.CollisionGroup = visual.CollisionGroup;
|
||||||
TTableLayer::edges_insert_circle(&circle, nullptr, &Field);
|
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)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
BallCapturedSecondStage = 1;
|
BallCapturedSecondStage = true;
|
||||||
}
|
}
|
||||||
return 0;
|
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)
|
if (!BallCapturedFlag)
|
||||||
{
|
{
|
||||||
BallCapturedSecondStage = 0;
|
BallCapturedSecondStage = false;
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1e9f;
|
||||||
BallCapturedFlag = 1;
|
BallCapturedFlag = true;
|
||||||
ball->CollisionComp = this;
|
ball->CollisionComp = this;
|
||||||
ball->Position.X = Circle.Center.X;
|
ball->Position.X = Circle.Center.X;
|
||||||
ball->Position.Y = Circle.Center.Y;
|
ball->Position.Y = Circle.Center.Y;
|
||||||
ball->Acceleration.Z = 0.0;
|
ball->Direction.Z = 0.0;
|
||||||
|
ball->CollisionDisabledFlag = true;
|
||||||
|
|
||||||
// Ramp hole has no delay in FT.
|
// Ramp hole has no delay in FT.
|
||||||
auto captureTime = pb::FullTiltMode ? 0 : 0.5f;
|
auto captureTime = pb::FullTiltMode ? 0 : 0.5f;
|
||||||
|
@ -88,36 +85,38 @@ void THole::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||||
|
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
loader::play_sound(HardHitSoundId);
|
loader::play_sound(HardHitSoundId, ball, "THole1");
|
||||||
control::handler(57, this);
|
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;
|
int result;
|
||||||
vector_type direction{};
|
vector2 direction{};
|
||||||
|
|
||||||
if (BallCapturedFlag)
|
if (BallCapturedFlag)
|
||||||
{
|
{
|
||||||
if (BallCapturedSecondStage)
|
if (BallCapturedSecondStage)
|
||||||
{
|
{
|
||||||
ball->Acceleration.Z -= PinballTable->GravityDirVectMult * ball->TimeDelta * GravityMult;
|
ball->Direction.Z -= PinballTable->GravityDirVectMult * ball->TimeDelta * GravityMult;
|
||||||
ball->Position.Z += ball->Acceleration.Z;
|
ball->Position.Z += ball->Direction.Z;
|
||||||
if (ball->Position.Z <= ZSetValue)
|
if (ball->Position.Z <= ZSetValue)
|
||||||
{
|
{
|
||||||
BallCapturedFlag = 0;
|
BallCapturedFlag = false;
|
||||||
BallCapturedSecondStage = 0;
|
BallCapturedSecondStage = false;
|
||||||
ball->Position.Z = ZSetValue;
|
ball->Position.Z = ZSetValue;
|
||||||
ball->Acceleration.Z = 0.0;
|
ball->CollisionMask = CollisionMask;
|
||||||
ball->FieldFlag = FieldFlag;
|
|
||||||
ball->Acceleration.Y = 0.0;
|
|
||||||
ball->CollisionComp = nullptr;
|
ball->CollisionComp = nullptr;
|
||||||
ball->Acceleration.X = 0.0;
|
ball->Direction = {};
|
||||||
ball->Speed = 0.0;
|
ball->Speed = 0.0f;
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, ball, "THole2");
|
||||||
control::handler(58, this);
|
control::handler(MessageCode::ControlBallReleased, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = 0;
|
result = 0;
|
||||||
|
@ -128,9 +127,9 @@ int THole::FieldEffect(TBall* ball, vector_type* vecDst)
|
||||||
direction.Y = Circle.Center.Y - ball->Position.Y;
|
direction.Y = Circle.Center.Y - ball->Position.Y;
|
||||||
if (direction.X * direction.X + direction.Y * direction.Y <= Circle.RadiusSq)
|
if (direction.X * direction.X + direction.Y * direction.Y <= Circle.RadiusSq)
|
||||||
{
|
{
|
||||||
maths::normalize_2d(&direction);
|
maths::normalize_2d(direction);
|
||||||
vecDst->X = direction.X * GravityPull - ball->Acceleration.X * ball->Speed;
|
vecDst->X = direction.X * GravityPull - ball->Direction.X * ball->Speed;
|
||||||
vecDst->Y = direction.Y * GravityPull - ball->Acceleration.Y * ball->Speed;
|
vecDst->Y = direction.Y * GravityPull - ball->Direction.Y * ball->Speed;
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -145,5 +144,5 @@ void THole::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto hole = static_cast<THole*>(caller);
|
auto hole = static_cast<THole*>(caller);
|
||||||
hole->Timer = 0;
|
hole->Timer = 0;
|
||||||
hole->BallCapturedSecondStage = 1;
|
hole->BallCapturedSecondStage = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,21 +8,21 @@ class THole :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
THole(TPinballTable* table, int groupIndex);
|
THole(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
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 TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int BallCapturedFlag;
|
bool BallCapturedFlag{};
|
||||||
int BallCapturedSecondStage{};
|
bool BallCapturedSecondStage{};
|
||||||
int Timer;
|
int Timer;
|
||||||
float Unknown3;
|
float Unknown3;
|
||||||
float Unknown4;
|
float Unknown4;
|
||||||
float GravityMult;
|
float GravityMult;
|
||||||
float ZSetValue;
|
float ZSetValue;
|
||||||
int FieldFlag;
|
int CollisionMask;
|
||||||
float GravityPull;
|
float GravityPull;
|
||||||
circle_type Circle{};
|
circle_type Circle{};
|
||||||
field_effect_type Field{};
|
field_effect_type Field{};
|
||||||
|
|
|
@ -19,13 +19,12 @@ TKickback::TKickback(TPinballTable* table, int groupIndex): TCollisionComponent(
|
||||||
Threshold = 1000000000.0f;
|
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);
|
timer::kill(Timer);
|
||||||
if (ListBitmap)
|
SpriteSet(-1);
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
KickActiveFlag = 0;
|
KickActiveFlag = 0;
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1000000000.0;
|
||||||
|
@ -33,7 +32,7 @@ int TKickback::Message(int code, float value)
|
||||||
return 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)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
if (PinballTable->TiltLockFlag)
|
if (PinballTable->TiltLockFlag)
|
||||||
|
@ -62,33 +61,13 @@ void TKickback::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
kick->Threshold = 0.0;
|
kick->Threshold = 0.0;
|
||||||
kick->Timer = timer::set(kick->TimerTime2, kick, TimerExpired);
|
kick->Timer = timer::set(kick->TimerTime2, kick, TimerExpired);
|
||||||
loader::play_sound(kick->HardHitSoundId);
|
loader::play_sound(kick->HardHitSoundId, kick, "TKickback");
|
||||||
if (kick->ListBitmap)
|
kick->SpriteSet(1);
|
||||||
{
|
|
||||||
auto bmp = kick->ListBitmap->at(1);
|
|
||||||
auto zMap = kick->ListZMap->at(1);
|
|
||||||
render::sprite_set(
|
|
||||||
kick->RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - kick->PinballTable->XOffset,
|
|
||||||
bmp->YPosition - kick->PinballTable->YOffset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (kick->ListBitmap)
|
kick->SpriteSet(0);
|
||||||
{
|
|
||||||
auto bmp = kick->ListBitmap->at(0);
|
|
||||||
auto zMap = kick->ListZMap->at(0);
|
|
||||||
render::sprite_set(
|
|
||||||
kick->RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - kick->PinballTable->XOffset,
|
|
||||||
bmp->YPosition - kick->PinballTable->YOffset);
|
|
||||||
}
|
|
||||||
kick->Timer = 0;
|
kick->Timer = 0;
|
||||||
control::handler(60, kick);
|
control::handler(MessageCode::ControlTimerExpired, kick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ class TKickback :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TKickback(TPinballTable* table, int groupIndex);
|
TKickback(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
|
@ -24,7 +24,7 @@ TKickout::TKickout(TPinballTable* table, int groupIndex, bool someFlag): TCollis
|
||||||
TimerTime2 = 0.05f;
|
TimerTime2 = 0.05f;
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
KickFlag1 = 0;
|
BallCaputeredFlag = 0;
|
||||||
FieldMult = *loader::query_float_attribute(groupIndex, 0, 305);
|
FieldMult = *loader::query_float_attribute(groupIndex, 0, 305);
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
SoftHitSoundId = visual.SoftHitSoundId;
|
SoftHitSoundId = visual.SoftHitSoundId;
|
||||||
|
@ -36,10 +36,10 @@ TKickout::TKickout(TPinballTable* table, int groupIndex, bool someFlag): TCollis
|
||||||
if (Circle.RadiusSq == 0.0f)
|
if (Circle.RadiusSq == 0.0f)
|
||||||
Circle.RadiusSq = 0.001f;
|
Circle.RadiusSq = 0.001f;
|
||||||
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
|
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)
|
if (tCircle)
|
||||||
{
|
{
|
||||||
tCircle->place_in_grid();
|
tCircle->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(tCircle);
|
EdgeList.push_back(tCircle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,38 +47,37 @@ TKickout::TKickout(TPinballTable* table, int groupIndex, bool someFlag): TCollis
|
||||||
auto zAttr = loader::query_float_attribute(groupIndex, 0, 408);
|
auto zAttr = loader::query_float_attribute(groupIndex, 0, 408);
|
||||||
CollisionBallSetZ = pb::FullTiltMode && !pb::FullTiltDemoMode ? zAttr[3] : zAttr[2];
|
CollisionBallSetZ = pb::FullTiltMode && !pb::FullTiltDemoMode ? zAttr[3] : zAttr[2];
|
||||||
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
||||||
BallAcceleration = visual.Kicker.ThrowBallAcceleration;
|
BallThrowDirection = visual.Kicker.ThrowBallDirection;
|
||||||
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
||||||
ThrowSpeedMult1 = visual.Kicker.Boost;
|
ThrowSpeedMult1 = visual.Kicker.Boost;
|
||||||
|
|
||||||
circle.RadiusSq = Circle.RadiusSq;
|
circle.RadiusSq = Circle.RadiusSq;
|
||||||
circle.Center.X = Circle.Center.X;
|
circle.Center.X = Circle.Center.X;
|
||||||
circle.Center.Y = Circle.Center.Y;
|
circle.Center.Y = Circle.Center.Y;
|
||||||
circle.Center.Z = Circle.Center.Z;
|
Field.ActiveFlag = &ActiveFlag;
|
||||||
Field.Flag2Ptr = &ActiveFlag;
|
|
||||||
Field.CollisionComp = this;
|
Field.CollisionComp = this;
|
||||||
Field.Mask = visual.CollisionGroup;
|
Field.CollisionGroup = visual.CollisionGroup;
|
||||||
TTableLayer::edges_insert_circle(&circle, nullptr, &Field);
|
TTableLayer::edges_insert_circle(&circle, nullptr, &Field);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TKickout::Message(int code, float value)
|
int TKickout::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 55:
|
case MessageCode::TKickoutRestartTimer:
|
||||||
if (KickFlag1)
|
if (BallCaputeredFlag)
|
||||||
{
|
{
|
||||||
if (value < 0.0f)
|
if (value < 0.0f)
|
||||||
value = TimerTime1;
|
value = TimerTime1;
|
||||||
Timer = timer::set(value, this, TimerExpired);
|
Timer = timer::set(value, this, TimerExpired);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1011:
|
case MessageCode::SetTiltLock:
|
||||||
if (NotSomeFlag)
|
if (NotSomeFlag)
|
||||||
ActiveFlag = 0;
|
ActiveFlag = 0;
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
if (KickFlag1)
|
if (BallCaputeredFlag)
|
||||||
{
|
{
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
|
@ -94,72 +93,69 @@ int TKickout::Message(int code, float value)
|
||||||
return 0;
|
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)
|
if (!BallCaputeredFlag)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Ball = ball;
|
Ball = ball;
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1000000000.0;
|
||||||
KickFlag1 = 1;
|
BallCaputeredFlag = 1;
|
||||||
ball->CollisionComp = this;
|
ball->CollisionComp = this;
|
||||||
ball->Position.X = Circle.Center.X;
|
ball->Position.X = Circle.Center.X;
|
||||||
ball->Position.Y = Circle.Center.Y;
|
ball->Position.Y = Circle.Center.Y;
|
||||||
OriginalBallZ = ball->Position.Z;
|
OriginalBallZ = ball->Position.Z;
|
||||||
ball->Position.Z = CollisionBallSetZ;
|
ball->Position.Z = CollisionBallSetZ;
|
||||||
|
ball->CollisionDisabledFlag = true;
|
||||||
if (PinballTable->TiltLockFlag)
|
if (PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
Message(55, 0.1f);
|
Message(MessageCode::TKickoutRestartTimer, 0.1f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, ball, "TKickout1");
|
||||||
control::handler(63, this);
|
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;
|
return 0;
|
||||||
direction.X = Circle.Center.X - ball->Position.X;
|
direction.X = Circle.Center.X - ball->Position.X;
|
||||||
direction.Y = Circle.Center.Y - ball->Position.Y;
|
direction.Y = Circle.Center.Y - ball->Position.Y;
|
||||||
if (direction.Y * direction.Y + direction.X * direction.X > Circle.RadiusSq)
|
if (direction.Y * direction.Y + direction.X * direction.X > Circle.RadiusSq)
|
||||||
return 0;
|
return 0;
|
||||||
maths::normalize_2d(&direction);
|
maths::normalize_2d(direction);
|
||||||
dstVec->X = direction.X * FieldMult - ball->Acceleration.X * ball->Speed;
|
dstVec->X = direction.X * FieldMult - ball->Direction.X * ball->Speed;
|
||||||
dstVec->Y = direction.Y * FieldMult - ball->Acceleration.Y * ball->Speed;
|
dstVec->Y = direction.Y * FieldMult - ball->Direction.Y * ball->Speed;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TKickout::TimerExpired(int timerId, void* caller)
|
void TKickout::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto kick = static_cast<TKickout*>(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);
|
kick->Timer = timer::set(kick->TimerTime2, kick, ResetTimerExpired);
|
||||||
if (kick->Ball)
|
if (kick->Ball)
|
||||||
{
|
{
|
||||||
|
loader::play_sound(kick->HardHitSoundId, kick->Ball, "TKickout2");
|
||||||
kick->Ball->Position.Z = kick->OriginalBallZ;
|
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->ThrowSpeedMult2);
|
||||||
kick->ActiveFlag = 0;
|
kick->ActiveFlag = 0;
|
||||||
kick->Ball = nullptr;
|
kick->Ball = nullptr;
|
||||||
loader::play_sound(kick->HardHitSoundId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,15 @@ class TKickout :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TKickout(TPinballTable* table, int groupIndex, bool someFlag);
|
TKickout(TPinballTable* table, int groupIndex, bool someFlag);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void put_scoring(int index, int score) override;
|
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge) override;
|
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 TimerExpired(int timerId, void* caller);
|
||||||
static void ResetTimerExpired(int timerId, void* caller);
|
static void ResetTimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int KickFlag1;
|
int BallCaputeredFlag;
|
||||||
int NotSomeFlag;
|
int NotSomeFlag;
|
||||||
int Timer;
|
int Timer;
|
||||||
float TimerTime1;
|
float TimerTime1;
|
||||||
|
@ -28,10 +26,9 @@ public:
|
||||||
float FieldMult;
|
float FieldMult;
|
||||||
circle_type Circle{};
|
circle_type Circle{};
|
||||||
float OriginalBallZ{};
|
float OriginalBallZ{};
|
||||||
vector_type BallAcceleration{};
|
vector3 BallThrowDirection{};
|
||||||
float ThrowAngleMult;
|
float ThrowAngleMult;
|
||||||
float ThrowSpeedMult1;
|
float ThrowSpeedMult1;
|
||||||
float ThrowSpeedMult2;
|
float ThrowSpeedMult2;
|
||||||
field_effect_type Field{};
|
field_effect_type Field{};
|
||||||
int Scores[5]{};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,224 +10,238 @@
|
||||||
|
|
||||||
TLight::TLight(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, true)
|
TLight::TLight(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, true)
|
||||||
{
|
{
|
||||||
Timer1 = 0;
|
TimeoutTimer = 0;
|
||||||
FlasherActive = 0;
|
FlasherOnFlag = false;
|
||||||
Timer2 = 0;
|
UndoOverrideTimer = 0;
|
||||||
Flasher.Timer = 0;
|
FlashTimer = 0;
|
||||||
Reset();
|
Reset();
|
||||||
float* floatArr1 = loader::query_float_attribute(groupIndex, 0, 900);
|
float* floatArr1 = loader::query_float_attribute(groupIndex, 0, 900);
|
||||||
Flasher.TimerDelay[0] = *floatArr1;
|
FlashDelay[0] = *floatArr1;
|
||||||
FlasherDelay[0] = *floatArr1;
|
SourceDelay[0] = *floatArr1;
|
||||||
float* floatArr2 = loader::query_float_attribute(groupIndex, 0, 901);
|
float* floatArr2 = loader::query_float_attribute(groupIndex, 0, 901);
|
||||||
Flasher.TimerDelay[1] = *floatArr2;
|
FlashDelay[1] = *floatArr2;
|
||||||
FlasherDelay[1] = *floatArr2;
|
SourceDelay[1] = *floatArr2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TLight::Message(int code, float value)
|
int TLight::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
int bmpIndex;
|
int bmpIndex;
|
||||||
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
Reset();
|
Reset();
|
||||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||||
{
|
{
|
||||||
auto playerPtr = &PlayerData[index];
|
auto playerPtr = &PlayerData[index];
|
||||||
playerPtr->FlasherActive = FlasherActive;
|
playerPtr->FlasherOnFlag = FlasherOnFlag;
|
||||||
playerPtr->BmpIndex2 = BmpIndex2;
|
playerPtr->LightOnBmpIndex = LightOnBmpIndex;
|
||||||
playerPtr->BmpIndex1 = BmpIndex1;
|
playerPtr->LightOnFlag = LightOnFlag;
|
||||||
playerPtr->MessageField = MessageField;
|
playerPtr->MessageField = MessageField;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
{
|
{
|
||||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||||
playerPtr->FlasherActive = FlasherActive;
|
playerPtr->FlasherOnFlag = FlasherOnFlag;
|
||||||
playerPtr->BmpIndex2 = BmpIndex2;
|
playerPtr->LightOnBmpIndex = LightOnBmpIndex;
|
||||||
playerPtr->BmpIndex1 = BmpIndex1;
|
playerPtr->LightOnFlag = LightOnFlag;
|
||||||
playerPtr->MessageField = MessageField;
|
playerPtr->MessageField = MessageField;
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
playerPtr = &PlayerData[static_cast<int>(floor(value))];
|
||||||
FlasherActive = playerPtr->FlasherActive;
|
FlasherOnFlag = playerPtr->FlasherOnFlag;
|
||||||
BmpIndex2 = playerPtr->BmpIndex2;
|
LightOnBmpIndex = playerPtr->LightOnBmpIndex;
|
||||||
BmpIndex1 = playerPtr->BmpIndex1;
|
LightOnFlag = playerPtr->LightOnFlag;
|
||||||
MessageField = playerPtr->MessageField;
|
MessageField = playerPtr->MessageField;
|
||||||
if (BmpIndex2)
|
if (LightOnBmpIndex)
|
||||||
{
|
{
|
||||||
Message(11, static_cast<float>(BmpIndex2));
|
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(LightOnBmpIndex));
|
||||||
}
|
}
|
||||||
if (BmpIndex1)
|
if (LightOnFlag)
|
||||||
Message(1, 0.0);
|
Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
if (FlasherActive)
|
if (FlasherOnFlag)
|
||||||
Message(4, 0.0);
|
Message(MessageCode::TLightFlasherStart, 0.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0:
|
case MessageCode::TLightTurnOff:
|
||||||
BmpIndex1 = 0;
|
LightOnFlag = false;
|
||||||
if (FlasherActive == 0 && !FlasherFlag1 && !FlasherFlag2)
|
if (!FlasherOnFlag && !ToggledOffFlag && !ToggledOnFlag)
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[0]);
|
SetSpriteBmp(BmpArr[0]);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case MessageCode::TLightTurnOn:
|
||||||
BmpIndex1 = 1;
|
LightOnFlag = true;
|
||||||
if (FlasherActive == 0 && !FlasherFlag1 && !FlasherFlag2)
|
if (!FlasherOnFlag && !ToggledOffFlag && !ToggledOnFlag)
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[1]);
|
SetSpriteBmp(BmpArr[1]);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case MessageCode::TLightGetLightOnFlag:
|
||||||
return BmpIndex1;
|
return LightOnFlag;
|
||||||
case 3:
|
case MessageCode::TLightGetFlasherOnFlag:
|
||||||
return FlasherActive;
|
return FlasherOnFlag;
|
||||||
case 4:
|
case MessageCode::TLightFlasherStart:
|
||||||
schedule_timeout(0.0);
|
schedule_timeout(0.0);
|
||||||
if (!FlasherActive || !Flasher.Timer)
|
if (!FlasherOnFlag || !FlashTimer)
|
||||||
{
|
{
|
||||||
FlasherActive = 1;
|
FlasherOnFlag = true;
|
||||||
FlasherFlag2 = 0;
|
ToggledOnFlag = false;
|
||||||
FlasherFlag1 = 0;
|
ToggledOffFlag = false;
|
||||||
TurnOffAfterFlashingFg = 0;
|
TurnOffAfterFlashingFg = false;
|
||||||
flasher_start(&Flasher, BmpIndex1);
|
flasher_start(LightOnFlag);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case MessageCode::TLightApplyMultDelay:
|
||||||
Flasher.TimerDelay[0] = value * FlasherDelay[0];
|
FlashDelay[0] = value * SourceDelay[0];
|
||||||
Flasher.TimerDelay[1] = value * FlasherDelay[1];
|
FlashDelay[1] = value * SourceDelay[1];
|
||||||
break;
|
break;
|
||||||
case 6:
|
case MessageCode::TLightApplyDelay:
|
||||||
Flasher.TimerDelay[0] = FlasherDelay[0];
|
FlashDelay[0] = SourceDelay[0];
|
||||||
Flasher.TimerDelay[1] = FlasherDelay[1];
|
FlashDelay[1] = SourceDelay[1];
|
||||||
break;
|
break;
|
||||||
case 7:
|
case MessageCode::TLightFlasherStartTimed:
|
||||||
if (!FlasherActive)
|
if (!FlasherOnFlag)
|
||||||
flasher_start(&Flasher, BmpIndex1);
|
flasher_start(LightOnFlag);
|
||||||
FlasherActive = 1;
|
FlasherOnFlag = true;
|
||||||
FlasherFlag2 = 0;
|
ToggledOnFlag = false;
|
||||||
TurnOffAfterFlashingFg = 0;
|
TurnOffAfterFlashingFg = false;
|
||||||
FlasherFlag1 = 0;
|
ToggledOffFlag = false;
|
||||||
schedule_timeout(value);
|
schedule_timeout(value);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case MessageCode::TLightTurnOffTimed:
|
||||||
if (!FlasherFlag1)
|
if (!ToggledOffFlag)
|
||||||
{
|
{
|
||||||
if (FlasherActive)
|
if (FlasherOnFlag)
|
||||||
{
|
{
|
||||||
flasher_stop(&Flasher, 0);
|
flasher_stop(0);
|
||||||
FlasherActive = 0;
|
FlasherOnFlag = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[0]);
|
SetSpriteBmp(BmpArr[0]);
|
||||||
}
|
}
|
||||||
FlasherFlag1 = 1;
|
ToggledOffFlag = true;
|
||||||
FlasherFlag2 = 0;
|
ToggledOnFlag = false;
|
||||||
}
|
}
|
||||||
schedule_timeout(value);
|
schedule_timeout(value);
|
||||||
break;
|
break;
|
||||||
case 9:
|
case MessageCode::TLightTurnOnTimed:
|
||||||
if (!FlasherFlag2)
|
if (!ToggledOnFlag)
|
||||||
{
|
{
|
||||||
if (FlasherActive)
|
if (FlasherOnFlag)
|
||||||
{
|
{
|
||||||
flasher_stop(&Flasher, 1);
|
flasher_stop(1);
|
||||||
FlasherActive = 0;
|
FlasherOnFlag = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[1]);
|
SetSpriteBmp(BmpArr[1]);
|
||||||
}
|
}
|
||||||
FlasherFlag2 = 1;
|
ToggledOnFlag = true;
|
||||||
FlasherFlag1 = 0;
|
ToggledOffFlag = false;
|
||||||
}
|
}
|
||||||
schedule_timeout(value);
|
schedule_timeout(value);
|
||||||
break;
|
break;
|
||||||
case 11:
|
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 (ToggledOffFlag)
|
||||||
bmpIndex = 0;
|
bmpIndex = 0;
|
||||||
BmpIndex2 = static_cast<int>(floor(value));
|
else if (ToggledOnFlag)
|
||||||
if (BmpIndex2 > static_cast<int>(ListBitmap->size()))
|
|
||||||
BmpIndex2 = ListBitmap->size();
|
|
||||||
if (BmpIndex2 < 0)
|
|
||||||
BmpIndex2 = 0;
|
|
||||||
Flasher.BmpArr[0] = nullptr;
|
|
||||||
Flasher.BmpArr[1] = ListBitmap->at(BmpIndex2);
|
|
||||||
if (FlasherActive == 0)
|
|
||||||
{
|
|
||||||
if (!FlasherFlag1)
|
|
||||||
{
|
|
||||||
if (FlasherFlag2)
|
|
||||||
bmpIndex = 1;
|
bmpIndex = 1;
|
||||||
else
|
else
|
||||||
bmpIndex = BmpIndex1;
|
bmpIndex = LightOnFlag;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bmpIndex = Flasher.BmpIndex;
|
bmpIndex = FlashLightOnFlag;
|
||||||
}
|
}
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[bmpIndex]);
|
SetSpriteBmp(BmpArr[bmpIndex]);
|
||||||
break;
|
break;
|
||||||
case 12:
|
case MessageCode::TLightIncOnStateBmpIndex:
|
||||||
bmpIndex = BmpIndex2 + 1;
|
bmpIndex = LightOnBmpIndex + 1;
|
||||||
if (bmpIndex > static_cast<int>(ListBitmap->size()))
|
if (bmpIndex >= static_cast<int>(ListBitmap->size()))
|
||||||
bmpIndex = ListBitmap->size();
|
bmpIndex = static_cast<int>(ListBitmap->size()) - 1;
|
||||||
Message(11, static_cast<float>(bmpIndex));
|
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(bmpIndex));
|
||||||
break;
|
break;
|
||||||
case 13:
|
case MessageCode::TLightDecOnStateBmpIndex:
|
||||||
bmpIndex = BmpIndex2 - 1;
|
bmpIndex = LightOnBmpIndex - 1;
|
||||||
if (bmpIndex < 0)
|
if (bmpIndex < 0)
|
||||||
bmpIndex = 0;
|
bmpIndex = 0;
|
||||||
Message(11, static_cast<float>(bmpIndex));
|
Message(MessageCode::TLightSetOnStateBmpIndex, static_cast<float>(bmpIndex));
|
||||||
break;
|
break;
|
||||||
case 14:
|
case MessageCode::TLightResetTimed:
|
||||||
if (Timer1)
|
if (TimeoutTimer)
|
||||||
timer::kill(Timer1);
|
timer::kill(TimeoutTimer);
|
||||||
Timer1 = 0;
|
TimeoutTimer = 0;
|
||||||
if (FlasherActive != 0)
|
if (FlasherOnFlag)
|
||||||
flasher_stop(&Flasher, -1);
|
flasher_stop(-1);
|
||||||
FlasherActive = 0;
|
FlasherOnFlag = false;
|
||||||
FlasherFlag1 = 0;
|
ToggledOffFlag = false;
|
||||||
FlasherFlag2 = 0;
|
ToggledOnFlag = false;
|
||||||
render::sprite_set_bitmap(RenderSprite, Flasher.BmpArr[BmpIndex1]);
|
SetSpriteBmp(BmpArr[LightOnFlag]);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case MessageCode::TLightFlasherStartTimedThenStayOn:
|
||||||
TurnOffAfterFlashingFg = 0;
|
TurnOffAfterFlashingFg = false;
|
||||||
if (Timer2)
|
if (UndoOverrideTimer)
|
||||||
timer::kill(Timer2);
|
timer::kill(UndoOverrideTimer);
|
||||||
Timer2 = 0;
|
UndoOverrideTimer = 0;
|
||||||
Message(1, 0.0);
|
Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
Message(7, value);
|
Message(MessageCode::TLightFlasherStartTimed, value);
|
||||||
break;
|
break;
|
||||||
case 16:
|
case MessageCode::TLightFlasherStartTimedThenStayOff:
|
||||||
if (Timer2)
|
if (UndoOverrideTimer)
|
||||||
timer::kill(Timer2);
|
timer::kill(UndoOverrideTimer);
|
||||||
Timer2 = 0;
|
UndoOverrideTimer = 0;
|
||||||
Message(7, value);
|
Message(MessageCode::TLightFlasherStartTimed, value);
|
||||||
TurnOffAfterFlashingFg = 1;
|
TurnOffAfterFlashingFg = true;
|
||||||
break;
|
break;
|
||||||
case 17:
|
case MessageCode::TLightToggleValue:
|
||||||
Message(static_cast<int>(floor(value)) != 0, 0.0);
|
Message(static_cast<int>(floor(value)) ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||||
return BmpIndex1;
|
return LightOnFlag;
|
||||||
case 18:
|
case MessageCode::TLightResetAndToggleValue:
|
||||||
Message(17, value);
|
Message(MessageCode::TLightToggleValue, value);
|
||||||
Message(14, 0.0);
|
Message(MessageCode::TLightResetTimed, 0.0);
|
||||||
return BmpIndex1;
|
return LightOnFlag;
|
||||||
case 19:
|
case MessageCode::TLightResetAndTurnOn:
|
||||||
Message(1, 0.0);
|
Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
Message(14, 0.0);
|
Message(MessageCode::TLightResetTimed, 0.0);
|
||||||
break;
|
break;
|
||||||
case 20:
|
case MessageCode::TLightResetAndTurnOff:
|
||||||
Message(0, 0.0);
|
Message(MessageCode::TLightTurnOff, 0.0);
|
||||||
Message(14, 0.0);
|
Message(MessageCode::TLightResetTimed, 0.0);
|
||||||
break;
|
break;
|
||||||
case 21:
|
case MessageCode::TLightToggle:
|
||||||
Message(17, static_cast<float>(BmpIndex1 == 0));
|
Message(MessageCode::TLightToggleValue, !LightOnFlag);
|
||||||
return BmpIndex1;
|
return LightOnFlag;
|
||||||
case 22:
|
case MessageCode::TLightResetAndToggle:
|
||||||
Message(18, static_cast<float>(BmpIndex1 == 0));
|
Message(MessageCode::TLightResetAndToggleValue, !LightOnFlag);
|
||||||
return BmpIndex1;
|
return LightOnFlag;
|
||||||
case 23:
|
case MessageCode::TLightSetMessageField:
|
||||||
MessageField = static_cast<int>(floor(value));
|
MessageField = static_cast<int>(floor(value));
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -237,85 +251,99 @@ int TLight::Message(int code, float value)
|
||||||
|
|
||||||
void TLight::Reset()
|
void TLight::Reset()
|
||||||
{
|
{
|
||||||
if (Timer1)
|
if (TimeoutTimer)
|
||||||
timer::kill(Timer1);
|
timer::kill(TimeoutTimer);
|
||||||
if (Timer2)
|
if (UndoOverrideTimer)
|
||||||
timer::kill(Timer2);
|
timer::kill(UndoOverrideTimer);
|
||||||
if (FlasherActive)
|
if (FlasherOnFlag)
|
||||||
flasher_stop(&Flasher, -1);
|
flasher_stop(-1);
|
||||||
Unknown20F = 1.0;
|
TimeoutTimer = 0;
|
||||||
Timer1 = 0;
|
UndoOverrideTimer = 0;
|
||||||
Timer2 = 0;
|
LightOnFlag = false;
|
||||||
BmpIndex1 = 0;
|
LightOnBmpIndex = 0;
|
||||||
BmpIndex2 = 0;
|
ToggledOffFlag = false;
|
||||||
FlasherFlag1 = 0;
|
ToggledOnFlag = false;
|
||||||
FlasherFlag2 = 0;
|
FlasherOnFlag = false;
|
||||||
FlasherActive = 0;
|
TemporaryOverrideFlag = false;
|
||||||
TurnOffAfterFlashingFg = 0;
|
TurnOffAfterFlashingFg = false;
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
PreviousBitmap = -1;
|
||||||
Flasher.Sprite = RenderSprite;
|
BmpArr[0] = -1;
|
||||||
Flasher.BmpArr[0] = nullptr;
|
BmpArr[1] = 0;
|
||||||
if (ListBitmap)
|
SetSpriteBmp(BmpArr[0]);
|
||||||
Flasher.BmpArr[1] = ListBitmap->at(0);
|
|
||||||
Flasher.Unknown4 = 0;
|
|
||||||
Flasher.Unknown3 = 0;
|
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLight::schedule_timeout(float time)
|
void TLight::schedule_timeout(float time)
|
||||||
{
|
{
|
||||||
Flasher.TimerDelay[0] = FlasherDelay[0];
|
FlashDelay[0] = SourceDelay[0];
|
||||||
Flasher.TimerDelay[1] = FlasherDelay[1];
|
FlashDelay[1] = SourceDelay[1];
|
||||||
if (Timer1)
|
if (TimeoutTimer)
|
||||||
timer::kill(Timer1);
|
timer::kill(TimeoutTimer);
|
||||||
Timer1 = 0;
|
TimeoutTimer = 0;
|
||||||
if (time > 0.0f)
|
if (time > 0.0f)
|
||||||
Timer1 = timer::set(time, this, TimerExpired);
|
TimeoutTimer = timer::set(time, this, TimerExpired);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLight::TimerExpired(int timerId, void* caller)
|
void TLight::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto light = static_cast<TLight*>(caller);
|
auto light = static_cast<TLight*>(caller);
|
||||||
if (light->FlasherActive)
|
if (light->FlasherOnFlag)
|
||||||
flasher_stop(&light->Flasher, -1);
|
light->flasher_stop(-1);
|
||||||
render::sprite_set_bitmap(light->RenderSprite, light->Flasher.BmpArr[light->BmpIndex1]);
|
light->SetSpriteBmp(light->BmpArr[light->LightOnFlag]);
|
||||||
light->FlasherFlag1 = 0;
|
light->ToggledOffFlag = false;
|
||||||
light->FlasherFlag2 = 0;
|
light->ToggledOnFlag = false;
|
||||||
light->FlasherActive = 0;
|
light->FlasherOnFlag = false;
|
||||||
light->Timer1 = 0;
|
light->TimeoutTimer = 0;
|
||||||
if (light->TurnOffAfterFlashingFg != 0)
|
if (light->TurnOffAfterFlashingFg)
|
||||||
{
|
{
|
||||||
light->TurnOffAfterFlashingFg = 0;
|
light->TurnOffAfterFlashingFg = false;
|
||||||
light->Message(20, 0.0);
|
light->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||||
}
|
}
|
||||||
if (light->Control)
|
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)
|
if (FlashTimer)
|
||||||
timer::kill(flash->Timer);
|
timer::kill(FlashTimer);
|
||||||
flash->Timer = 0;
|
FlashTimer = 0;
|
||||||
if (bmpIndex >= 0)
|
if (bmpIndex >= 0)
|
||||||
{
|
{
|
||||||
flash->BmpIndex = bmpIndex;
|
FlashLightOnFlag = bmpIndex;
|
||||||
render::sprite_set_bitmap(flash->Sprite, flash->BmpArr[bmpIndex]);
|
SetSpriteBmp(BmpArr[FlashLightOnFlag]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLight::flasher_start(flasher_type* flash, int bmpIndex)
|
void TLight::flasher_start(bool bmpIndex)
|
||||||
{
|
{
|
||||||
flash->BmpIndex = bmpIndex;
|
FlashLightOnFlag = bmpIndex;
|
||||||
flasher_callback(0, flash);
|
flasher_callback(0, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TLight::SetSpriteBmp(int index)
|
||||||
|
{
|
||||||
|
PreviousBitmap = index;
|
||||||
|
if (!TemporaryOverrideFlag)
|
||||||
|
SpriteSet(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLight::flasher_callback(int timerId, void* caller)
|
void TLight::flasher_callback(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto flash = static_cast<flasher_type*>(caller);
|
auto light = static_cast<TLight*>(caller);
|
||||||
auto index = 1 - flash->BmpIndex;
|
light->FlashLightOnFlag ^= true;
|
||||||
flash->BmpIndex = index;
|
light->SetSpriteBmp(light->BmpArr[light->FlashLightOnFlag]);
|
||||||
render::sprite_set_bitmap(flash->Sprite, flash->BmpArr[index]);
|
light->FlashTimer = timer::set(light->FlashDelay[light->FlashLightOnFlag], light, flasher_callback);
|
||||||
flash->Timer = timer::set(flash->TimerDelay[flash->BmpIndex], flash, 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
|
#pragma once
|
||||||
#include "TPinballComponent.h"
|
#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
|
struct TLight_player_backup
|
||||||
{
|
{
|
||||||
int MessageField;
|
int MessageField;
|
||||||
int BmpIndex1;
|
bool LightOnFlag;
|
||||||
int FlasherActive;
|
int LightOnBmpIndex;
|
||||||
int Unknown3;
|
bool FlasherOnFlag;
|
||||||
int Unknown4;
|
|
||||||
int BmpIndex2;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,25 +15,32 @@ class TLight :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TLight(TPinballTable* table, int groupIndex);
|
TLight(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void Reset();
|
void Reset();
|
||||||
void schedule_timeout(float time);
|
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 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 flasher_callback(int timerId, void* caller);
|
||||||
|
static void UndoTmpOverride(int timerId, void* caller);
|
||||||
|
|
||||||
flasher_type Flasher{};
|
int BmpArr[2]{-1};
|
||||||
int BmpIndex1{};
|
float FlashDelay[2]{};
|
||||||
int FlasherActive;
|
int FlashTimer;
|
||||||
int FlasherFlag1{};
|
bool FlashLightOnFlag{};
|
||||||
int FlasherFlag2{};
|
bool LightOnFlag{};
|
||||||
int TurnOffAfterFlashingFg{};
|
bool FlasherOnFlag;
|
||||||
int BmpIndex2{};
|
bool ToggledOffFlag{};
|
||||||
float FlasherDelay[2]{};
|
bool ToggledOnFlag{};
|
||||||
int Timer1;
|
bool TurnOffAfterFlashingFg{};
|
||||||
int Timer2;
|
int LightOnBmpIndex{};
|
||||||
float Unknown20F{};
|
float SourceDelay[2]{};
|
||||||
|
int TimeoutTimer;
|
||||||
|
int UndoOverrideTimer;
|
||||||
|
bool TemporaryOverrideFlag{};
|
||||||
|
int PreviousBitmap = -1;
|
||||||
TLight_player_backup PlayerData[4]{};
|
TLight_player_backup PlayerData[4]{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,13 +32,13 @@ TLightBargraph::~TLightBargraph()
|
||||||
delete[] TimerTimeArray;
|
delete[] TimerTimeArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TLightBargraph::Message(int code, float value)
|
int TLightBargraph::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 37:
|
case MessageCode::TLightGroupGetOnCount:
|
||||||
return TimeIndex;
|
return TimeIndex;
|
||||||
case 45:
|
case MessageCode::TLightGroupToggleSplitIndex:
|
||||||
{
|
{
|
||||||
if (TimerBargraph)
|
if (TimerBargraph)
|
||||||
{
|
{
|
||||||
|
@ -51,24 +51,24 @@ int TLightBargraph::Message(int code, float value)
|
||||||
timeIndex = maxCount - 1;
|
timeIndex = maxCount - 1;
|
||||||
if (timeIndex >= 0)
|
if (timeIndex >= 0)
|
||||||
{
|
{
|
||||||
TLightGroup::Message(45, static_cast<float>(timeIndex / 2));
|
TLightGroup::Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(timeIndex / 2));
|
||||||
if (!(timeIndex & 1))
|
if (!(timeIndex & 1))
|
||||||
TLightGroup::Message(46, 0.0);
|
TLightGroup::Message(MessageCode::TLightGroupStartFlasher, 0.0);
|
||||||
if (TimerTimeArray)
|
if (TimerTimeArray)
|
||||||
TimerBargraph = timer::set(TimerTimeArray[timeIndex], this, BargraphTimerExpired);
|
TimerBargraph = timer::set(TimerTimeArray[timeIndex], this, BargraphTimerExpired);
|
||||||
TimeIndex = timeIndex;
|
TimeIndex = timeIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TLightGroup::Message(20, 0.0);
|
TLightGroup::Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||||
TimeIndex = 0;
|
TimeIndex = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1011:
|
case MessageCode::SetTiltLock:
|
||||||
Reset();
|
Reset();
|
||||||
break;
|
break;
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
if (TimerBargraph)
|
if (TimerBargraph)
|
||||||
{
|
{
|
||||||
timer::kill(TimerBargraph);
|
timer::kill(TimerBargraph);
|
||||||
|
@ -79,10 +79,10 @@ int TLightBargraph::Message(int code, float value)
|
||||||
TimeIndex = PlayerTimerIndexBackup[static_cast<int>(floor(value))];
|
TimeIndex = PlayerTimerIndexBackup[static_cast<int>(floor(value))];
|
||||||
if (TimeIndex)
|
if (TimeIndex)
|
||||||
{
|
{
|
||||||
TLightBargraph::Message(45, static_cast<float>(TimeIndex));
|
TLightBargraph::Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(TimeIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
int* playerPtr = PlayerTimerIndexBackup;
|
int* playerPtr = PlayerTimerIndexBackup;
|
||||||
|
@ -92,7 +92,7 @@ int TLightBargraph::Message(int code, float value)
|
||||||
|
|
||||||
++playerPtr;
|
++playerPtr;
|
||||||
}
|
}
|
||||||
TLightGroup::Message(1024, value);
|
TLightGroup::Message(MessageCode::Reset, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -119,12 +119,12 @@ void TLightBargraph::BargraphTimerExpired(int timerId, void* caller)
|
||||||
bar->TimerBargraph = 0;
|
bar->TimerBargraph = 0;
|
||||||
if (bar->TimeIndex)
|
if (bar->TimeIndex)
|
||||||
{
|
{
|
||||||
bar->Message(45, static_cast<float>(bar->TimeIndex - 1));
|
bar->Message(MessageCode::TLightGroupToggleSplitIndex, static_cast<float>(bar->TimeIndex - 1));
|
||||||
control::handler(60, bar);
|
control::handler(MessageCode::ControlTimerExpired, bar);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bar->Message(20, 0.0);
|
bar->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||||
control::handler(47, bar);
|
control::handler(MessageCode::TLightGroupCountdownEnded, bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ class TLightBargraph :
|
||||||
public:
|
public:
|
||||||
TLightBargraph(TPinballTable* table, int groupIndex);
|
TLightBargraph(TPinballTable* table, int groupIndex);
|
||||||
~TLightBargraph() override;
|
~TLightBargraph() override;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
|
|
||||||
static void BargraphTimerExpired(int timerId, void* caller);
|
static void BargraphTimerExpired(int timerId, void* caller);
|
||||||
|
|
|
@ -28,15 +28,15 @@ TLightGroup::TLightGroup(TPinballTable* table, int groupIndex) : TPinballCompone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TLightGroup::Message(int code, float value)
|
int TLightGroup::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
auto const count = static_cast<int>(List.size());
|
auto const count = static_cast<int>(List.size());
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1011:
|
case MessageCode::SetTiltLock:
|
||||||
case 1022:
|
case MessageCode::GameOver:
|
||||||
break;
|
break;
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
{
|
{
|
||||||
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
auto playerPtr = &PlayerData[PinballTable->CurrentPlayer];
|
||||||
playerPtr->MessageField = MessageField;
|
playerPtr->MessageField = MessageField;
|
||||||
|
@ -53,7 +53,7 @@ int TLightGroup::Message(int code, float value)
|
||||||
TimerExpired(0, this);
|
TimerExpired(0, this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
Reset();
|
Reset();
|
||||||
for (auto index = 0; index < PinballTable->PlayerCount; index++)
|
for (auto index = 0; index < PinballTable->PlayerCount; index++)
|
||||||
{
|
{
|
||||||
|
@ -63,99 +63,99 @@ int TLightGroup::Message(int code, float value)
|
||||||
playerPtr->Timer1Time = Timer1Time;
|
playerPtr->Timer1Time = Timer1Time;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 24:
|
case MessageCode::TLightGroupStepBackward:
|
||||||
{
|
{
|
||||||
auto lastLight = List.at(count - 1);
|
auto lastLight = List.at(count - 1);
|
||||||
if (lastLight->FlasherActive || lastLight->FlasherFlag2 || lastLight->FlasherFlag1)
|
if (lastLight->FlasherOnFlag || lastLight->ToggledOnFlag || lastLight->ToggledOffFlag)
|
||||||
break;
|
break;
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
{
|
{
|
||||||
TLightGroup::Message(34, 0.0);
|
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
}
|
}
|
||||||
AnimationFlag = 1;
|
AnimationFlag = 1;
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
auto lightMessageField = lastLight->MessageField;
|
auto lastMessage = lastLight->MessageField;
|
||||||
auto bmpIndex1 = lastLight->BmpIndex1;
|
auto lastStatus = lastLight->LightOnFlag;
|
||||||
for (auto index = count - 1; index > 0; --index)
|
for (auto index = count - 1; index > 0; --index)
|
||||||
{
|
{
|
||||||
auto lightCur = List.at(index);
|
auto lightCur = List.at(index);
|
||||||
auto lightPrev = List.at(index - 1);
|
auto lightPrev = List.at(index - 1);
|
||||||
lightCur->Message(lightPrev->BmpIndex1 != 0, 0.0);
|
lightCur->Message(lightPrev->LightOnFlag ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||||
lightCur->MessageField = lightPrev->MessageField;
|
lightCur->MessageField = lightPrev->MessageField;
|
||||||
}
|
}
|
||||||
auto firstLight = List.at(0);
|
auto firstLight = List.at(0);
|
||||||
firstLight->Message(bmpIndex1 != 0, 0.0);
|
firstLight->Message(lastStatus ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||||
firstLight->MessageField = lightMessageField;
|
firstLight->MessageField = lastMessage;
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 25:
|
case MessageCode::TLightGroupStepForward:
|
||||||
{
|
{
|
||||||
auto lastLight = List.at(count - 1);
|
auto lastLight = List.at(count - 1);
|
||||||
if (lastLight->FlasherActive || lastLight->FlasherFlag2 || lastLight->FlasherFlag1)
|
if (lastLight->FlasherOnFlag || lastLight->ToggledOnFlag || lastLight->ToggledOffFlag)
|
||||||
break;
|
break;
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
{
|
{
|
||||||
TLightGroup::Message(34, 0.0);
|
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
}
|
}
|
||||||
auto firstLight = List.at(0);
|
auto firstLight = List.at(0);
|
||||||
AnimationFlag = 1;
|
AnimationFlag = 1;
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
auto lightMessageField = firstLight->MessageField;
|
auto firstMessage = firstLight->MessageField;
|
||||||
auto bmpIndex1 = firstLight->BmpIndex1;
|
auto firstStatus = firstLight->LightOnFlag;
|
||||||
for (auto index = 0; index < count - 1; index++)
|
for (auto index = 0; index < count - 1; index++)
|
||||||
{
|
{
|
||||||
auto lightCur = List.at(index);
|
auto lightCur = List.at(index);
|
||||||
auto lightNext = List.at(index + 1);
|
auto lightNext = List.at(index + 1);
|
||||||
lightCur->Message(lightNext->BmpIndex1 != 0, 0.0);
|
lightCur->Message(lightNext->LightOnFlag ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||||
lightCur->MessageField = lightNext->MessageField;
|
lightCur->MessageField = lightNext->MessageField;
|
||||||
}
|
}
|
||||||
lastLight->Message(bmpIndex1 != 0, 0.0);
|
lastLight->Message(firstStatus ? MessageCode::TLightTurnOn : MessageCode::TLightTurnOff, 0.0);
|
||||||
lastLight->MessageField = lightMessageField;
|
lastLight->MessageField = firstMessage;
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 26:
|
case MessageCode::TLightGroupAnimationBackward:
|
||||||
{
|
{
|
||||||
if (AnimationFlag || !MessageField2)
|
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
auto lastLight = List.at(count - 1);
|
auto lastLight = List.at(count - 1);
|
||||||
auto flasherFlag2 = lastLight->FlasherFlag2;
|
auto lastStatus = lastLight->ToggledOnFlag;
|
||||||
for (auto i = count - 1; i > 0; --i)
|
for (auto i = count - 1; i > 0; --i)
|
||||||
{
|
{
|
||||||
auto lightCur = List.at(i);
|
auto lightCur = List.at(i);
|
||||||
auto lightPrev = List.at(i - 1);
|
auto lightPrev = List.at(i - 1);
|
||||||
lightCur->Message((lightPrev->FlasherFlag2 != 0) + 8, 0.0);
|
lightCur->Message(lightPrev->ToggledOnFlag ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0.0);
|
||||||
}
|
}
|
||||||
auto firstLight = List.at(0);
|
auto firstLight = List.at(0);
|
||||||
firstLight->Message((flasherFlag2 != 0) + 8, 0);
|
firstLight->Message(lastStatus ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0);
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 27:
|
case MessageCode::TLightGroupAnimationForward:
|
||||||
{
|
{
|
||||||
if (AnimationFlag || !MessageField2)
|
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
auto firstLight = List.at(0);
|
auto firstLight = List.at(0);
|
||||||
auto flasherFlag2 = firstLight->FlasherFlag2;
|
auto firstStatus = firstLight->ToggledOnFlag;
|
||||||
for (auto i = 0; i < count - 1; i++)
|
for (auto i = 0; i < count - 1; i++)
|
||||||
{
|
{
|
||||||
auto lightCur = List.at(i);
|
auto lightCur = List.at(i);
|
||||||
auto lightNext = List.at(i + 1);
|
auto lightNext = List.at(i + 1);
|
||||||
lightCur->Message((lightNext->FlasherFlag2 != 0) + 8, 0.0);
|
lightCur->Message(lightNext->ToggledOnFlag ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0.0);
|
||||||
}
|
}
|
||||||
auto lastLight = List.at(count - 1);
|
auto lastLight = List.at(count - 1);
|
||||||
lastLight->Message((flasherFlag2 != 0) + 8, 0);
|
lastLight->Message(firstStatus ? MessageCode::TLightTurnOnTimed : MessageCode::TLightTurnOffTimed, 0);
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 28:
|
case MessageCode::TLightGroupLightShowAnimation:
|
||||||
{
|
{
|
||||||
if (AnimationFlag || !MessageField2)
|
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
|
@ -164,32 +164,32 @@ int TLightGroup::Message(int code, float value)
|
||||||
if (rand() % 100 > 70)
|
if (rand() % 100 > 70)
|
||||||
{
|
{
|
||||||
auto randVal = RandFloat() * value * 3.0f + 0.1f;
|
auto randVal = RandFloat() * value * 3.0f + 0.1f;
|
||||||
light->Message(9, randVal);
|
light->Message(MessageCode::TLightTurnOnTimed, randVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 29:
|
case MessageCode::TLightGroupGameOverAnimation:
|
||||||
{
|
{
|
||||||
if (AnimationFlag || !MessageField2)
|
if (AnimationFlag || MessageField2 == MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
MessageField2 = code;
|
MessageField2 = code;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
for (auto light : List)
|
for (auto light : List)
|
||||||
{
|
{
|
||||||
auto randVal = static_cast<float>(rand() % 100 > 70);
|
auto randVal = static_cast<float>(rand() % 100 > 70);
|
||||||
light->Message(18, randVal);
|
light->Message(MessageCode::TLightResetAndToggleValue, randVal);
|
||||||
}
|
}
|
||||||
reschedule_animation(value);
|
reschedule_animation(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 30:
|
case MessageCode::TLightGroupRandomAnimationSaturation:
|
||||||
{
|
{
|
||||||
auto noBmpInd1Count = 0;
|
auto noBmpInd1Count = 0;
|
||||||
for (auto light : List)
|
for (auto light : List)
|
||||||
{
|
{
|
||||||
if (!light->BmpIndex1)
|
if (!light->LightOnFlag)
|
||||||
++noBmpInd1Count;
|
++noBmpInd1Count;
|
||||||
}
|
}
|
||||||
if (!noBmpInd1Count)
|
if (!noBmpInd1Count)
|
||||||
|
@ -199,23 +199,23 @@ int TLightGroup::Message(int code, float value)
|
||||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto light = *it;
|
auto light = *it;
|
||||||
if (!light->BmpIndex1 && randModCount-- == 0)
|
if (!light->LightOnFlag && randModCount-- == 0)
|
||||||
{
|
{
|
||||||
light->Message(1, 0.0);
|
light->Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 31:
|
case MessageCode::TLightGroupRandomAnimationDesaturation:
|
||||||
{
|
{
|
||||||
auto bmpInd1Count = 0;
|
auto bmpInd1Count = 0;
|
||||||
for (auto light : List)
|
for (auto light : List)
|
||||||
{
|
{
|
||||||
if (light->BmpIndex1)
|
if (light->LightOnFlag)
|
||||||
++bmpInd1Count;
|
++bmpInd1Count;
|
||||||
}
|
}
|
||||||
if (!bmpInd1Count)
|
if (!bmpInd1Count)
|
||||||
|
@ -225,130 +225,131 @@ int TLightGroup::Message(int code, float value)
|
||||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto light = *it;
|
auto light = *it;
|
||||||
if (light->BmpIndex1 && randModCount-- == 0)
|
if (light->LightOnFlag && randModCount-- == 0)
|
||||||
{
|
{
|
||||||
light->Message(0, 0.0);
|
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 32:
|
case MessageCode::TLightGroupOffsetAnimationForward:
|
||||||
{
|
{
|
||||||
auto index = next_light_up();
|
auto index = next_light_up();
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
break;
|
break;
|
||||||
List.at(index)->Message(1, 0.0);
|
List.at(index)->Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case 33:
|
case MessageCode::TLightGroupOffsetAnimationBackward:
|
||||||
{
|
{
|
||||||
auto index = next_light_down();
|
auto index = next_light_down();
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
break;
|
break;
|
||||||
List.at(index)->Message(0, 0.0);
|
List.at(index)->Message(MessageCode::TLightTurnOff, 0.0);
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case 34:
|
case MessageCode::TLightGroupReset:
|
||||||
{
|
{
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
if (MessageField2 == 26 || MessageField2 == 27 || MessageField2 == 28)
|
if (MessageField2 == MessageCode::TLightGroupAnimationBackward ||
|
||||||
TLightGroup::Message(14, 0.0);
|
MessageField2 == MessageCode::TLightGroupAnimationForward || MessageField2 == MessageCode::TLightGroupLightShowAnimation)
|
||||||
MessageField2 = 0;
|
TLightGroup::Message(MessageCode::TLightResetTimed, 0.0);
|
||||||
|
MessageField2 = MessageCode::TLightGroupNull;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 35:
|
case MessageCode::TLightGroupTurnOnAtIndex:
|
||||||
{
|
{
|
||||||
auto index = static_cast<int>(floor(value));
|
auto index = static_cast<int>(floor(value));
|
||||||
if (index >= count || index < 0)
|
if (index >= count || index < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto light = List.at(index);
|
auto light = List.at(index);
|
||||||
light->Message(1, 0.0);
|
light->Message(MessageCode::TLightTurnOn, 0.0);
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 36:
|
case MessageCode::TLightGroupTurnOffAtIndex:
|
||||||
{
|
{
|
||||||
auto index = static_cast<int>(floor(value));
|
auto index = static_cast<int>(floor(value));
|
||||||
if (index >= count || index < 0)
|
if (index >= count || index < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto light = List.at(index);
|
auto light = List.at(index);
|
||||||
light->Message(0, 0.0);
|
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||||
if (MessageField2)
|
if (MessageField2 != MessageCode::TLightGroupNull)
|
||||||
start_animation();
|
start_animation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 37:
|
case MessageCode::TLightGroupGetOnCount:
|
||||||
{
|
{
|
||||||
auto bmp1Count = 0;
|
auto bmp1Count = 0;
|
||||||
for (auto light : List)
|
for (auto light : List)
|
||||||
{
|
{
|
||||||
if (light->BmpIndex1)
|
if (light->LightOnFlag)
|
||||||
++bmp1Count;
|
++bmp1Count;
|
||||||
}
|
}
|
||||||
return bmp1Count;
|
return bmp1Count;
|
||||||
}
|
}
|
||||||
case 38:
|
case MessageCode::TLightGroupGetLightCount:
|
||||||
return count;
|
return count;
|
||||||
case 39:
|
case MessageCode::TLightGroupGetMessage2:
|
||||||
return MessageField2;
|
return static_cast<int>(MessageField2);
|
||||||
case 40:
|
case MessageCode::TLightGroupGetAnimationFlag:
|
||||||
return AnimationFlag;
|
return AnimationFlag;
|
||||||
case 41:
|
case MessageCode::TLightGroupResetAndTurnOn:
|
||||||
{
|
{
|
||||||
auto index = next_light_up();
|
auto index = next_light_up();
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
break;
|
break;
|
||||||
if (MessageField2 || AnimationFlag)
|
if (MessageField2 != MessageCode::TLightGroupNull || AnimationFlag)
|
||||||
TLightGroup::Message(34, 0.0);
|
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
List.at(index)->Message(15, value);
|
List.at(index)->Message(MessageCode::TLightFlasherStartTimedThenStayOn, value);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case 42:
|
case MessageCode::TLightGroupResetAndTurnOff:
|
||||||
{
|
{
|
||||||
auto index = next_light_down();
|
auto index = next_light_down();
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
break;
|
break;
|
||||||
if (MessageField2 || AnimationFlag)
|
if (MessageField2 != MessageCode::TLightGroupNull || AnimationFlag)
|
||||||
TLightGroup::Message(34, 0.0);
|
TLightGroup::Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
List.at(index)->Message(16, value);
|
List.at(index)->Message(MessageCode::TLightFlasherStartTimedThenStayOff, value);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case 43:
|
case MessageCode::TLightGroupRestartNotifyTimer:
|
||||||
if (NotifyTimer)
|
if (NotifyTimer)
|
||||||
timer::kill(NotifyTimer);
|
timer::kill(NotifyTimer);
|
||||||
NotifyTimer = 0;
|
NotifyTimer = 0;
|
||||||
if (value > 0.0f)
|
if (value > 0.0f)
|
||||||
NotifyTimer = timer::set(value, this, NotifyTimerExpired);
|
NotifyTimer = timer::set(value, this, NotifyTimerExpired);
|
||||||
break;
|
break;
|
||||||
case 44:
|
case MessageCode::TLightGroupFlashWhenOn:
|
||||||
{
|
{
|
||||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto light = *it;
|
auto light = *it;
|
||||||
if (light->BmpIndex1)
|
if (light->LightOnFlag)
|
||||||
{
|
{
|
||||||
light->Message(0, 0.0);
|
light->Message(MessageCode::TLightTurnOff, 0.0);
|
||||||
light->Message(16, value);
|
light->Message(MessageCode::TLightFlasherStartTimedThenStayOff, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 45:
|
case MessageCode::TLightGroupToggleSplitIndex:
|
||||||
{
|
{
|
||||||
control::handler(code, this);
|
control::handler(code, this);
|
||||||
auto index = static_cast<int>(floor(value));
|
auto index = static_cast<int>(floor(value));
|
||||||
|
@ -357,23 +358,23 @@ int TLightGroup::Message(int code, float value)
|
||||||
// Turn off lights (index, end]
|
// Turn off lights (index, end]
|
||||||
for (auto i = count - 1; i > index; i--)
|
for (auto i = count - 1; i > index; i--)
|
||||||
{
|
{
|
||||||
List.at(i)->Message(20, 0.0);
|
List.at(i)->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn on lights [begin, index]
|
// Turn on lights [begin, index]
|
||||||
for (auto i = index; i >= 0; i--)
|
for (auto i = index; i >= 0; i--)
|
||||||
{
|
{
|
||||||
List.at(i)->Message(19, 0.0);
|
List.at(i)->Message(MessageCode::TLightResetAndTurnOn, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 46:
|
case MessageCode::TLightGroupStartFlasher:
|
||||||
{
|
{
|
||||||
auto index = next_light_down();
|
auto index = next_light_down();
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
List.at(index)->Message(4, 0.0);
|
List.at(index)->Message(MessageCode::TLightFlasherStart, 0.0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -395,7 +396,7 @@ void TLightGroup::Reset()
|
||||||
if (NotifyTimer)
|
if (NotifyTimer)
|
||||||
timer::kill(NotifyTimer);
|
timer::kill(NotifyTimer);
|
||||||
NotifyTimer = 0;
|
NotifyTimer = 0;
|
||||||
MessageField2 = 0;
|
MessageField2 = MessageCode::TLightGroupNull;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
Timer1Time = Timer1TimeDefault;
|
Timer1Time = Timer1TimeDefault;
|
||||||
}
|
}
|
||||||
|
@ -407,7 +408,7 @@ void TLightGroup::reschedule_animation(float time)
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
if (time == 0)
|
if (time == 0)
|
||||||
{
|
{
|
||||||
MessageField2 = 0;
|
MessageField2 = MessageCode::TLightGroupNull;
|
||||||
AnimationFlag = 0;
|
AnimationFlag = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -421,10 +422,10 @@ void TLightGroup::start_animation()
|
||||||
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
for (auto it = List.rbegin(); it != List.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto light = *it;
|
auto light = *it;
|
||||||
if (light->BmpIndex1)
|
if (light->LightOnFlag)
|
||||||
light->Message(9, 0.0);
|
light->Message(MessageCode::TLightTurnOnTimed, 0.0);
|
||||||
else
|
else
|
||||||
light->Message(8, 0.0);
|
light->Message(MessageCode::TLightTurnOffTimed, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +433,7 @@ int TLightGroup::next_light_up()
|
||||||
{
|
{
|
||||||
for (auto index = 0u; index < List.size(); ++index)
|
for (auto index = 0u; index < List.size(); ++index)
|
||||||
{
|
{
|
||||||
if (!List[index]->BmpIndex1)
|
if (!List[index]->LightOnFlag)
|
||||||
return static_cast<int>(index);
|
return static_cast<int>(index);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -442,7 +443,7 @@ int TLightGroup::next_light_down()
|
||||||
{
|
{
|
||||||
for (auto index = static_cast<int>(List.size()) - 1; index >= 0; --index)
|
for (auto index = static_cast<int>(List.size()) - 1; index >= 0; --index)
|
||||||
{
|
{
|
||||||
if (List.at(index)->BmpIndex1)
|
if (List.at(index)->LightOnFlag)
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -459,5 +460,5 @@ void TLightGroup::NotifyTimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto group = static_cast<TLightGroup*>(caller);
|
auto group = static_cast<TLightGroup*>(caller);
|
||||||
group->NotifyTimer = 0;
|
group->NotifyTimer = 0;
|
||||||
control::handler(61, group);
|
control::handler(MessageCode::ControlNotifyTimerExpired, group);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ class TLight;
|
||||||
struct TLightGroup_player_backup
|
struct TLightGroup_player_backup
|
||||||
{
|
{
|
||||||
int MessageField;
|
int MessageField;
|
||||||
int MessageField2;
|
MessageCode MessageField2;
|
||||||
float Timer1Time;
|
float Timer1Time;
|
||||||
int Unknown3;
|
int Unknown3;
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@ class TLightGroup :
|
||||||
public:
|
public:
|
||||||
TLightGroup(TPinballTable* table, int groupIndex);
|
TLightGroup(TPinballTable* table, int groupIndex);
|
||||||
~TLightGroup() override = default;
|
~TLightGroup() override = default;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
void reschedule_animation(float time);
|
void reschedule_animation(float time);
|
||||||
void start_animation();
|
void start_animation();
|
||||||
|
@ -32,7 +32,7 @@ public:
|
||||||
std::vector<TLight*> List;
|
std::vector<TLight*> List;
|
||||||
float Timer1Time{};
|
float Timer1Time{};
|
||||||
float Timer1TimeDefault;
|
float Timer1TimeDefault;
|
||||||
int MessageField2{};
|
MessageCode MessageField2{};
|
||||||
int AnimationFlag{};
|
int AnimationFlag{};
|
||||||
int NotifyTimer;
|
int NotifyTimer;
|
||||||
int Timer;
|
int Timer;
|
||||||
|
|
|
@ -13,33 +13,31 @@ TLightRollover::TLightRollover(TPinballTable* table, int groupIndex) : TRollover
|
||||||
{
|
{
|
||||||
RolloverFlag = 0;
|
RolloverFlag = 0;
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
if (ListBitmap != nullptr)
|
SpriteSet(-1);
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
|
||||||
build_walls(groupIndex);
|
build_walls(groupIndex);
|
||||||
FloatArr = *loader::query_float_attribute(groupIndex, 0, 407);
|
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;
|
ActiveFlag = 1;
|
||||||
RolloverFlag = 0;
|
RolloverFlag = 0;
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
if (ListBitmap)
|
SpriteSet(-1);
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
|
||||||
}
|
}
|
||||||
return 0;
|
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)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
|
@ -53,11 +51,10 @@ void TLightRollover::Collision(TBall* ball, vector_type* nextPosition, vector_ty
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, this, "TLightRollover");
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
RolloverFlag = RolloverFlag == 0;
|
RolloverFlag = RolloverFlag == 0;
|
||||||
if (ListBitmap)
|
SpriteSet(0);
|
||||||
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +62,6 @@ void TLightRollover::Collision(TBall* ball, vector_type* nextPosition, vector_ty
|
||||||
void TLightRollover::delay_expired(int timerId, void* caller)
|
void TLightRollover::delay_expired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto roll = static_cast<TLightRollover*>(caller);
|
auto roll = static_cast<TLightRollover*>(caller);
|
||||||
render::sprite_set_bitmap(roll->RenderSprite, nullptr);
|
roll->SpriteSet(-1);
|
||||||
roll->Timer = 0;
|
roll->Timer = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ class TLightRollover :
|
||||||
public:
|
public:
|
||||||
TLightRollover(TPinballTable* table, int groupIndex);
|
TLightRollover(TPinballTable* table, int groupIndex);
|
||||||
~TLightRollover() override = default;
|
~TLightRollover() override = default;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void delay_expired(int timerId, void* caller);
|
static void delay_expired(int timerId, void* caller);
|
||||||
|
|
|
@ -11,48 +11,56 @@ TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collis
|
||||||
Y0 = y0;
|
Y0 = y0;
|
||||||
X1 = x1;
|
X1 = x1;
|
||||||
Y1 = y1;
|
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,
|
TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start,
|
||||||
struct vector_type* end) : TEdgeSegment(collCmp, activeFlag, collisionGroup)
|
const vector2& end) : TEdgeSegment(collCmp, activeFlag, collisionGroup)
|
||||||
{
|
{
|
||||||
X0 = start->X;
|
X0 = start.X;
|
||||||
Y0 = start->Y;
|
Y0 = start.Y;
|
||||||
X1 = end->X;
|
X1 = end.X;
|
||||||
Y1 = end->Y;
|
Y1 = end.Y;
|
||||||
maths::line_init(&Line, X0, Y0, X1, Y1);
|
maths::line_init(Line, X0, Y0, X1, Y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLine::Offset(float offset)
|
void TLine::Offset(float offset)
|
||||||
{
|
{
|
||||||
float offX = offset * Line.PerpendicularL.X;
|
float offX = offset * Line.PerpendicularC.X;
|
||||||
float offY = offset * Line.PerpendicularL.Y;
|
float offY = offset * Line.PerpendicularC.Y;
|
||||||
|
|
||||||
X0 += offX;
|
X0 += offX;
|
||||||
Y0 += offY;
|
Y0 += offY;
|
||||||
X1 += offX;
|
X1 += offX;
|
||||||
Y1 += offY;
|
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(
|
CollisionComponent->Collision(
|
||||||
ball,
|
ball,
|
||||||
&Line.RayIntersect,
|
&Line.RayIntersect,
|
||||||
&Line.PerpendicularL,
|
&Line.PerpendicularC,
|
||||||
coef,
|
distance,
|
||||||
this);
|
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 edgeMan = TTableLayer::edge_manager;
|
||||||
auto xBox0 = edgeMan->box_x(X0);
|
auto xBox0 = edgeMan->box_x(X0);
|
||||||
auto yBox0 = edgeMan->box_y(Y0);
|
auto yBox0 = edgeMan->box_y(Y0);
|
||||||
|
@ -79,123 +87,45 @@ void TLine::place_in_grid()
|
||||||
{
|
{
|
||||||
if (dirY == 1)
|
if (dirY == 1)
|
||||||
{
|
{
|
||||||
if (yBox0 <= yBox1)
|
while (yBox0 <= yBox1)
|
||||||
{
|
|
||||||
do
|
|
||||||
edgeMan->add_edge_to_box(xBox0, yBox0++, this);
|
edgeMan->add_edge_to_box(xBox0, yBox0++, this);
|
||||||
while (yBox0 <= yBox1);
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else if (yBox0 >= yBox1)
|
|
||||||
{
|
{
|
||||||
do
|
while (yBox0 >= yBox1)
|
||||||
edgeMan->add_edge_to_box(xBox0, yBox0--, this);
|
edgeMan->add_edge_to_box(xBox0, yBox0--, this);
|
||||||
while (yBox0 >= yBox1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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);
|
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
|
// Advance indexY when discrete value is ahead/behind
|
||||||
{
|
// Advance indexX when discrete value matches linear value
|
||||||
yCoord = bresIndexY * edgeMan->AdvanceY + edgeMan->Y;
|
indexY += dirY;
|
||||||
xCoord = (bresIndexX * edgeMan->AdvanceX + edgeMan->X) * bresDyDx + bresXAdd;
|
if (ylinear == yDiscrete)
|
||||||
if (xCoord >= yCoord)
|
indexX += dirX;
|
||||||
{
|
|
||||||
if (xCoord == yCoord)
|
|
||||||
{
|
|
||||||
++indexX1;
|
|
||||||
++bresIndexX;
|
|
||||||
}
|
|
||||||
++indexY1;
|
|
||||||
++bresIndexY;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++indexX1;
|
// Advance indexX otherwise
|
||||||
++bresIndexX;
|
indexX += dirX;
|
||||||
}
|
|
||||||
edgeMan->add_edge_to_box(indexX1, indexY1, this);
|
|
||||||
}
|
|
||||||
while (indexX1 != xBox1 || indexY1 != yBox1);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
edgeMan->add_edge_to_box(indexX, indexY, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ public:
|
||||||
line_type Line{};
|
line_type Line{};
|
||||||
float X0, Y0, X1, Y1;
|
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, 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);
|
void Offset(float offset);
|
||||||
float FindCollisionDistance(ray_type* ray) override;
|
float FindCollisionDistance(const ray_type& ray) override;
|
||||||
void EdgeCollision(TBall* ball, float coef) override;
|
void EdgeCollision(TBall* ball, float distance) override;
|
||||||
void place_in_grid() override;
|
void place_in_grid(RectF* aabb) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
TOneway::TOneway(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
TOneway::TOneway(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||||
{
|
{
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
vector_type linePt1{}, linePt2{};
|
vector2 linePt1{}, linePt2{};
|
||||||
|
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
if (visual.FloatArrCount == 2)
|
if (visual.FloatArrCount == 2)
|
||||||
|
@ -21,38 +21,38 @@ TOneway::TOneway(TPinballTable* table, int groupIndex) : TCollisionComponent(tab
|
||||||
linePt1.X = visual.FloatArr[2];
|
linePt1.X = visual.FloatArr[2];
|
||||||
linePt1.Y = visual.FloatArr[3];
|
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)
|
if (line)
|
||||||
{
|
{
|
||||||
line->Offset(table->CollisionCompOffset);
|
line->Offset(table->CollisionCompOffset);
|
||||||
line->place_in_grid();
|
line->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(line);
|
EdgeList.push_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, &linePt1, &linePt2);
|
line = new TLine(this, &ActiveFlag, visual.CollisionGroup, linePt1, linePt2);
|
||||||
Line = line;
|
Line = line;
|
||||||
if (line)
|
if (line)
|
||||||
{
|
{
|
||||||
line->Offset(-table->CollisionCompOffset * 0.8f);
|
line->Offset(-table->CollisionCompOffset * 0.8f);
|
||||||
Line->place_in_grid();
|
Line->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(Line);
|
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)
|
if (edge == Line)
|
||||||
{
|
{
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
if (HardHitSoundId)
|
if (HardHitSoundId)
|
||||||
loader::play_sound(HardHitSoundId);
|
loader::play_sound(HardHitSoundId, ball, "TOneway1");
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (PinballTable->TiltLockFlag)
|
else if (PinballTable->TiltLockFlag)
|
||||||
|
@ -69,17 +69,6 @@ void TOneway::Collision(TBall* ball, vector_type* nextPosition, vector_type* dir
|
||||||
Boost) > 0.2f)
|
Boost) > 0.2f)
|
||||||
{
|
{
|
||||||
if (SoftHitSoundId)
|
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:
|
public:
|
||||||
TOneway(TPinballTable* table, int groupIndex);
|
TOneway(TPinballTable* table, int groupIndex);
|
||||||
~TOneway() override = default;
|
~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;
|
TEdgeSegment* edge) override;
|
||||||
void put_scoring(int index, int score) override;
|
|
||||||
int get_scoring(int index) override;
|
|
||||||
|
|
||||||
TLine* Line;
|
TLine* Line;
|
||||||
int Scores[6]{};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "TPinballComponent.h"
|
#include "TPinballComponent.h"
|
||||||
|
|
||||||
|
#include "control.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "proj.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.h"
|
||||||
|
#include "TTableLayer.h"
|
||||||
|
|
||||||
TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals)
|
TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals)
|
||||||
{
|
{
|
||||||
|
@ -14,64 +18,69 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
|
||||||
PinballTable = table;
|
PinballTable = table;
|
||||||
RenderSprite = nullptr;
|
RenderSprite = nullptr;
|
||||||
ListBitmap = nullptr;
|
ListBitmap = nullptr;
|
||||||
ListZMap = nullptr;
|
|
||||||
GroupName = nullptr;
|
GroupName = nullptr;
|
||||||
Control = nullptr;
|
Control = nullptr;
|
||||||
|
VisualPosNormX= -1.0f;
|
||||||
|
VisualPosNormY = -1.0f;
|
||||||
|
GroupIndex = groupIndex;
|
||||||
if (table)
|
if (table)
|
||||||
table->ComponentList.push_back(this);
|
table->ComponentList.push_back(this);
|
||||||
if (groupIndex >= 0)
|
if (groupIndex >= 0)
|
||||||
GroupName = loader::query_name(groupIndex);
|
GroupName = loader::query_name(groupIndex);
|
||||||
|
|
||||||
if (loadVisuals && groupIndex >= 0)
|
if (loadVisuals && groupIndex >= 0)
|
||||||
{
|
{
|
||||||
int visualCount = loader::query_visual_states(groupIndex);
|
int visualCount = loader::query_visual_states(groupIndex);
|
||||||
for (int index = 0; index < visualCount; ++index)
|
for (int index = 0; index < visualCount; ++index)
|
||||||
{
|
{
|
||||||
loader::query_visual(groupIndex, index, &visual);
|
loader::query_visual(groupIndex, index, &visual);
|
||||||
if (visual.Bitmap)
|
if (visual.Bitmap.Bmp)
|
||||||
{
|
{
|
||||||
|
assertm(visual.Bitmap.ZMap, "Bitmap/zMap pairing is mandatory");
|
||||||
if (!ListBitmap)
|
if (!ListBitmap)
|
||||||
ListBitmap = new std::vector<gdrv_bitmap8*>();
|
ListBitmap = new std::vector<SpriteData>();
|
||||||
if (ListBitmap)
|
|
||||||
ListBitmap->push_back(visual.Bitmap);
|
ListBitmap->push_back(visual.Bitmap);
|
||||||
}
|
}
|
||||||
if (visual.ZMap)
|
|
||||||
{
|
|
||||||
if (!ListZMap)
|
|
||||||
ListZMap = new std::vector<zmap_header_type*>();
|
|
||||||
if (ListZMap)
|
|
||||||
ListZMap->push_back(visual.ZMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto zMap = ListZMap ? ListZMap->at(0) : nullptr;
|
|
||||||
if (ListBitmap)
|
if (ListBitmap)
|
||||||
{
|
{
|
||||||
rectangle_type bmp1Rect{}, tmpRect{};
|
rectangle_type bmp1Rect{}, tmpRect{};
|
||||||
auto rootBmp = ListBitmap->at(0);
|
const auto rootSprite = ListBitmap->at(0);
|
||||||
|
const auto rootBmp = rootSprite.Bmp;
|
||||||
|
|
||||||
bmp1Rect.XPosition = rootBmp->XPosition - table->XOffset;
|
bmp1Rect.XPosition = rootBmp->XPosition - table->XOffset;
|
||||||
bmp1Rect.YPosition = rootBmp->YPosition - table->YOffset;
|
bmp1Rect.YPosition = rootBmp->YPosition - table->YOffset;
|
||||||
bmp1Rect.Width = rootBmp->Width;
|
bmp1Rect.Width = rootBmp->Width;
|
||||||
bmp1Rect.Height = rootBmp->Height;
|
bmp1Rect.Height = rootBmp->Height;
|
||||||
|
|
||||||
for (auto index = 1u; index < ListBitmap->size(); index++)
|
for (auto index = 1u; index < ListBitmap->size(); index++)
|
||||||
{
|
{
|
||||||
auto bmp = ListBitmap->at(index);
|
auto bmp = ListBitmap->at(index).Bmp;
|
||||||
tmpRect.XPosition = bmp->XPosition - table->XOffset;
|
tmpRect.XPosition = bmp->XPosition - table->XOffset;
|
||||||
tmpRect.YPosition = bmp->YPosition - table->YOffset;
|
tmpRect.YPosition = bmp->YPosition - table->YOffset;
|
||||||
tmpRect.Width = bmp->Width;
|
tmpRect.Width = bmp->Width;
|
||||||
tmpRect.Height = bmp->Height;
|
tmpRect.Height = bmp->Height;
|
||||||
maths::enclosing_box(&bmp1Rect, &tmpRect, &bmp1Rect);
|
maths::enclosing_box(bmp1Rect, tmpRect, bmp1Rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderSprite = render::create_sprite(
|
RenderSprite = new render_sprite(
|
||||||
visualCount > 0 ? VisualTypes::Sprite : VisualTypes::None,
|
VisualTypes::Sprite,
|
||||||
rootBmp,
|
rootBmp,
|
||||||
zMap,
|
rootSprite.ZMap,
|
||||||
rootBmp->XPosition - table->XOffset,
|
rootBmp->XPosition - table->XOffset,
|
||||||
rootBmp->YPosition - table->YOffset,
|
rootBmp->YPosition - table->YOffset,
|
||||||
&bmp1Rect);
|
&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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,14 +96,13 @@ TPinballComponent::~TPinballComponent()
|
||||||
}
|
}
|
||||||
|
|
||||||
delete ListBitmap;
|
delete ListBitmap;
|
||||||
delete ListZMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int TPinballComponent::Message(int code, float value)
|
int TPinballComponent::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
MessageField = code;
|
MessageField = static_cast<int>(code);
|
||||||
if (code == 1024)
|
if (code == MessageCode::Reset)
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -103,11 +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::SpriteSet(int index) const
|
||||||
|
{
|
||||||
|
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::SpriteSetBall(int index, vector2i pos, float depth) const
|
||||||
|
{
|
||||||
|
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,17 +1,134 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
struct SpriteData;
|
||||||
|
struct vector2i;
|
||||||
struct zmap_header_type;
|
struct zmap_header_type;
|
||||||
struct gdrv_bitmap8;
|
struct gdrv_bitmap8;
|
||||||
struct render_sprite_type_struct;
|
struct render_sprite;
|
||||||
struct component_control;
|
struct component_control;
|
||||||
|
struct vector2;
|
||||||
class TPinballTable;
|
class TPinballTable;
|
||||||
|
|
||||||
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,
|
Reset = 1024,
|
||||||
LightActiveCount = 37,
|
|
||||||
LightTotalCount = 38,
|
|
||||||
LightSetMessageField = 23,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TPinballComponent
|
class TPinballComponent
|
||||||
|
@ -19,10 +136,12 @@ class TPinballComponent
|
||||||
public:
|
public:
|
||||||
TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals);
|
TPinballComponent(TPinballTable* table, int groupIndex, bool loadVisuals);
|
||||||
virtual ~TPinballComponent();
|
virtual ~TPinballComponent();
|
||||||
virtual int Message(int code, float value);
|
virtual int Message(MessageCode code, float value);
|
||||||
virtual void port_draw();
|
virtual void port_draw();
|
||||||
virtual void put_scoring(int index, int score);
|
int get_scoring(unsigned int index) const;
|
||||||
virtual int get_scoring(int index);
|
virtual vector2 get_coordinates();
|
||||||
|
void SpriteSet(int index) const;
|
||||||
|
void SpriteSetBall(int index, vector2i pos, float depth) const;
|
||||||
|
|
||||||
char UnusedBaseFlag;
|
char UnusedBaseFlag;
|
||||||
char ActiveFlag;
|
char ActiveFlag;
|
||||||
|
@ -30,8 +149,10 @@ public:
|
||||||
char* GroupName;
|
char* GroupName;
|
||||||
component_control* Control;
|
component_control* Control;
|
||||||
int GroupIndex;
|
int GroupIndex;
|
||||||
render_sprite_type_struct* RenderSprite;
|
render_sprite* RenderSprite;
|
||||||
TPinballTable* PinballTable;
|
TPinballTable* PinballTable;
|
||||||
std::vector<gdrv_bitmap8*>* ListBitmap;
|
std::vector<SpriteData>* ListBitmap;
|
||||||
std::vector<zmap_header_type*>* ListZMap;
|
private:
|
||||||
|
float VisualPosNormX;
|
||||||
|
float VisualPosNormY;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "midi.h"
|
||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "pinball.h"
|
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "TBall.h"
|
#include "TBall.h"
|
||||||
#include "TBlocker.h"
|
#include "TBlocker.h"
|
||||||
|
@ -37,6 +37,7 @@
|
||||||
#include "TPlunger.h"
|
#include "TPlunger.h"
|
||||||
#include "TFlipper.h"
|
#include "TFlipper.h"
|
||||||
#include "TDrain.h"
|
#include "TDrain.h"
|
||||||
|
#include "translations.h"
|
||||||
|
|
||||||
int TPinballTable::score_multipliers[5] = {1, 2, 3, 5, 10};
|
int TPinballTable::score_multipliers[5] = {1, 2, 3, 5, 10};
|
||||||
|
|
||||||
|
@ -48,20 +49,19 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false)
|
||||||
CurScoreStruct = nullptr;
|
CurScoreStruct = nullptr;
|
||||||
ScoreBallcount = nullptr;
|
ScoreBallcount = nullptr;
|
||||||
ScorePlayerNumber1 = nullptr;
|
ScorePlayerNumber1 = nullptr;
|
||||||
BallInSink = 0;
|
BallInDrainFlag = 0;
|
||||||
ActiveFlag = 1;
|
ActiveFlag = 1;
|
||||||
TiltLockFlag = 0;
|
TiltLockFlag = 0;
|
||||||
EndGameTimeoutTimer = 0;
|
EndGameTimeoutTimer = 0;
|
||||||
LightShowTimer = 0;
|
LightShowTimer = 0;
|
||||||
ReplayTimer = 0;
|
ReplayTimer = 0;
|
||||||
TiltTimeoutTimer = 0;
|
TiltTimeoutTimer = 0;
|
||||||
MultiballFlag = 0;
|
MultiballFlag = false;
|
||||||
PlayerCount = 0;
|
PlayerCount = 0;
|
||||||
|
|
||||||
auto ballObj = new TBall(this);
|
auto ball = AddBall({0.0f, 0.0f});
|
||||||
BallList.push_back(ballObj);
|
ball->Disable();
|
||||||
if (ballObj)
|
|
||||||
ballObj->ActiveFlag = 0;
|
|
||||||
new TTableLayer(this);
|
new TTableLayer(this);
|
||||||
LightGroup = new TLightGroup(this, 0);
|
LightGroup = new TLightGroup(this, 0);
|
||||||
|
|
||||||
|
@ -185,8 +185,8 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
render::build_occlude_list();
|
render::build_occlude_list();
|
||||||
pinball::InfoTextBox = dynamic_cast<TTextBox*>(find_component("info_text_box"));
|
pb::InfoTextBox = dynamic_cast<TTextBox*>(find_component("info_text_box"));
|
||||||
pinball::MissTextBox = dynamic_cast<TTextBox*>(find_component("mission_text_box"));
|
pb::MissTextBox = dynamic_cast<TTextBox*>(find_component("mission_text_box"));
|
||||||
control::make_links(this);
|
control::make_links(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +214,7 @@ TPinballTable::~TPinballTable()
|
||||||
delete ComponentList[0];
|
delete ComponentList[0];
|
||||||
}
|
}
|
||||||
control::ClearLinks();
|
control::ClearLinks();
|
||||||
|
pb::InfoTextBox = pb::MissTextBox = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TPinballComponent* TPinballTable::find_component(LPCSTR componentName)
|
TPinballComponent* TPinballTable::find_component(LPCSTR componentName)
|
||||||
|
@ -227,7 +228,7 @@ TPinballComponent* TPinballTable::find_component(LPCSTR componentName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find:", componentName, nullptr);
|
pb::ShowMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find:", componentName);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,23 +242,24 @@ TPinballComponent* TPinballTable::find_component(int groupIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(Buffer, sizeof Buffer, "%d", groupIndex);
|
snprintf(Buffer, sizeof Buffer, "%d", groupIndex);
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find (lh):", Buffer, nullptr);
|
pb::ShowMessageBox(SDL_MESSAGEBOX_WARNING, "Table cant find (lh):", Buffer);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPinballTable::AddScore(int score)
|
int TPinballTable::AddScore(int score)
|
||||||
{
|
{
|
||||||
if (ScoreSpecial3Flag)
|
if (JackpotScoreFlag)
|
||||||
{
|
{
|
||||||
ScoreSpecial3 += score;
|
JackpotScore += score;
|
||||||
if (ScoreSpecial3 > 5000000)
|
const auto jackpotLimit = !pb::FullTiltMode ? 5000000 : 10000000;
|
||||||
ScoreSpecial3 = 5000000;
|
if (JackpotScore > jackpotLimit)
|
||||||
|
JackpotScore = jackpotLimit;
|
||||||
}
|
}
|
||||||
if (ScoreSpecial2Flag)
|
if (BonusScoreFlag)
|
||||||
{
|
{
|
||||||
ScoreSpecial2 += score;
|
BonusScore += score;
|
||||||
if (ScoreSpecial2 > 5000000)
|
if (BonusScore > 5000000)
|
||||||
ScoreSpecial2 = 5000000;
|
BonusScore = 5000000;
|
||||||
}
|
}
|
||||||
int addedScore = ScoreAdded + score * score_multipliers[ScoreMultiplier];
|
int addedScore = ScoreAdded + score * score_multipliers[ScoreMultiplier];
|
||||||
CurScore += addedScore;
|
CurScore += addedScore;
|
||||||
|
@ -286,21 +288,21 @@ void TPinballTable::ChangeBallCount(int count)
|
||||||
|
|
||||||
void TPinballTable::tilt(float time)
|
void TPinballTable::tilt(float time)
|
||||||
{
|
{
|
||||||
if (!TiltLockFlag && !BallInSink)
|
if (!TiltLockFlag && !BallInDrainFlag)
|
||||||
{
|
{
|
||||||
pinball::InfoTextBox->Clear();
|
pb::InfoTextBox->Clear();
|
||||||
pinball::MissTextBox->Clear();
|
pb::MissTextBox->Clear();
|
||||||
pinball::InfoTextBox->Display(pinball::get_rc_string(35, 0), -1.0);
|
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING136), -1.0);
|
||||||
loader::play_sound(SoundIndex3);
|
loader::play_sound(SoundIndex3, nullptr, "TPinballTable1");
|
||||||
TiltTimeoutTimer = timer::set(30.0, this, tilt_timeout);
|
TiltTimeoutTimer = timer::set(30.0, this, tilt_timeout);
|
||||||
|
|
||||||
for (auto component : ComponentList)
|
for (auto component : ComponentList)
|
||||||
{
|
{
|
||||||
component->Message(1011, time);
|
component->Message(MessageCode::SetTiltLock, time);
|
||||||
}
|
}
|
||||||
LightGroup->Message(8, 0);
|
LightGroup->Message(MessageCode::TLightTurnOffTimed, 0);
|
||||||
TiltLockFlag = 1;
|
TiltLockFlag = 1;
|
||||||
control::table_control_handler(1011);
|
control::table_control_handler(MessageCode::SetTiltLock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,49 +315,50 @@ void TPinballTable::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)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1000:
|
case MessageCode::LeftFlipperInputPressed:
|
||||||
if (!TiltLockFlag)
|
if (!TiltLockFlag)
|
||||||
{
|
{
|
||||||
FlipperL->Message(1, value);
|
FlipperL->Message(MessageCode::TFlipperExtend, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1001:
|
case MessageCode::LeftFlipperInputReleased:
|
||||||
if (!TiltLockFlag)
|
if (!TiltLockFlag)
|
||||||
{
|
{
|
||||||
FlipperL->Message(2, value);
|
FlipperL->Message(MessageCode::TFlipperRetract, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1002:
|
case MessageCode::RightFlipperInputPressed:
|
||||||
if (!TiltLockFlag)
|
if (!TiltLockFlag)
|
||||||
{
|
{
|
||||||
FlipperR->Message(1, value);
|
FlipperR->Message(MessageCode::TFlipperExtend, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1003:
|
case MessageCode::RightFlipperInputReleased:
|
||||||
if (!TiltLockFlag)
|
if (!TiltLockFlag)
|
||||||
{
|
{
|
||||||
FlipperR->Message(2, value);
|
FlipperR->Message(MessageCode::TFlipperRetract, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1004:
|
case MessageCode::PlungerInputPressed:
|
||||||
case 1005:
|
case MessageCode::PlungerInputReleased:
|
||||||
Plunger->Message(code, value);
|
Plunger->Message(code, value);
|
||||||
break;
|
break;
|
||||||
case 1008:
|
case MessageCode::Pause:
|
||||||
case 1009:
|
case MessageCode::Resume:
|
||||||
case 1010:
|
case MessageCode::LooseFocus:
|
||||||
for (auto component : ComponentList)
|
for (auto component : ComponentList)
|
||||||
{
|
{
|
||||||
component->Message(code, value);
|
component->Message(code, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1012:
|
case MessageCode::ClearTiltLock:
|
||||||
LightGroup->Message(14, 0.0);
|
LightGroup->Message(MessageCode::TLightResetTimed, 0.0);
|
||||||
if (TiltLockFlag)
|
if (TiltLockFlag)
|
||||||
{
|
{
|
||||||
TiltLockFlag = 0;
|
TiltLockFlag = 0;
|
||||||
|
@ -364,35 +367,37 @@ int TPinballTable::Message(int code, float value)
|
||||||
TiltTimeoutTimer = 0;
|
TiltTimeoutTimer = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1013:
|
case MessageCode::StartGamePlayer1:
|
||||||
LightGroup->Message(34, 0.0);
|
LightGroup->Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
LightGroup->Message(20, 0.0);
|
LightGroup->Message(MessageCode::TLightResetAndTurnOff, 0.0);
|
||||||
Plunger->Message(1016, 0.0);
|
Plunger->Message(MessageCode::PlungerStartFeedTimer, 0.0);
|
||||||
if (Demo && Demo->ActiveFlag)
|
if (Demo && Demo->ActiveFlag)
|
||||||
rc_text = pinball::get_rc_string(30, 0);
|
rc_text = pb::get_rc_string(Msg::STRING131);
|
||||||
else
|
else
|
||||||
rc_text = pinball::get_rc_string(26, 0);
|
rc_text = pb::get_rc_string(Msg::STRING127);
|
||||||
pinball::InfoTextBox->Display(rc_text, -1.0);
|
pb::InfoTextBox->Display(rc_text, -1.0);
|
||||||
if (Demo)
|
if (Demo)
|
||||||
Demo->Message(1014, 0.0);
|
Demo->Message(MessageCode::NewGame, 0.0);
|
||||||
break;
|
break;
|
||||||
case 1014:
|
case MessageCode::NewGame:
|
||||||
if (EndGameTimeoutTimer)
|
if (EndGameTimeoutTimer)
|
||||||
{
|
{
|
||||||
timer::kill(EndGameTimeoutTimer);
|
timer::kill(EndGameTimeoutTimer);
|
||||||
EndGame_timeout(0, this);
|
EndGame_timeout(0, this);
|
||||||
pb::mode_change(1);
|
pb::mode_change(GameModes::InGame);
|
||||||
}
|
}
|
||||||
if (LightShowTimer)
|
if (LightShowTimer)
|
||||||
{
|
{
|
||||||
timer::kill(LightShowTimer);
|
timer::kill(LightShowTimer);
|
||||||
LightShowTimer = 0;
|
LightShowTimer = 0;
|
||||||
Message(1013, 0.0);
|
Message(MessageCode::StartGamePlayer1, 0.0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Some of the control cheats persist across games.
|
||||||
|
// Was this loose anti-cheat by design?
|
||||||
CheatsUsed = 0;
|
CheatsUsed = 0;
|
||||||
Message(1024, 0.0);
|
Message(MessageCode::Reset, 0.0);
|
||||||
auto ball = BallList[0];
|
auto ball = BallList[0];
|
||||||
ball->Position.Y = 0.0;
|
ball->Position.Y = 0.0;
|
||||||
ball->Position.X = 0.0;
|
ball->Position.X = 0.0;
|
||||||
|
@ -426,7 +431,7 @@ int TPinballTable::Message(int code, float value)
|
||||||
scorePtr->BallCount = MaxBallCount;
|
scorePtr->BallCount = MaxBallCount;
|
||||||
scorePtr->ExtraBalls = ExtraBalls;
|
scorePtr->ExtraBalls = ExtraBalls;
|
||||||
scorePtr->BallLockedCounter = BallLockedCounter;
|
scorePtr->BallLockedCounter = BallLockedCounter;
|
||||||
scorePtr->Unknown2 = ScoreSpecial3;
|
scorePtr->JackpotScore = JackpotScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
BallCount = MaxBallCount;
|
BallCount = MaxBallCount;
|
||||||
|
@ -439,32 +444,42 @@ int TPinballTable::Message(int code, float value)
|
||||||
score::set(PlayerScores[scoreIndex].ScoreStruct, -1);
|
score::set(PlayerScores[scoreIndex].ScoreStruct, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScoreSpecial3Flag = 0;
|
JackpotScoreFlag = false;
|
||||||
ScoreSpecial2Flag = 0;
|
BonusScoreFlag = false;
|
||||||
UnknownP71 = 0;
|
UnknownP71 = 0;
|
||||||
pinball::InfoTextBox->Clear();
|
pb::InfoTextBox->Clear();
|
||||||
pinball::MissTextBox->Clear();
|
pb::MissTextBox->Clear();
|
||||||
LightGroup->Message(28, 0.2f);
|
LightGroup->Message(MessageCode::TLightGroupLightShowAnimation, 0.2f);
|
||||||
auto time = loader::play_sound(SoundIndex1);
|
auto time = loader::play_sound(SoundIndex1, nullptr, "TPinballTable2");
|
||||||
|
if (time < 0)
|
||||||
|
time = 5.0f;
|
||||||
LightShowTimer = timer::set(time, this, LightShow_timeout);
|
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;
|
break;
|
||||||
case 1018:
|
case MessageCode::PlungerRelaunchBall:
|
||||||
if (ReplayTimer)
|
if (ReplayTimer)
|
||||||
timer::kill(ReplayTimer);
|
timer::kill(ReplayTimer);
|
||||||
ReplayTimer = timer::set(floor(value), this, replay_timer_callback);
|
ReplayTimer = timer::set(floor(value), this, replay_timer_callback);
|
||||||
ReplayActiveFlag = 1;
|
ReplayActiveFlag = 1;
|
||||||
break;
|
break;
|
||||||
case 1021:
|
case MessageCode::SwitchToNextPlayer:
|
||||||
{
|
{
|
||||||
if (PlayerCount <= 1)
|
if (PlayerCount <= 1)
|
||||||
{
|
{
|
||||||
char* textboxText;
|
const char* textboxText;
|
||||||
if (Demo->ActiveFlag)
|
if (Demo->ActiveFlag)
|
||||||
textboxText = pinball::get_rc_string(30, 0);
|
textboxText = pb::get_rc_string(Msg::STRING131);
|
||||||
else
|
else
|
||||||
textboxText = pinball::get_rc_string(26, 0);
|
textboxText = pb::get_rc_string(Msg::STRING127);
|
||||||
pinball::InfoTextBox->Display(textboxText, -1.0);
|
pb::InfoTextBox->Display(textboxText, -1.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,14 +493,14 @@ int TPinballTable::Message(int code, float value)
|
||||||
PlayerScores[CurrentPlayer].BallCount = BallCount;
|
PlayerScores[CurrentPlayer].BallCount = BallCount;
|
||||||
PlayerScores[CurrentPlayer].ExtraBalls = ExtraBalls;
|
PlayerScores[CurrentPlayer].ExtraBalls = ExtraBalls;
|
||||||
PlayerScores[CurrentPlayer].BallLockedCounter = BallLockedCounter;
|
PlayerScores[CurrentPlayer].BallLockedCounter = BallLockedCounter;
|
||||||
PlayerScores[CurrentPlayer].Unknown2 = ScoreSpecial3;
|
PlayerScores[CurrentPlayer].JackpotScore = JackpotScore;
|
||||||
|
|
||||||
CurScore = nextScorePtr->Score;
|
CurScore = nextScorePtr->Score;
|
||||||
CurScoreE9 = nextScorePtr->ScoreE9Part;
|
CurScoreE9 = nextScorePtr->ScoreE9Part;
|
||||||
BallCount = nextScorePtr->BallCount;
|
BallCount = nextScorePtr->BallCount;
|
||||||
ExtraBalls = nextScorePtr->ExtraBalls;
|
ExtraBalls = nextScorePtr->ExtraBalls;
|
||||||
BallLockedCounter = nextScorePtr->BallLockedCounter;
|
BallLockedCounter = nextScorePtr->BallLockedCounter;
|
||||||
ScoreSpecial3 = nextScorePtr->Unknown2;
|
JackpotScore = nextScorePtr->JackpotScore;
|
||||||
|
|
||||||
CurScoreStruct = nextScorePtr->ScoreStruct;
|
CurScoreStruct = nextScorePtr->ScoreStruct;
|
||||||
score::set(CurScoreStruct, CurScore);
|
score::set(CurScoreStruct, CurScore);
|
||||||
|
@ -497,58 +512,58 @@ int TPinballTable::Message(int code, float value)
|
||||||
|
|
||||||
for (auto component : ComponentList)
|
for (auto component : ComponentList)
|
||||||
{
|
{
|
||||||
component->Message(1020, static_cast<float>(nextPlayer));
|
component->Message(MessageCode::PlayerChanged, static_cast<float>(nextPlayer));
|
||||||
}
|
}
|
||||||
|
|
||||||
char* textboxText = nullptr;
|
const char* textboxText = nullptr;
|
||||||
switch (nextPlayer)
|
switch (nextPlayer)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if (Demo->ActiveFlag)
|
if (Demo->ActiveFlag)
|
||||||
textboxText = pinball::get_rc_string(30, 0);
|
textboxText = pb::get_rc_string(Msg::STRING131);
|
||||||
else
|
else
|
||||||
textboxText = pinball::get_rc_string(26, 0);
|
textboxText = pb::get_rc_string(Msg::STRING127);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (Demo->ActiveFlag)
|
if (Demo->ActiveFlag)
|
||||||
textboxText = pinball::get_rc_string(31, 0);
|
textboxText = pb::get_rc_string(Msg::STRING132);
|
||||||
else
|
else
|
||||||
textboxText = pinball::get_rc_string(27, 0);
|
textboxText = pb::get_rc_string(Msg::STRING128);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (Demo->ActiveFlag)
|
if (Demo->ActiveFlag)
|
||||||
textboxText = pinball::get_rc_string(32, 0);
|
textboxText = pb::get_rc_string(Msg::STRING133);
|
||||||
else
|
else
|
||||||
textboxText = pinball::get_rc_string(28, 0);
|
textboxText = pb::get_rc_string(Msg::STRING129);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (Demo->ActiveFlag)
|
if (Demo->ActiveFlag)
|
||||||
textboxText = pinball::get_rc_string(33, 0);
|
textboxText = pb::get_rc_string(Msg::STRING134);
|
||||||
else
|
else
|
||||||
textboxText = pinball::get_rc_string(29, 0);
|
textboxText = pb::get_rc_string(Msg::STRING130);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textboxText != nullptr)
|
if (textboxText != nullptr)
|
||||||
pinball::InfoTextBox->Display(textboxText, -1);
|
pb::InfoTextBox->Display(textboxText, -1);
|
||||||
ScoreSpecial3Flag = 0;
|
JackpotScoreFlag = false;
|
||||||
ScoreSpecial2Flag = 0;
|
BonusScoreFlag = false;
|
||||||
UnknownP71 = 0;
|
UnknownP71 = 0;
|
||||||
CurrentPlayer = nextPlayer;
|
CurrentPlayer = nextPlayer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1022:
|
case MessageCode::GameOver:
|
||||||
loader::play_sound(SoundIndex2);
|
loader::play_sound(SoundIndex2, nullptr, "TPinballTable3");
|
||||||
pinball::MissTextBox->Clear();
|
pb::MissTextBox->Clear();
|
||||||
pinball::InfoTextBox->Display(pinball::get_rc_string(34, 0), -1.0);
|
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING135), -1.0);
|
||||||
EndGameTimeoutTimer = timer::set(3.0, this, EndGame_timeout);
|
EndGameTimeoutTimer = timer::set(3.0, this, EndGame_timeout);
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
for (auto component : ComponentList)
|
for (auto component : ComponentList)
|
||||||
{
|
{
|
||||||
component->Message(1024, 0);
|
component->Message(MessageCode::Reset, 0);
|
||||||
}
|
}
|
||||||
if (ReplayTimer)
|
if (ReplayTimer)
|
||||||
timer::kill(ReplayTimer);
|
timer::kill(ReplayTimer);
|
||||||
|
@ -556,21 +571,21 @@ int TPinballTable::Message(int code, float value)
|
||||||
if (LightShowTimer)
|
if (LightShowTimer)
|
||||||
{
|
{
|
||||||
timer::kill(LightShowTimer);
|
timer::kill(LightShowTimer);
|
||||||
LightGroup->Message(34, 0.0);
|
LightGroup->Message(MessageCode::TLightGroupReset, 0.0);
|
||||||
}
|
}
|
||||||
LightShowTimer = 0;
|
LightShowTimer = 0;
|
||||||
ScoreMultiplier = 0;
|
ScoreMultiplier = 0;
|
||||||
ScoreAdded = 0;
|
ScoreAdded = 0;
|
||||||
ScoreSpecial1 = 0;
|
ReflexShotScore = 0;
|
||||||
ScoreSpecial2 = 10000;
|
BonusScore = 10000;
|
||||||
ScoreSpecial2Flag = 0;
|
BonusScoreFlag = false;
|
||||||
ScoreSpecial3 = 20000;
|
JackpotScore = 20000;
|
||||||
ScoreSpecial3Flag = 0;
|
JackpotScoreFlag = false;
|
||||||
UnknownP71 = 0;
|
UnknownP71 = 0;
|
||||||
ExtraBalls = 0;
|
ExtraBalls = 0;
|
||||||
UnknownP75 = 0;
|
MultiballCount = 0;
|
||||||
BallLockedCounter = 0;
|
BallLockedCounter = 0;
|
||||||
MultiballFlag = 0;
|
MultiballFlag = false;
|
||||||
UnknownP78 = 0;
|
UnknownP78 = 0;
|
||||||
ReplayActiveFlag = 0;
|
ReplayActiveFlag = 0;
|
||||||
ReplayTimer = 0;
|
ReplayTimer = 0;
|
||||||
|
@ -584,6 +599,75 @@ int TPinballTable::Message(int code, float value)
|
||||||
return 0;
|
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)
|
void TPinballTable::EndGame_timeout(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto table = static_cast<TPinballTable*>(caller);
|
auto table = static_cast<TPinballTable*>(caller);
|
||||||
|
@ -592,19 +676,19 @@ void TPinballTable::EndGame_timeout(int timerId, void* caller)
|
||||||
|
|
||||||
for (auto component : table->ComponentList)
|
for (auto component : table->ComponentList)
|
||||||
{
|
{
|
||||||
component->Message(1022, 0);
|
component->Message(MessageCode::GameOver, 0);
|
||||||
}
|
}
|
||||||
if (table->Demo)
|
if (table->Demo)
|
||||||
table->Demo->Message(1022, 0.0);
|
table->Demo->Message(MessageCode::GameOver, 0.0);
|
||||||
control::handler(67, pinball::MissTextBox);
|
control::handler(MessageCode::ControlMissionStarted, pb::MissTextBox);
|
||||||
pinball::InfoTextBox->Display(pinball::get_rc_string(24, 0), -1.0);
|
pb::InfoTextBox->Display(pb::get_rc_string(Msg::STRING125), -1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPinballTable::LightShow_timeout(int timerId, void* caller)
|
void TPinballTable::LightShow_timeout(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto table = static_cast<TPinballTable*>(caller);
|
auto table = static_cast<TPinballTable*>(caller);
|
||||||
table->LightShowTimer = 0;
|
table->LightShowTimer = 0;
|
||||||
table->Message(1013, 0.0);
|
table->Message(MessageCode::StartGamePlayer1, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPinballTable::replay_timer_callback(int timerId, void* caller)
|
void TPinballTable::replay_timer_callback(int timerId, void* caller)
|
||||||
|
@ -617,7 +701,7 @@ void TPinballTable::replay_timer_callback(int timerId, void* caller)
|
||||||
void TPinballTable::tilt_timeout(int timerId, void* caller)
|
void TPinballTable::tilt_timeout(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto table = static_cast<TPinballTable*>(caller);
|
auto table = static_cast<TPinballTable*>(caller);
|
||||||
vector_type vec{};
|
vector2 vec{};
|
||||||
|
|
||||||
table->TiltTimeoutTimer = 0;
|
table->TiltTimeoutTimer = 0;
|
||||||
if (table->TiltLockFlag)
|
if (table->TiltLockFlag)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "maths.h"
|
||||||
#include "TPinballComponent.h"
|
#include "TPinballComponent.h"
|
||||||
|
|
||||||
class TBall;
|
class TBall;
|
||||||
|
@ -15,7 +16,7 @@ struct score_struct_super
|
||||||
scoreStruct* ScoreStruct;
|
scoreStruct* ScoreStruct;
|
||||||
int Score;
|
int Score;
|
||||||
int ScoreE9Part;
|
int ScoreE9Part;
|
||||||
int Unknown2;
|
int JackpotScore;
|
||||||
int BallCount;
|
int BallCount;
|
||||||
int ExtraBalls;
|
int ExtraBalls;
|
||||||
int BallLockedCounter;
|
int BallLockedCounter;
|
||||||
|
@ -33,7 +34,10 @@ public:
|
||||||
void ChangeBallCount(int count);
|
void ChangeBallCount(int count);
|
||||||
void tilt(float time);
|
void tilt(float time);
|
||||||
void port_draw() override;
|
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 EndGame_timeout(int timerId, void* caller);
|
||||||
static void LightShow_timeout(int timerId, void* caller);
|
static void LightShow_timeout(int timerId, void* caller);
|
||||||
|
@ -49,7 +53,7 @@ public:
|
||||||
int SoundIndex1{};
|
int SoundIndex1{};
|
||||||
int SoundIndex2{};
|
int SoundIndex2{};
|
||||||
int SoundIndex3{};
|
int SoundIndex3{};
|
||||||
int BallInSink;
|
int BallInDrainFlag;
|
||||||
int CurScore{};
|
int CurScore{};
|
||||||
int CurScoreE9{};
|
int CurScoreE9{};
|
||||||
int LightShowTimer;
|
int LightShowTimer;
|
||||||
|
@ -67,27 +71,27 @@ public:
|
||||||
int Height{};
|
int Height{};
|
||||||
std::vector<TPinballComponent*> ComponentList;
|
std::vector<TPinballComponent*> ComponentList;
|
||||||
std::vector<TBall*> BallList;
|
std::vector<TBall*> BallList;
|
||||||
|
std::vector<TFlipper*> FlipperList;
|
||||||
TLightGroup* LightGroup;
|
TLightGroup* LightGroup;
|
||||||
float GravityDirVectMult{};
|
float GravityDirVectMult{};
|
||||||
float GravityAngleX{};
|
float GravityAngleX{};
|
||||||
float GravityAnglY{};
|
float GravityAnglY{};
|
||||||
float CollisionCompOffset{};
|
float CollisionCompOffset{};
|
||||||
float PlungerPositionX{};
|
vector2 PlungerPosition{};
|
||||||
float PlungerPositionY{};
|
|
||||||
int ScoreMultiplier{};
|
int ScoreMultiplier{};
|
||||||
int ScoreAdded{};
|
int ScoreAdded{};
|
||||||
int ScoreSpecial1{};
|
int ReflexShotScore{};
|
||||||
int ScoreSpecial2{};
|
int BonusScore{};
|
||||||
int ScoreSpecial2Flag{};
|
bool BonusScoreFlag{};
|
||||||
int ScoreSpecial3{};
|
int JackpotScore{};
|
||||||
int ScoreSpecial3Flag{};
|
bool JackpotScoreFlag{};
|
||||||
int UnknownP71{};
|
int UnknownP71{};
|
||||||
int BallCount{};
|
int BallCount{};
|
||||||
int MaxBallCount;
|
int MaxBallCount;
|
||||||
int ExtraBalls{};
|
int ExtraBalls{};
|
||||||
int UnknownP75{};
|
int MultiballCount{};
|
||||||
int BallLockedCounter{};
|
int BallLockedCounter{};
|
||||||
int MultiballFlag;
|
bool MultiballFlag;
|
||||||
int UnknownP78{};
|
int UnknownP78{};
|
||||||
int ReplayActiveFlag{};
|
int ReplayActiveFlag{};
|
||||||
int ReplayTimer;
|
int ReplayTimer;
|
||||||
|
|
|
@ -23,131 +23,175 @@ TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(t
|
||||||
SoundIndexP2 = visual.SoundIndex3;
|
SoundIndexP2 = visual.SoundIndex3;
|
||||||
HardHitSoundId = visual.Kicker.HardHitSoundId;
|
HardHitSoundId = visual.Kicker.HardHitSoundId;
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1000000000.0;
|
||||||
|
|
||||||
|
// FT:MaxPullback = 50; 3DPB: MaxPullback = 100, PullbackIncrement is floored.
|
||||||
|
if (pb::FullTiltMode)
|
||||||
|
{
|
||||||
|
MaxPullback = 50;
|
||||||
|
PullbackIncrement = MaxPullback / (ListBitmap->size() * 8.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
MaxPullback = 100;
|
MaxPullback = 100;
|
||||||
|
PullbackIncrement = std::floor(MaxPullback / (ListBitmap->size() * 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
Elasticity = 0.5f;
|
Elasticity = 0.5f;
|
||||||
Smoothness = 0.5f;
|
Smoothness = 0.5f;
|
||||||
PullbackIncrement = static_cast<int>(100.0 / (ListBitmap->size() * 8.0));
|
|
||||||
Unknown4F = 0.025f;
|
PullbackDelay = 0.025f;
|
||||||
float* floatArr = loader::query_float_attribute(groupIndex, 0, 601);
|
float* floatArr = loader::query_float_attribute(groupIndex, 0, 601);
|
||||||
table->PlungerPositionX = floatArr[0];
|
table->PlungerPosition = {floatArr[0], floatArr[1]};
|
||||||
table->PlungerPositionY = 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)
|
if (PinballTable->TiltLockFlag || SomeCounter > 0)
|
||||||
Message(1017, 0.0);
|
{
|
||||||
coef = RandFloat() * Boost * 0.1f + Boost; // it is intended that the passed in coef is never used!
|
auto boost = RandFloat() * MaxPullback * 0.1f + MaxPullback;
|
||||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, coef);
|
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)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 1004:
|
case MessageCode::PlungerInputPressed:
|
||||||
if (!PullbackTimer_)
|
if (!PullbackStartedFlag && (!pb::FullTiltMode || PinballTable->MultiballCount > 0 && !PinballTable->
|
||||||
|
TiltLockFlag))
|
||||||
{
|
{
|
||||||
|
PullbackStartedFlag = true;
|
||||||
Boost = 0.0;
|
Boost = 0.0;
|
||||||
Threshold = 1000000000.0;
|
Threshold = 1000000000.0;
|
||||||
loader::play_sound(HardHitSoundId);
|
loader::play_sound(HardHitSoundId, this, "TPlunger1");
|
||||||
PullbackTimer(0, this);
|
PullbackTimer(0, this);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
case 1015:
|
|
||||||
{
|
|
||||||
auto ball = PinballTable->BallList.at(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;
|
break;
|
||||||
case 1005:
|
case MessageCode::PlungerFeedBall:
|
||||||
case 1009:
|
|
||||||
case 1010:
|
|
||||||
case 1024:
|
|
||||||
{
|
{
|
||||||
if (code == 1024)
|
if (PinballTable->BallCountInRect(PinballTable->PlungerPosition, PinballTable->CollisionCompOffset * 1.2f))
|
||||||
{
|
{
|
||||||
if (BallFeedTimer_)
|
timer::set(1.0f, this, BallFeedTimer);
|
||||||
timer::kill(BallFeedTimer_);
|
|
||||||
BallFeedTimer_ = 0;
|
|
||||||
}
|
}
|
||||||
|
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;
|
Threshold = 0.0;
|
||||||
if (PullbackTimer_)
|
if (PullbackTimer_)
|
||||||
timer::kill(PullbackTimer_);
|
timer::kill(PullbackTimer_);
|
||||||
PullbackTimer_ = 0;
|
PullbackTimer_ = 0;
|
||||||
if (code == 1005)
|
loader::play_sound(SoundIndexP2, this, "TPlunger3");
|
||||||
loader::play_sound(SoundIndexP2);
|
SpriteSet(0);
|
||||||
auto bmp = ListBitmap->at(0);
|
timer::set(PullbackDelay, this, ReleasedTimer);
|
||||||
auto zMap = ListZMap->at(0);
|
}
|
||||||
render::sprite_set(
|
break;
|
||||||
RenderSprite,
|
case MessageCode::Reset:
|
||||||
bmp,
|
{
|
||||||
zMap,
|
PullbackStartedFlag = false;
|
||||||
bmp->XPosition - PinballTable->XOffset,
|
Boost = 0.0f;
|
||||||
bmp->YPosition - PinballTable->YOffset);
|
Threshold = 1000000000.0f;
|
||||||
|
SomeCounter = 0;
|
||||||
|
|
||||||
timer::set(Unknown4F, this, PlungerReleasedTimer);
|
timer::kill(BallFeedTimer);
|
||||||
|
timer::kill(PullbackTimer);
|
||||||
|
timer::kill(ReleasedTimer);
|
||||||
|
|
||||||
|
SpriteSet(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
control::handler(code, this);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPlunger::BallFeedTimer(int timerId, void* caller)
|
void TPlunger::BallFeedTimer(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto plunger = static_cast<TPlunger*>(caller);
|
auto plunger = static_cast<TPlunger*>(caller);
|
||||||
plunger->PullbackTimer_ = 0;
|
plunger->Message(MessageCode::PlungerFeedBall, 0.0);
|
||||||
plunger->Message(1015, 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPlunger::PullbackTimer(int timerId, void* caller)
|
void TPlunger::PullbackTimer(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto plunger = static_cast<TPlunger*>(caller);
|
auto plunger = static_cast<TPlunger*>(caller);
|
||||||
plunger->Boost += static_cast<float>(plunger->PullbackIncrement);
|
plunger->Boost += plunger->PullbackIncrement;
|
||||||
if (plunger->Boost <= static_cast<float>(plunger->MaxPullback))
|
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
|
else
|
||||||
{
|
{
|
||||||
plunger->PullbackTimer_ = 0;
|
plunger->PullbackTimer_ = 0;
|
||||||
plunger->Boost = static_cast<float>(plunger->MaxPullback);
|
plunger->Boost = plunger->MaxPullback;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = static_cast<int>(floor(
|
int index = static_cast<int>(floor(
|
||||||
static_cast<float>(plunger->ListBitmap->size() - 1) *
|
static_cast<float>(plunger->ListBitmap->size() - 1) *
|
||||||
(plunger->Boost / static_cast<float>(plunger->MaxPullback))));
|
(plunger->Boost / plunger->MaxPullback)));
|
||||||
auto bmp = plunger->ListBitmap->at(index);
|
plunger->SpriteSet(index);
|
||||||
auto zMap = plunger->ListZMap->at(index);
|
|
||||||
render::sprite_set(
|
|
||||||
plunger->RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - plunger->PinballTable->XOffset,
|
|
||||||
bmp->YPosition - plunger->PinballTable->YOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPlunger::PlungerReleasedTimer(int timerId, void* caller)
|
void TPlunger::ReleasedTimer(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto plunger = static_cast<TPlunger*>(caller);
|
auto plunger = static_cast<TPlunger*>(caller);
|
||||||
plunger->Threshold = 1000000000.0;
|
plunger->Threshold = 1000000000.0;
|
||||||
|
|
|
@ -7,19 +7,21 @@ class TPlunger :
|
||||||
public:
|
public:
|
||||||
TPlunger(TPinballTable* table, int groupIndex);
|
TPlunger(TPinballTable* table, int groupIndex);
|
||||||
~TPlunger() override = default;
|
~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;
|
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 BallFeedTimer(int timerId, void* caller);
|
||||||
static void PullbackTimer(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 PullbackTimer_;
|
||||||
int BallFeedTimer_;
|
int BallFeedTimer_;
|
||||||
int MaxPullback;
|
float MaxPullback;
|
||||||
int PullbackIncrement;
|
float PullbackIncrement;
|
||||||
float Unknown4F;
|
float PullbackDelay;
|
||||||
int SoundIndexP1;
|
int SoundIndexP1;
|
||||||
int SoundIndexP2;
|
int SoundIndexP2;
|
||||||
|
bool PullbackStartedFlag{};
|
||||||
|
int SomeCounter{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,38 +10,38 @@
|
||||||
|
|
||||||
TPopupTarget::TPopupTarget(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
TPopupTarget::TPopupTarget(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||||
{
|
{
|
||||||
this->Timer = 0;
|
Timer = 0;
|
||||||
this->TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPopupTarget::Message(int code, float value)
|
int TPopupTarget::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 49:
|
case MessageCode::TPopupTargetDisable:
|
||||||
this->ActiveFlag = 0;
|
ActiveFlag = 0;
|
||||||
render::sprite_set_bitmap(this->RenderSprite, nullptr);
|
SpriteSet(-1);
|
||||||
break;
|
break;
|
||||||
case 50:
|
case MessageCode::TPopupTargetEnable:
|
||||||
this->Timer = timer::set(this->TimerTime, this, TimerExpired);
|
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||||
break;
|
break;
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
this->PlayerMessagefieldBackup[this->PinballTable->CurrentPlayer] = this->MessageField;
|
PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField;
|
||||||
this->MessageField = this->PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
MessageField = PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
||||||
TPopupTarget::Message(50 - (MessageField != 0), 0.0);
|
TPopupTarget::Message(MessageField ? MessageCode::TPopupTargetDisable : MessageCode::TPopupTargetEnable, 0.0);
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
{
|
{
|
||||||
this->MessageField = 0;
|
MessageField = 0;
|
||||||
int* playerPtr = this->PlayerMessagefieldBackup;
|
int* playerPtr = PlayerMessagefieldBackup;
|
||||||
for (auto index = 0; index < this->PinballTable->PlayerCount; ++index)
|
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
||||||
{
|
{
|
||||||
*playerPtr = 0;
|
*playerPtr = 0;
|
||||||
++playerPtr;
|
++playerPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->Timer)
|
if (Timer)
|
||||||
timer::kill(this->Timer);
|
timer::kill(Timer);
|
||||||
TimerExpired(0, this);
|
TimerExpired(0, this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -51,37 +51,26 @@ int TPopupTarget::Message(int code, float value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPopupTarget::put_scoring(int index, int score)
|
void TPopupTarget::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
{
|
|
||||||
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,
|
|
||||||
TEdgeSegment* edge)
|
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(
|
else if (maths::basic_collision(
|
||||||
ball,
|
ball,
|
||||||
nextPosition,
|
nextPosition,
|
||||||
direction,
|
direction,
|
||||||
this->Elasticity,
|
Elasticity,
|
||||||
this->Smoothness,
|
Smoothness,
|
||||||
this->Threshold,
|
Threshold,
|
||||||
this->Boost) > this->Threshold)
|
Boost) > Threshold)
|
||||||
{
|
{
|
||||||
if (this->HardHitSoundId)
|
if (HardHitSoundId)
|
||||||
loader::play_sound(this->HardHitSoundId);
|
loader::play_sound(HardHitSoundId, this, "TPopupTarget1");
|
||||||
this->Message(49, 0.0);
|
Message(MessageCode::TPopupTargetDisable, 0.0);
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +79,10 @@ void TPopupTarget::TimerExpired(int timerId, void* caller)
|
||||||
auto target = static_cast<TPopupTarget*>(caller);
|
auto target = static_cast<TPopupTarget*>(caller);
|
||||||
target->Timer = 0;
|
target->Timer = 0;
|
||||||
target->ActiveFlag = 1;
|
target->ActiveFlag = 1;
|
||||||
render::sprite_set_bitmap(target->RenderSprite, target->ListBitmap->at(0));
|
target->SpriteSet(0);
|
||||||
if (timerId)
|
if (timerId)
|
||||||
{
|
{
|
||||||
if (target->SoftHitSoundId)
|
if (target->SoftHitSoundId)
|
||||||
loader::play_sound(target->SoftHitSoundId);
|
loader::play_sound(target->SoftHitSoundId, target, "TPopupTarget2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,13 @@ class TPopupTarget :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TPopupTarget(TPinballTable* table, int groupIndex);
|
TPopupTarget(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void put_scoring(int index, int score) override;
|
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge) override;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int Timer;
|
int Timer;
|
||||||
float TimerTime;
|
float TimerTime;
|
||||||
int Scores[3]{};
|
|
||||||
int PlayerMessagefieldBackup[4]{};
|
int PlayerMessagefieldBackup[4]{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||||
{
|
{
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
vector_type end{}, start{}, *end2, *start2, *start3, *end3;
|
vector2 wall1End{}, wall1Start{}, wall2Start{}, wall2End{};
|
||||||
|
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
UnusedBaseFlag = 1;
|
UnusedBaseFlag = 1;
|
||||||
|
@ -22,60 +22,51 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
CollisionGroup = visual.CollisionGroup;
|
CollisionGroup = visual.CollisionGroup;
|
||||||
|
|
||||||
BallFieldMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
BallFieldMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
|
||||||
RampFlag1 = static_cast<int>(loader::query_float_attribute(groupIndex, 0, 1305, 0));
|
BallZOffsetFlag = static_cast<int>(loader::query_float_attribute(groupIndex, 0, 1305, 0));
|
||||||
|
|
||||||
auto floatArr3Plane = loader::query_float_attribute(groupIndex, 0, 1300);
|
auto floatArr3Plane = loader::query_float_attribute(groupIndex, 0, 1300);
|
||||||
RampPlaneCount = static_cast<int>(floor(*floatArr3Plane));
|
RampPlaneCount = static_cast<int>(floor(*floatArr3Plane));
|
||||||
RampPlane = reinterpret_cast<ramp_plane_type*>(floatArr3Plane + 1);
|
RampPlane = reinterpret_cast<ramp_plane_type*>(floatArr3Plane + 1);
|
||||||
|
|
||||||
auto floatArr4 = loader::query_float_attribute(groupIndex, 0, 1303);
|
auto wall0Arr = loader::query_float_attribute(groupIndex, 0, 1303);
|
||||||
end.X = floatArr4[2];
|
auto wall0CollisionGroup = 1 << static_cast<int>(floor(wall0Arr[0]));
|
||||||
end.Y = floatArr4[3];
|
auto wall0Pts = reinterpret_cast<wall_point_type*>(wall0Arr + 2);
|
||||||
start.X = floatArr4[4];
|
Line1 = new TLine(this, &ActiveFlag, wall0CollisionGroup, wall0Pts->Pt1, wall0Pts->Pt0);
|
||||||
start.Y = floatArr4[5];
|
|
||||||
Line1 = new TLine(this, &ActiveFlag, 1 << static_cast<int>(floor(floatArr4[0])), &start, &end);
|
|
||||||
EdgeList.push_back(Line1);
|
|
||||||
if (Line1)
|
|
||||||
{
|
|
||||||
Line1->WallValue = nullptr;
|
Line1->WallValue = nullptr;
|
||||||
Line1->place_in_grid();
|
Line1->place_in_grid(&AABB);
|
||||||
}
|
EdgeList.push_back(Line1);
|
||||||
|
|
||||||
auto floatArr5WallPoint = loader::query_float_attribute(groupIndex, 0, 1301);
|
auto wall1Arr = loader::query_float_attribute(groupIndex, 0, 1301);
|
||||||
Wall1PointFirst = 1 << static_cast<int>(floor(floatArr5WallPoint[0]));
|
Wall1CollisionGroup = 1 << static_cast<int>(floor(wall1Arr[0]));
|
||||||
auto wallPt1_2 = static_cast<int>(floor(floatArr5WallPoint[1]));
|
auto wall1Enabled = static_cast<int>(floor(wall1Arr[1]));
|
||||||
Wall1PointLast = floatArr5WallPoint[7];
|
Wall1BallOffset = wall1Arr[7];
|
||||||
maths::find_closest_edge(
|
maths::find_closest_edge(
|
||||||
RampPlane,
|
RampPlane,
|
||||||
RampPlaneCount,
|
RampPlaneCount,
|
||||||
reinterpret_cast<wall_point_type*>(floatArr5WallPoint + 3),
|
reinterpret_cast<wall_point_type*>(wall1Arr + 3),
|
||||||
&end2,
|
wall1End,
|
||||||
&start2);
|
wall1Start);
|
||||||
Line2 = new TLine(this, &ActiveFlag, CollisionGroup, start2, end2);
|
|
||||||
EdgeList.push_back(Line2);
|
Line2 = new TLine(this, &ActiveFlag, CollisionGroup, wall1Start, wall1End);
|
||||||
if (Line2)
|
|
||||||
{
|
|
||||||
Line2->WallValue = nullptr;
|
Line2->WallValue = nullptr;
|
||||||
Line2->place_in_grid();
|
Line2->place_in_grid(&AABB);
|
||||||
}
|
EdgeList.push_back(Line2);
|
||||||
|
|
||||||
auto floatArr6WallPoint = loader::query_float_attribute(groupIndex, 0, 1302);
|
auto wall2Arr = loader::query_float_attribute(groupIndex, 0, 1302);
|
||||||
auto wall2Pt1_2 = static_cast<int>(floor(floatArr6WallPoint[1]));
|
Wall2CollisionGroup = 1 << static_cast<int>(floor(wall2Arr[0]));
|
||||||
Wall2PointFirst = 1 << static_cast<int>(floor(floatArr6WallPoint[0]));
|
auto wall2Enabled = static_cast<int>(floor(wall2Arr[1]));
|
||||||
Wall2PointLast = floatArr6WallPoint[7];
|
Wall2BallOffset = wall2Arr[7];
|
||||||
maths::find_closest_edge(
|
maths::find_closest_edge(
|
||||||
RampPlane,
|
RampPlane,
|
||||||
RampPlaneCount,
|
RampPlaneCount,
|
||||||
reinterpret_cast<wall_point_type*>(floatArr6WallPoint + 3),
|
reinterpret_cast<wall_point_type*>(wall2Arr + 3),
|
||||||
&end3,
|
wall2End,
|
||||||
&start3);
|
wall2Start);
|
||||||
Line3 = new TLine(this, &ActiveFlag, CollisionGroup, start3, end3);
|
|
||||||
EdgeList.push_back(Line3);
|
Line3 = new TLine(this, &ActiveFlag, CollisionGroup, wall2Start, wall2End);
|
||||||
if (Line3)
|
|
||||||
{
|
|
||||||
Line3->WallValue = nullptr;
|
Line3->WallValue = nullptr;
|
||||||
Line3->place_in_grid();
|
Line3->place_in_grid(&AABB);
|
||||||
}
|
EdgeList.push_back(Line3);
|
||||||
|
|
||||||
|
|
||||||
auto xMin = 1000000000.0f;
|
auto xMin = 1000000000.0f;
|
||||||
|
@ -84,57 +75,50 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
auto xMax = -1000000000.0f;
|
auto xMax = -1000000000.0f;
|
||||||
for (auto index = 0; index < RampPlaneCount; index++)
|
for (auto index = 0; index < RampPlaneCount; index++)
|
||||||
{
|
{
|
||||||
auto plane = &RampPlane[index];
|
auto& plane = RampPlane[index];
|
||||||
auto pVec1 = reinterpret_cast<vector_type*>(&plane->V1);
|
vector2* pointOrder[4] = { &plane.V1, &plane.V2, &plane.V3, &plane.V1 };
|
||||||
auto pVec2 = reinterpret_cast<vector_type*>(&plane->V2);
|
|
||||||
auto pVec3 = reinterpret_cast<vector_type*>(&plane->V3);
|
|
||||||
|
|
||||||
xMin = std::min(std::min(std::min(plane->V3.X, plane->V1.X), plane->V2.X), xMin);
|
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
|
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);
|
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);
|
yMax = std::max(std::max(std::max(plane.V3.Y, plane.V1.Y), plane.V2.Y), xMin);
|
||||||
|
|
||||||
vector_type* pointOrder[4] = {pVec1, pVec2, pVec3, pVec1};
|
|
||||||
for (auto pt = 0; pt < 3; pt++)
|
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;
|
auto collisionGroup = 0;
|
||||||
if (point1 != end2 || point2 != start2)
|
|
||||||
|
if (point1 == wall1End && point2 == wall1Start)
|
||||||
{
|
{
|
||||||
if (point1 != end3 || point2 != start3)
|
if (wall1Enabled)
|
||||||
|
collisionGroup = Wall1CollisionGroup;
|
||||||
|
}
|
||||||
|
else if (point1 == wall2End && point2 == wall2Start)
|
||||||
{
|
{
|
||||||
|
if (wall2Enabled)
|
||||||
|
collisionGroup = Wall2CollisionGroup;
|
||||||
|
}
|
||||||
|
else
|
||||||
collisionGroup = visual.CollisionGroup;
|
collisionGroup = visual.CollisionGroup;
|
||||||
}
|
|
||||||
else if (wall2Pt1_2)
|
|
||||||
{
|
|
||||||
collisionGroup = Wall2PointFirst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (wallPt1_2)
|
|
||||||
{
|
|
||||||
collisionGroup = Wall1PointFirst;
|
|
||||||
}
|
|
||||||
if (collisionGroup)
|
if (collisionGroup)
|
||||||
{
|
{
|
||||||
auto line = new TLine(this, &ActiveFlag, collisionGroup, point1, point2);
|
auto line = new TLine(this, &ActiveFlag, collisionGroup, point1, point2);
|
||||||
|
line->WallValue = &plane;
|
||||||
|
line->place_in_grid(&AABB);
|
||||||
EdgeList.push_back(line);
|
EdgeList.push_back(line);
|
||||||
if (line)
|
|
||||||
{
|
|
||||||
line->WallValue = plane;
|
|
||||||
line->place_in_grid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plane->FieldForce.X = cos(plane->GravityAngle2) * sin(plane->GravityAngle1) *
|
plane.FieldForce.X = cos(plane.GravityAngle2) * sin(plane.GravityAngle1) *
|
||||||
PinballTable->GravityDirVectMult;
|
PinballTable->GravityDirVectMult;
|
||||||
plane->FieldForce.Y = sin(plane->GravityAngle2) * sin(plane->GravityAngle1) *
|
plane.FieldForce.Y = sin(plane.GravityAngle2) * sin(plane.GravityAngle1) *
|
||||||
PinballTable->GravityDirVectMult;
|
PinballTable->GravityDirVectMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
Field.Flag2Ptr = &ActiveFlag;
|
Field.ActiveFlag = &ActiveFlag;
|
||||||
Field.CollisionComp = this;
|
Field.CollisionComp = this;
|
||||||
Field.Mask = visual.CollisionGroup;
|
Field.CollisionGroup = visual.CollisionGroup;
|
||||||
|
|
||||||
auto x1 = xMax;
|
auto x1 = xMax;
|
||||||
auto y1 = yMax;
|
auto y1 = yMax;
|
||||||
|
@ -143,23 +127,12 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
TTableLayer::edges_insert_square(y0, x0, y1, x1, nullptr, &Field);
|
TTableLayer::edges_insert_square(y0, x0, y1, x1, nullptr, &Field);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TRamp::put_scoring(int index, int score)
|
void TRamp::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
|
|
||||||
auto plane = static_cast<ramp_plane_type*>(edge->WallValue);
|
auto plane = static_cast<ramp_plane_type*>(edge->WallValue);
|
||||||
if (plane)
|
if (plane)
|
||||||
|
@ -171,8 +144,8 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||||
ball->RampFieldForce.X = plane->FieldForce.X;
|
ball->RampFieldForce.X = plane->FieldForce.X;
|
||||||
ball->RampFieldForce.Y = plane->FieldForce.Y;
|
ball->RampFieldForce.Y = plane->FieldForce.Y;
|
||||||
ball->Position.Z = ball->Position.X * ball->CollisionOffset.X + ball->Position.Y * ball->CollisionOffset.Y +
|
ball->Position.Z = ball->Position.X * ball->CollisionOffset.X + ball->Position.Y * ball->CollisionOffset.Y +
|
||||||
ball->Offset + ball->CollisionOffset.Z;
|
ball->Radius + ball->CollisionOffset.Z;
|
||||||
ball->FieldFlag = CollisionGroup;
|
ball->CollisionMask = CollisionGroup;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,8 +153,8 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||||
{
|
{
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, ball, "TRamp");
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -189,25 +162,23 @@ void TRamp::Collision(TBall* ball, vector_type* nextPosition, vector_type* direc
|
||||||
ball->CollisionFlag = 0;
|
ball->CollisionFlag = 0;
|
||||||
if (edge == Line2)
|
if (edge == Line2)
|
||||||
{
|
{
|
||||||
ball->FieldFlag = Wall1PointFirst;
|
ball->CollisionMask = Wall1CollisionGroup;
|
||||||
if (!RampFlag1)
|
if (BallZOffsetFlag)
|
||||||
return;
|
ball->Position.Z = ball->Radius + Wall1BallOffset;
|
||||||
ball->Position.Z = ball->Offset + Wall1PointLast;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ball->FieldFlag = Wall2PointFirst;
|
ball->CollisionMask = Wall2CollisionGroup;
|
||||||
if (!RampFlag1)
|
if (BallZOffsetFlag)
|
||||||
return;
|
ball->Position.Z = ball->Radius + Wall2BallOffset;
|
||||||
ball->Position.Z = ball->Offset + Wall2PointLast;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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->X = ball->RampFieldForce.X - ball->Direction.X * ball->Speed * BallFieldMult;
|
||||||
vecDst->Y = ball->RampFieldForce.Y - ball->Acceleration.Y * ball->Speed * BallFieldMult;
|
vecDst->Y = ball->RampFieldForce.Y - ball->Direction.Y * ball->Speed * BallFieldMult;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,25 +10,22 @@ class TRamp :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TRamp(TPinballTable* table, int groupIndex);
|
TRamp(TPinballTable* table, int groupIndex);
|
||||||
void put_scoring(int index, int score) override;
|
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge) override;
|
TEdgeSegment* edge) override;
|
||||||
int FieldEffect(TBall* ball, vector_type* vecDst) override;
|
int FieldEffect(TBall* ball, vector2* vecDst) override;
|
||||||
void port_draw() override;
|
void port_draw() override;
|
||||||
|
|
||||||
int Scores[4]{};
|
|
||||||
field_effect_type Field{};
|
field_effect_type Field{};
|
||||||
int CollisionGroup;
|
int CollisionGroup;
|
||||||
int RampFlag1;
|
bool BallZOffsetFlag;
|
||||||
int RampPlaneCount;
|
int RampPlaneCount;
|
||||||
float BallFieldMult;
|
float BallFieldMult;
|
||||||
ramp_plane_type* RampPlane;
|
ramp_plane_type* RampPlane;
|
||||||
TEdgeSegment* Line2;
|
TEdgeSegment* Line2;
|
||||||
TEdgeSegment* Line3;
|
TEdgeSegment* Line3;
|
||||||
TEdgeSegment* Line1;
|
TEdgeSegment* Line1;
|
||||||
int Wall1PointFirst;
|
int Wall1CollisionGroup;
|
||||||
int Wall2PointFirst;
|
int Wall2CollisionGroup;
|
||||||
float Wall1PointLast;
|
float Wall1BallOffset;
|
||||||
float Wall2PointLast;
|
float Wall2BallOffset;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,32 +19,30 @@ TRollover::TRollover(TPinballTable* table, int groupIndex, bool createWall) : TC
|
||||||
|
|
||||||
TRollover::TRollover(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
TRollover::TRollover(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
|
||||||
{
|
{
|
||||||
if (ListBitmap)
|
SpriteSet(0);
|
||||||
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
|
|
||||||
build_walls(groupIndex);
|
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;
|
ActiveFlag = 1;
|
||||||
this->RolloverFlag = 0;
|
RolloverFlag = 0;
|
||||||
if (this->ListBitmap)
|
SpriteSet(0);
|
||||||
render::sprite_set_bitmap(this->RenderSprite, this->ListBitmap->at(0));
|
|
||||||
}
|
}
|
||||||
return 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)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
gdrv_bitmap8* bmp = nullptr;
|
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
if (RolloverFlag)
|
if (RolloverFlag)
|
||||||
|
@ -54,31 +52,14 @@ void TRollover::Collision(TBall* ball, vector_type* nextPosition, vector_type* d
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, ball, "TRollover");
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
RolloverFlag = RolloverFlag == 0;
|
RolloverFlag = RolloverFlag == 0;
|
||||||
if (ListBitmap)
|
SpriteSet(RolloverFlag ? -1 : 0);
|
||||||
{
|
|
||||||
if (!RolloverFlag)
|
|
||||||
bmp = ListBitmap->at(0);
|
|
||||||
render::sprite_set_bitmap(RenderSprite, bmp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void TRollover::build_walls(int groupIndex)
|
||||||
{
|
{
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
|
|
|
@ -9,15 +9,12 @@ protected:
|
||||||
public:
|
public:
|
||||||
TRollover(TPinballTable* table, int groupIndex);
|
TRollover(TPinballTable* table, int groupIndex);
|
||||||
~TRollover() override = default;
|
~TRollover() override = default;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
void put_scoring(int index, int score) override;
|
|
||||||
int get_scoring(int index) override;
|
|
||||||
void build_walls(int groupIndex);
|
void build_walls(int groupIndex);
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
char RolloverFlag{};
|
char RolloverFlag{};
|
||||||
int Scores[2]{};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "TPinballTable.h"
|
#include "TPinballTable.h"
|
||||||
#include "TBall.h"
|
#include "TBall.h"
|
||||||
|
#include "TDrain.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||||
|
@ -14,9 +15,8 @@ TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
visualStruct visual{};
|
visualStruct visual{};
|
||||||
|
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
Timer = 0;
|
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
BallAcceleration = visual.Kicker.ThrowBallAcceleration;
|
BallThrowDirection = visual.Kicker.ThrowBallDirection;
|
||||||
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
ThrowAngleMult = visual.Kicker.ThrowBallAngleMult;
|
||||||
ThrowSpeedMult1 = visual.Kicker.Boost;
|
ThrowSpeedMult1 = visual.Kicker.Boost;
|
||||||
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f;
|
||||||
|
@ -28,34 +28,26 @@ TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
|
||||||
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
TimerTime = *loader::query_float_attribute(groupIndex, 0, 407);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TSink::Message(int code, float value)
|
int TSink::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 56:
|
case MessageCode::TSinkResetTimer:
|
||||||
if (value < 0.0f)
|
if (value < 0.0f)
|
||||||
value = TimerTime;
|
value = TimerTime;
|
||||||
Timer = timer::set(value, this, TimerExpired);
|
timer::set(value, this, TimerExpired);
|
||||||
break;
|
break;
|
||||||
case 1020:
|
case MessageCode::PlayerChanged:
|
||||||
|
timer::kill(TimerExpired);
|
||||||
PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField;
|
PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField;
|
||||||
MessageField = PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
MessageField = PlayerMessagefieldBackup[static_cast<int>(floor(value))];
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
{
|
{
|
||||||
if (Timer)
|
timer::kill(TimerExpired);
|
||||||
timer::kill(Timer);
|
|
||||||
Timer = 0;
|
|
||||||
MessageField = 0;
|
MessageField = 0;
|
||||||
|
for (auto &msgBackup : PlayerMessagefieldBackup)
|
||||||
auto playerPtr = PlayerMessagefieldBackup;
|
msgBackup = 0;
|
||||||
for (auto index = 0; index < PinballTable->PlayerCount; ++index)
|
|
||||||
{
|
|
||||||
*playerPtr = 0;
|
|
||||||
|
|
||||||
++playerPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -64,44 +56,36 @@ int TSink::Message(int code, float value)
|
||||||
return 0;
|
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)
|
if (PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0);
|
PinballTable->Drain->Collision(ball, nextPosition, direction, distance, edge);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ball->ActiveFlag = 0;
|
ball->Disable();
|
||||||
render::sprite_set_bitmap(ball->RenderSprite, nullptr);
|
loader::play_sound(SoundIndex4, ball, "TSink1");
|
||||||
loader::play_sound(SoundIndex4);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
control::handler(63, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSink::TimerExpired(int timerId, void* caller)
|
void TSink::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto sink = static_cast<TSink*>(caller);
|
auto sink = static_cast<TSink*>(caller);
|
||||||
auto ball = sink->PinballTable->BallList.at(0);
|
auto table = sink->PinballTable;
|
||||||
ball->CollisionComp = nullptr;
|
if (table->BallCountInRect(sink->BallPosition, table->CollisionCompOffset * 2.0f))
|
||||||
ball->ActiveFlag = 1;
|
{
|
||||||
ball->Position.X = sink->BallPosition.X;
|
timer::set(0.5f, sink, TimerExpired);
|
||||||
ball->Position.Y = sink->BallPosition.Y;
|
}
|
||||||
TBall::throw_ball(ball, &sink->BallAcceleration, sink->ThrowAngleMult, sink->ThrowSpeedMult1,
|
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);
|
sink->ThrowSpeedMult2);
|
||||||
if (sink->SoundIndex3)
|
if (sink->SoundIndex3)
|
||||||
loader::play_sound(sink->SoundIndex3);
|
loader::play_sound(sink->SoundIndex3, ball, "TSink2");
|
||||||
sink->Timer = 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,23 +7,19 @@ class TSink :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TSink(TPinballTable* table, int groupIndex);
|
TSink(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void put_scoring(int index, int score) override;
|
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge) override;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int Timer;
|
|
||||||
float TimerTime;
|
float TimerTime;
|
||||||
vector_type BallPosition{};
|
vector2 BallPosition{};
|
||||||
vector_type BallAcceleration{};
|
vector3 BallThrowDirection{};
|
||||||
float ThrowAngleMult;
|
float ThrowAngleMult;
|
||||||
float ThrowSpeedMult1;
|
float ThrowSpeedMult1;
|
||||||
float ThrowSpeedMult2;
|
float ThrowSpeedMult2;
|
||||||
int SoundIndex4;
|
int SoundIndex4;
|
||||||
int SoundIndex3;
|
int SoundIndex3;
|
||||||
int Scores[3]{};
|
|
||||||
int PlayerMessagefieldBackup[4]{};
|
int PlayerMessagefieldBackup[4]{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,18 +16,18 @@ TSoloTarget::TSoloTarget(TPinballTable* table, int groupIndex) : TCollisionCompo
|
||||||
TimerTime = 0.1f;
|
TimerTime = 0.1f;
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
SoundIndex4 = visual.SoundIndex4;
|
SoundIndex4 = visual.SoundIndex4;
|
||||||
TSoloTarget::Message(50, 0.0);
|
TSoloTarget::Message(MessageCode::TSoloTargetEnable, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TSoloTarget::Message(int code, float value)
|
int TSoloTarget::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case 49:
|
case MessageCode::TSoloTargetDisable:
|
||||||
case 50:
|
case MessageCode::TSoloTargetEnable:
|
||||||
ActiveFlag = code == 50;
|
ActiveFlag = code == MessageCode::TSoloTargetEnable;
|
||||||
break;
|
break;
|
||||||
case 1024:
|
case MessageCode::Reset:
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
|
@ -37,47 +37,24 @@ int TSoloTarget::Message(int code, float value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ListBitmap)
|
SpriteSet(1 - ActiveFlag);
|
||||||
{
|
|
||||||
auto index = 1 - ActiveFlag;
|
|
||||||
auto bmp = ListBitmap->at(index);
|
|
||||||
auto zMap = ListZMap->at(index);
|
|
||||||
render::sprite_set(
|
|
||||||
RenderSprite,
|
|
||||||
bmp,
|
|
||||||
zMap,
|
|
||||||
bmp->XPosition - PinballTable->XOffset,
|
|
||||||
bmp->YPosition - PinballTable->YOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSoloTarget::put_scoring(int index, int score)
|
void TSoloTarget::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
{
|
|
||||||
if (index < 1)
|
|
||||||
Scores[index] = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TSoloTarget::get_scoring(int index)
|
|
||||||
{
|
|
||||||
return index < 1 ? Scores[index] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TSoloTarget::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
if (DefaultCollision(ball, nextPosition, direction))
|
if (DefaultCollision(ball, nextPosition, direction))
|
||||||
{
|
{
|
||||||
Message(49, 0.0);
|
Message(MessageCode::TSoloTargetDisable, 0.0);
|
||||||
Timer = timer::set(TimerTime, this, TimerExpired);
|
Timer = timer::set(TimerTime, this, TimerExpired);
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSoloTarget::TimerExpired(int timerId, void* caller)
|
void TSoloTarget::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto target = static_cast<TSoloTarget*>(caller);
|
auto target = static_cast<TSoloTarget*>(caller);
|
||||||
target->Message(50, 0.0);
|
target->Message(MessageCode::TSoloTargetEnable, 0.0);
|
||||||
target->Timer = 0;
|
target->Timer = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,8 @@ class TSoloTarget :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TSoloTarget(TPinballTable* table, int groupIndex);
|
TSoloTarget(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void put_scoring(int index, int score) override;
|
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
int get_scoring(int index) override;
|
|
||||||
void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
|
||||||
TEdgeSegment* edge) override;
|
TEdgeSegment* edge) override;
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
@ -17,5 +15,4 @@ public:
|
||||||
int Timer;
|
int Timer;
|
||||||
float TimerTime;
|
float TimerTime;
|
||||||
int SoundIndex4;
|
int SoundIndex4;
|
||||||
int Scores[1]{};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ TSound::TSound(TPinballTable* table, int groupIndex) : TPinballComponent(table,
|
||||||
this->SoundIndex = visual.SoundIndex4;
|
this->SoundIndex = visual.SoundIndex4;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TSound::Play()
|
float TSound::Play(TPinballComponent *soundSource, const char* info)
|
||||||
{
|
{
|
||||||
return loader::play_sound(this->SoundIndex);
|
return loader::play_sound(this->SoundIndex, soundSource, info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ class TSound :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TSound(TPinballTable* table, int groupIndex);
|
TSound(TPinballTable* table, int groupIndex);
|
||||||
float Play();
|
float Play(TPinballComponent *soundSource, const char* info);
|
||||||
|
|
||||||
int SoundIndex;
|
int SoundIndex;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,19 +20,20 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f
|
||||||
|
|
||||||
auto groupIndex = loader::query_handle("table");
|
auto groupIndex = loader::query_handle("table");
|
||||||
loader::query_visual(groupIndex, 0, &visual);
|
loader::query_visual(groupIndex, 0, &visual);
|
||||||
|
auto spriteData = visual.Bitmap;
|
||||||
|
|
||||||
/*Full tilt: proj center first value is offset by resolution*/
|
/*Full tilt: proj center first value is offset by resolution*/
|
||||||
auto projCenter = loader::query_float_attribute(groupIndex, 0, 700 + fullscrn::GetResolution());
|
auto projCenter = loader::query_float_attribute(groupIndex, 0, 700 + fullscrn::GetResolution());
|
||||||
proj::recenter(projCenter[0], projCenter[1]);
|
proj::recenter(projCenter[0], projCenter[1]);
|
||||||
render::set_background_zmap(visual.ZMap, 0, 0);
|
render::set_background_zmap(spriteData.ZMap, 0, 0);
|
||||||
|
|
||||||
auto bmp = visual.Bitmap;
|
auto bmp = spriteData.Bmp;
|
||||||
VisBmp = visual.Bitmap;
|
VisBmp = bmp;
|
||||||
rect.XPosition = 0;
|
rect.XPosition = 0;
|
||||||
rect.YPosition = 0;
|
rect.YPosition = 0;
|
||||||
rect.Width = bmp->Width;
|
rect.Width = bmp->Width;
|
||||||
rect.Height = bmp->Height;
|
rect.Height = bmp->Height;
|
||||||
render::create_sprite(VisualTypes::None, bmp, visual.ZMap, 0, 0, &rect);
|
new render_sprite(VisualTypes::Background, bmp, spriteData.ZMap, 0, 0, &rect);
|
||||||
|
|
||||||
PinballTable->SoundIndex1 = visual.SoundIndex4;
|
PinballTable->SoundIndex1 = visual.SoundIndex4;
|
||||||
PinballTable->SoundIndex2 = visual.SoundIndex3;
|
PinballTable->SoundIndex2 = visual.SoundIndex3;
|
||||||
|
@ -71,37 +72,33 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f
|
||||||
Threshold = visual.Kicker.Threshold;
|
Threshold = visual.Kicker.Threshold;
|
||||||
Boost = 15.0f;
|
Boost = 15.0f;
|
||||||
|
|
||||||
auto visArrPtr = visual.FloatArr;
|
auto edgePoints = reinterpret_cast<vector2*>(visual.FloatArr);
|
||||||
Unknown1F = std::min(visArrPtr[0], std::min(visArrPtr[2], visArrPtr[4]));
|
XMin = std::min(edgePoints[0].X, std::min(edgePoints[1].X, edgePoints[2].X));
|
||||||
Unknown2F = std::min(visArrPtr[1], std::min(visArrPtr[3], visArrPtr[5]));
|
YMin = std::min(edgePoints[0].Y, std::min(edgePoints[1].Y, edgePoints[2].Y));
|
||||||
Unknown3F = std::max(visArrPtr[0], std::max(visArrPtr[2], visArrPtr[4]));
|
XMax = std::max(edgePoints[0].X, std::max(edgePoints[1].X, edgePoints[2].X));
|
||||||
Unknown4F = std::max(visArrPtr[1], std::max(visArrPtr[3], visArrPtr[5]));
|
YMax = std::max(edgePoints[0].Y, std::max(edgePoints[1].Y, edgePoints[2].Y));
|
||||||
auto a2 = Unknown4F - Unknown2F;
|
|
||||||
auto a1 = Unknown3F - Unknown1F;
|
|
||||||
edge_manager = new TEdgeManager(Unknown1F, Unknown2F, a1, a2);
|
|
||||||
|
|
||||||
for (auto visFloatArrCount = visual.FloatArrCount; visFloatArrCount > 0; visFloatArrCount--)
|
auto height = YMax - YMin;
|
||||||
|
auto width = XMax - XMin;
|
||||||
|
edge_manager = new TEdgeManager(XMin, YMin, width, height);
|
||||||
|
|
||||||
|
for (auto i = 0; i < visual.FloatArrCount; i++)
|
||||||
{
|
{
|
||||||
auto line = new TLine(this,
|
auto line = new TLine(this,
|
||||||
&ActiveFlag,
|
&ActiveFlag,
|
||||||
visual.CollisionGroup,
|
visual.CollisionGroup,
|
||||||
visArrPtr[2],
|
edgePoints[i + 1].X,
|
||||||
visArrPtr[3],
|
edgePoints[i + 1].Y,
|
||||||
visArrPtr[0],
|
edgePoints[i].X,
|
||||||
visArrPtr[1]);
|
edgePoints[i].Y);
|
||||||
if (line)
|
line->place_in_grid(&AABB);
|
||||||
{
|
|
||||||
line->place_in_grid();
|
|
||||||
EdgeList.push_back(line);
|
EdgeList.push_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
visArrPtr += 2;
|
Field.CollisionGroup = -1;
|
||||||
}
|
Field.ActiveFlag = &ActiveFlag;
|
||||||
|
|
||||||
Field.Mask = -1;
|
|
||||||
Field.Flag2Ptr = &ActiveFlag;
|
|
||||||
Field.CollisionComp = this;
|
Field.CollisionComp = this;
|
||||||
edges_insert_square(Unknown2F, Unknown1F, Unknown4F, Unknown3F, nullptr,
|
edges_insert_square(YMin, XMin, YMax, XMax, nullptr,
|
||||||
&Field);
|
&Field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,11 +108,11 @@ TTableLayer::~TTableLayer()
|
||||||
delete edge_manager;
|
delete edge_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TTableLayer::FieldEffect(TBall* ball, vector_type* vecDst)
|
int TTableLayer::FieldEffect(TBall* ball, vector2* vecDst)
|
||||||
{
|
{
|
||||||
vecDst->X = GraityDirX - (0.5f - RandFloat() + ball->Acceleration.X) *
|
vecDst->X = GraityDirX - (0.5f - RandFloat() + ball->Direction.X) *
|
||||||
ball->Speed * GraityMult;
|
ball->Speed * GraityMult;
|
||||||
vecDst->Y = GraityDirY - ball->Acceleration.Y * ball->Speed * GraityMult;
|
vecDst->Y = GraityDirY - ball->Direction.Y * ball->Speed * GraityMult;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,10 +131,10 @@ void TTableLayer::edges_insert_square(float y0, float x0, float y1, float x1, TE
|
||||||
int xMaxBox = edge_manager->box_x(xMax);
|
int xMaxBox = edge_manager->box_x(xMax);
|
||||||
int yMaxBox = edge_manager->box_y(yMax);
|
int yMaxBox = edge_manager->box_y(yMax);
|
||||||
|
|
||||||
float boxX = static_cast<float>(xMinBox) * edge_manager->AdvanceX + edge_manager->X;
|
float boxX = static_cast<float>(xMinBox) * edge_manager->AdvanceX + edge_manager->MinX;
|
||||||
for (int indexX = xMinBox; indexX <= xMaxBox; ++indexX)
|
for (int indexX = xMinBox; indexX <= xMaxBox; ++indexX)
|
||||||
{
|
{
|
||||||
float boxY = static_cast<float>(yMinBox) * edge_manager->AdvanceY + edge_manager->Y;
|
float boxY = static_cast<float>(yMinBox) * edge_manager->AdvanceY + edge_manager->MinY;
|
||||||
for (int indexY = yMinBox; indexY <= yMaxBox; ++indexY)
|
for (int indexY = yMinBox; indexY <= yMaxBox; ++indexY)
|
||||||
{
|
{
|
||||||
if (xMax >= boxX && xMin <= boxX + edge_manager->AdvanceX &&
|
if (xMax >= boxX && xMin <= boxX + edge_manager->AdvanceX &&
|
||||||
|
@ -161,7 +158,7 @@ void TTableLayer::edges_insert_square(float y0, float x0, float y1, float x1, TE
|
||||||
void TTableLayer::edges_insert_circle(circle_type* circle, TEdgeSegment* edge, field_effect_type* field)
|
void TTableLayer::edges_insert_circle(circle_type* circle, TEdgeSegment* edge, field_effect_type* field)
|
||||||
{
|
{
|
||||||
ray_type ray{};
|
ray_type ray{};
|
||||||
vector_type vec1{};
|
vector2 vec1{};
|
||||||
|
|
||||||
auto radiusM = sqrt(circle->RadiusSq) + edge_manager->AdvanceX * 0.001f;
|
auto radiusM = sqrt(circle->RadiusSq) + edge_manager->AdvanceX * 0.001f;
|
||||||
auto radiusMSq = radiusM * radiusM;
|
auto radiusMSq = radiusM * radiusM;
|
||||||
|
@ -182,10 +179,10 @@ void TTableLayer::edges_insert_circle(circle_type* circle, TEdgeSegment* edge, f
|
||||||
xMaxBox = edge_manager->increment_box_x(xMaxBox);
|
xMaxBox = edge_manager->increment_box_x(xMaxBox);
|
||||||
yMaxBox = edge_manager->increment_box_y(yMaxBox);
|
yMaxBox = edge_manager->increment_box_y(yMaxBox);
|
||||||
|
|
||||||
vec1.X = static_cast<float>(dirX) * edge_manager->AdvanceX + edge_manager->X;
|
vec1.X = static_cast<float>(dirX) * edge_manager->AdvanceX + edge_manager->MinX;
|
||||||
for (auto indexX = dirX; indexX <= xMaxBox; ++indexX)
|
for (auto indexX = dirX; indexX <= xMaxBox; ++indexX)
|
||||||
{
|
{
|
||||||
vec1.Y = static_cast<float>(dirY) * edge_manager->AdvanceY + edge_manager->Y;
|
vec1.Y = static_cast<float>(dirY) * edge_manager->AdvanceY + edge_manager->MinY;
|
||||||
for (int indexY = dirY; indexY <= yMaxBox; ++indexY)
|
for (int indexY = dirY; indexY <= yMaxBox; ++indexY)
|
||||||
{
|
{
|
||||||
auto vec1XAdv = vec1.X + edge_manager->AdvanceX;
|
auto vec1XAdv = vec1.X + edge_manager->AdvanceX;
|
||||||
|
@ -220,45 +217,45 @@ void TTableLayer::edges_insert_circle(circle_type* circle, TEdgeSegment* edge, f
|
||||||
ray.Direction.X = 1.0;
|
ray.Direction.X = 1.0;
|
||||||
ray.Direction.Y = 0.0;
|
ray.Direction.Y = 0.0;
|
||||||
ray.MaxDistance = edge_manager->AdvanceX;
|
ray.MaxDistance = edge_manager->AdvanceX;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.X = -1.0;
|
ray.Direction.X = -1.0;
|
||||||
ray.Origin.X = ray.Origin.X + edge_manager->AdvanceX;
|
ray.Origin.X = ray.Origin.X + edge_manager->AdvanceX;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.X = 0.0;
|
ray.Direction.X = 0.0;
|
||||||
ray.Direction.Y = 1.0;
|
ray.Direction.Y = 1.0;
|
||||||
ray.MaxDistance = edge_manager->AdvanceY;
|
ray.MaxDistance = edge_manager->AdvanceY;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.Y = -1.0;
|
ray.Direction.Y = -1.0;
|
||||||
ray.Origin.Y = ray.Origin.Y + edge_manager->AdvanceY;
|
ray.Origin.Y = ray.Origin.Y + edge_manager->AdvanceY;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.Y = 0.0;
|
ray.Direction.Y = 0.0;
|
||||||
ray.Direction.X = -1.0;
|
ray.Direction.X = -1.0;
|
||||||
ray.MaxDistance = edge_manager->AdvanceX;
|
ray.MaxDistance = edge_manager->AdvanceX;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.X = 1.0;
|
ray.Direction.X = 1.0;
|
||||||
ray.Origin.X = ray.Origin.X - edge_manager->AdvanceX;
|
ray.Origin.X = ray.Origin.X - edge_manager->AdvanceX;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.X = 0.0;
|
ray.Direction.X = 0.0;
|
||||||
ray.Direction.Y = -1.0;
|
ray.Direction.Y = -1.0;
|
||||||
ray.MaxDistance = edge_manager->AdvanceY;
|
ray.MaxDistance = edge_manager->AdvanceY;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ray.Direction.Y = 1.0;
|
ray.Direction.Y = 1.0;
|
||||||
ray.Origin.Y = ray.Origin.Y - edge_manager->AdvanceY;
|
ray.Origin.Y = ray.Origin.Y - edge_manager->AdvanceY;
|
||||||
if (maths::ray_intersect_circle(&ray, circle) < 1000000000.0f)
|
if (maths::ray_intersect_circle(ray, *circle) < 1000000000.0f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
collision = false;
|
collision = false;
|
||||||
|
|
|
@ -14,17 +14,17 @@ class TTableLayer :
|
||||||
public:
|
public:
|
||||||
TTableLayer(TPinballTable* table);
|
TTableLayer(TPinballTable* table);
|
||||||
~TTableLayer() override;
|
~TTableLayer() override;
|
||||||
int FieldEffect(TBall* ball, vector_type* vecDst) override;
|
int FieldEffect(TBall* ball, vector2* vecDst) override;
|
||||||
|
|
||||||
static void edges_insert_square(float y0, float x0, float y1, float x1, TEdgeSegment* edge,
|
static void edges_insert_square(float y0, float x0, float y1, float x1, TEdgeSegment* edge,
|
||||||
field_effect_type* field);
|
field_effect_type* field);
|
||||||
static void edges_insert_circle(circle_type* circle, TEdgeSegment* edge, field_effect_type* field);
|
static void edges_insert_circle(circle_type* circle, TEdgeSegment* edge, field_effect_type* field);
|
||||||
|
|
||||||
gdrv_bitmap8* VisBmp;
|
gdrv_bitmap8* VisBmp;
|
||||||
float Unknown1F;
|
float XMin;
|
||||||
float Unknown2F;
|
float YMin;
|
||||||
float Unknown3F;
|
float XMax;
|
||||||
float Unknown4F;
|
float YMax;
|
||||||
float GraityDirX;
|
float GraityDirX;
|
||||||
float GraityDirY;
|
float GraityDirY;
|
||||||
float GraityMult;
|
float GraityMult;
|
||||||
|
|
|
@ -18,8 +18,8 @@ TTextBox::TTextBox(TPinballTable* table, int groupIndex) : TPinballComponent(tab
|
||||||
Height = 0;
|
Height = 0;
|
||||||
BgBmp = render::background_bitmap;
|
BgBmp = render::background_bitmap;
|
||||||
Font = score::msg_fontp;
|
Font = score::msg_fontp;
|
||||||
Message1 = nullptr;
|
CurrentMessage = nullptr;
|
||||||
Message2 = nullptr;
|
PreviousMessage = nullptr;
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
|
|
||||||
if (groupIndex > 0)
|
if (groupIndex > 0)
|
||||||
|
@ -42,16 +42,16 @@ TTextBox::~TTextBox()
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
while (Message1)
|
while (CurrentMessage)
|
||||||
{
|
{
|
||||||
TTextBoxMessage* message = Message1;
|
TTextBoxMessage* message = CurrentMessage;
|
||||||
TTextBoxMessage* nextMessage = message->NextMessage;
|
TTextBoxMessage* nextMessage = message->NextMessage;
|
||||||
delete message;
|
delete message;
|
||||||
Message1 = nextMessage;
|
CurrentMessage = nextMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TTextBox::Message(int code, float value)
|
int TTextBox::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -59,19 +59,19 @@ int TTextBox::Message(int code, float value)
|
||||||
void TTextBox::TimerExpired(int timerId, void* caller)
|
void TTextBox::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto tb = static_cast<TTextBox*>(caller);
|
auto tb = static_cast<TTextBox*>(caller);
|
||||||
TTextBoxMessage* message = tb->Message1;
|
TTextBoxMessage* message = tb->CurrentMessage;
|
||||||
tb->Timer = 0;
|
tb->Timer = 0;
|
||||||
if (message)
|
if (message)
|
||||||
{
|
{
|
||||||
TTextBoxMessage* nextMessage = message->NextMessage;
|
TTextBoxMessage* nextMessage = message->NextMessage;
|
||||||
delete message;
|
delete message;
|
||||||
tb->Message1 = nextMessage;
|
tb->CurrentMessage = nextMessage;
|
||||||
tb->Draw();
|
tb->Draw();
|
||||||
control::handler(60, tb);
|
control::handler(MessageCode::ControlTimerExpired, tb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TTextBox::Clear()
|
void TTextBox::Clear(bool lowPriorityOnly)
|
||||||
{
|
{
|
||||||
gdrv_bitmap8* bmp = BgBmp;
|
gdrv_bitmap8* bmp = BgBmp;
|
||||||
if (bmp)
|
if (bmp)
|
||||||
|
@ -92,24 +92,25 @@ void TTextBox::Clear()
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
while (Message1)
|
while (CurrentMessage && (!lowPriorityOnly || CurrentMessage->LowPriority))
|
||||||
{
|
{
|
||||||
TTextBoxMessage* message = Message1;
|
auto message = CurrentMessage;
|
||||||
TTextBoxMessage* nextMessage = message->NextMessage;
|
CurrentMessage = message->NextMessage;
|
||||||
delete message;
|
delete message;
|
||||||
Message1 = nextMessage;
|
|
||||||
}
|
}
|
||||||
|
if (CurrentMessage)
|
||||||
|
Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TTextBox::Display(const char* text, float time)
|
void TTextBox::Display(const char* text, float time, bool lowPriority)
|
||||||
{
|
{
|
||||||
if (!text)
|
if (!text)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Message1 && !strcmp(text, Message2->Text))
|
if (CurrentMessage && !strcmp(text, PreviousMessage->Text))
|
||||||
{
|
{
|
||||||
Message2->Refresh(time);
|
PreviousMessage->Refresh(time);
|
||||||
if (Message2 == Message1)
|
if (PreviousMessage == CurrentMessage)
|
||||||
{
|
{
|
||||||
if (Timer && Timer != -1)
|
if (Timer && Timer != -1)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
|
@ -124,16 +125,14 @@ void TTextBox::Display(const char* text, float time)
|
||||||
if (Timer == -1)
|
if (Timer == -1)
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
auto message = new TTextBoxMessage(text, time);
|
auto message = new TTextBoxMessage(text, time, lowPriority);
|
||||||
if (message)
|
|
||||||
{
|
|
||||||
if (message->Text)
|
if (message->Text)
|
||||||
{
|
{
|
||||||
if (Message1)
|
if (CurrentMessage)
|
||||||
Message2->NextMessage = message;
|
PreviousMessage->NextMessage = message;
|
||||||
else
|
else
|
||||||
Message1 = message;
|
CurrentMessage = message;
|
||||||
Message2 = message;
|
PreviousMessage = message;
|
||||||
if (Timer == 0)
|
if (Timer == 0)
|
||||||
Draw();
|
Draw();
|
||||||
}
|
}
|
||||||
|
@ -142,7 +141,45 @@ void TTextBox::Display(const char* text, float time)
|
||||||
delete message;
|
delete message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TTextBox::DrawImGui()
|
||||||
|
{
|
||||||
|
// Do nothing when using a font (the text will be rendered to VScreen in TTextBox::Draw)
|
||||||
|
if (Font || !CurrentMessage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char windowName[64];
|
||||||
|
SDL_Rect rect;
|
||||||
|
ImGuiWindowFlags window_flags =
|
||||||
|
ImGuiWindowFlags_NoBackground |
|
||||||
|
ImGuiWindowFlags_NoDecoration |
|
||||||
|
ImGuiWindowFlags_NoSavedSettings |
|
||||||
|
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||||
|
ImGuiWindowFlags_NoInputs;
|
||||||
|
|
||||||
|
rect.x = OffsetX;
|
||||||
|
rect.y = OffsetY;
|
||||||
|
rect.w = Width;
|
||||||
|
rect.h = Height;
|
||||||
|
|
||||||
|
rect = fullscrn::GetScreenRectFromPinballRect(rect);
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(static_cast<float>(rect.x), static_cast<float>(rect.y)));
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(static_cast<float>(rect.w), static_cast<float>(rect.h)));
|
||||||
|
|
||||||
|
// Use the pointer to generate a window unique name per text box
|
||||||
|
snprintf(windowName, sizeof(windowName), "TTextBox_%p", static_cast<void*>(this));
|
||||||
|
if (ImGui::Begin(windowName, nullptr, window_flags))
|
||||||
|
{
|
||||||
|
ImGui::SetWindowFontScale(fullscrn::GetScreenToPinballRatio());
|
||||||
|
|
||||||
|
// ToDo: centered text in FT
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, pb::TextBoxColor);
|
||||||
|
ImGui::TextWrapped("%s", CurrentMessage->Text);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TTextBox::Draw()
|
void TTextBox::Draw()
|
||||||
|
@ -162,26 +199,26 @@ void TTextBox::Draw()
|
||||||
gdrv::fill_bitmap(render::vscreen, Width, Height, OffsetX, OffsetY, 0);
|
gdrv::fill_bitmap(render::vscreen, Width, Height, OffsetX, OffsetY, 0);
|
||||||
|
|
||||||
bool display = false;
|
bool display = false;
|
||||||
while (Message1)
|
while (CurrentMessage)
|
||||||
{
|
{
|
||||||
if (Message1->Time == -1.0f)
|
if (CurrentMessage->Time == -1.0f)
|
||||||
{
|
{
|
||||||
if (!Message1->NextMessage)
|
if (!CurrentMessage->NextMessage)
|
||||||
{
|
{
|
||||||
Timer = -1;
|
Timer = -1;
|
||||||
display = true;
|
display = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Message1->TimeLeft() >= -2.0f)
|
else if (CurrentMessage->TimeLeft() >= -2.0f)
|
||||||
{
|
{
|
||||||
Timer = timer::set(std::max(Message1->TimeLeft(), 0.25f), this, TimerExpired);
|
Timer = timer::set(std::max(CurrentMessage->TimeLeft(), 0.25f), this, TimerExpired);
|
||||||
display = true;
|
display = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tmp = Message1;
|
auto tmp = CurrentMessage;
|
||||||
Message1 = Message1->NextMessage;
|
CurrentMessage = CurrentMessage->NextMessage;
|
||||||
delete tmp;
|
delete tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,19 +226,13 @@ void TTextBox::Draw()
|
||||||
{
|
{
|
||||||
if (!Font)
|
if (!Font)
|
||||||
{
|
{
|
||||||
gdrv::grtext_draw_ttext_in_box(
|
// Immediate mode drawing using system font is handled by TTextBox::DrawImGui
|
||||||
Message1->Text,
|
|
||||||
render::vscreen->XPosition + OffsetX,
|
|
||||||
render::vscreen->YPosition + OffsetY,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
255);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LayoutResult> lines{};
|
std::vector<LayoutResult> lines{};
|
||||||
auto textHeight = 0;
|
auto textHeight = 0;
|
||||||
for (auto text = Message1->Text; ; textHeight += Font->Height)
|
for (auto text = CurrentMessage->Text; ; textHeight += Font->Height)
|
||||||
{
|
{
|
||||||
if (!text[0] || textHeight + Font->Height > Height)
|
if (!text[0] || textHeight + Font->Height > Height)
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -14,14 +14,15 @@ public:
|
||||||
int Timer;
|
int Timer;
|
||||||
gdrv_bitmap8* BgBmp;
|
gdrv_bitmap8* BgBmp;
|
||||||
score_msg_font_type* Font;
|
score_msg_font_type* Font;
|
||||||
TTextBoxMessage* Message1;
|
TTextBoxMessage* CurrentMessage;
|
||||||
TTextBoxMessage* Message2;
|
TTextBoxMessage* PreviousMessage;
|
||||||
|
|
||||||
TTextBox(TPinballTable* table, int groupIndex);
|
TTextBox(TPinballTable* table, int groupIndex);
|
||||||
~TTextBox() override;
|
~TTextBox() override;
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) override;
|
||||||
void Clear();
|
void Clear(bool lowPriorityOnly = false);
|
||||||
void Display(const char* text, float time);
|
void Display(const char* text, float time, bool lowPriority = false);
|
||||||
|
void DrawImGui();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct LayoutResult
|
struct LayoutResult
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
#include "TTextBoxMessage.h"
|
#include "TTextBoxMessage.h"
|
||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
|
|
||||||
TTextBoxMessage::TTextBoxMessage(const char* text, float time)
|
TTextBoxMessage::TTextBoxMessage(const char* text, float time, bool lowPriority)
|
||||||
{
|
{
|
||||||
NextMessage = nullptr;
|
NextMessage = nullptr;
|
||||||
Time = time;
|
Time = time;
|
||||||
EndTicks = pb::time_ticks + static_cast<int>(time * 1000.0f);
|
EndTicks = pb::time_ticks + static_cast<int>(time * 1000.0f);
|
||||||
|
LowPriority = lowPriority;
|
||||||
if (text)
|
if (text)
|
||||||
{
|
{
|
||||||
const auto textLen = strlen(text) + 1;
|
const auto textLen = strlen(text) + 1;
|
||||||
|
|
|
@ -6,8 +6,9 @@ public:
|
||||||
char* Text;
|
char* Text;
|
||||||
float Time;
|
float Time;
|
||||||
int EndTicks;
|
int EndTicks;
|
||||||
|
bool LowPriority;
|
||||||
|
|
||||||
TTextBoxMessage(const char* text, float time);
|
TTextBoxMessage(const char* text, float time, bool lowPriority);
|
||||||
~TTextBoxMessage();
|
~TTextBoxMessage();
|
||||||
float TimeLeft() const;
|
float TimeLeft() const;
|
||||||
void Refresh(float time);
|
void Refresh(float time);
|
||||||
|
|
|
@ -9,21 +9,25 @@ TTimer::TTimer(TPinballTable* table, int groupIndex) : TPinballComponent(table,
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TTimer::Message(int code, float value)
|
int TTimer::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
if (code == 59)
|
switch (code)
|
||||||
{
|
{
|
||||||
|
case MessageCode::TTimerResetTimer:
|
||||||
if (Timer)
|
if (Timer)
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = timer::set(value, this, TimerExpired);
|
Timer = timer::set(value, this, TimerExpired);
|
||||||
}
|
break;
|
||||||
else if (code == 1011 || code == 1022 || code == 1024)
|
case MessageCode::SetTiltLock:
|
||||||
{
|
case MessageCode::GameOver:
|
||||||
|
case MessageCode::Reset:
|
||||||
if (Timer)
|
if (Timer)
|
||||||
{
|
{
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
Timer = 0;
|
Timer = 0;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -33,5 +37,5 @@ void TTimer::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto timer = static_cast<TTimer*>(caller);
|
auto timer = static_cast<TTimer*>(caller);
|
||||||
timer->Timer = 0;
|
timer->Timer = 0;
|
||||||
control::handler(60, timer);
|
control::handler(MessageCode::ControlTimerExpired, timer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ class TTimer :
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TTimer(TPinballTable* table, int groupIndex);
|
TTimer(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);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int Timer;
|
int Timer;
|
||||||
|
|
|
@ -10,16 +10,16 @@ TTripwire::TTripwire(TPinballTable* table, int groupIndex) : TRollover(table, gr
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void TTripwire::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef,
|
void TTripwire::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||||
TEdgeSegment* edge)
|
TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
ball->Position.X = nextPosition->X;
|
ball->Position.X = nextPosition->X;
|
||||||
ball->Position.Y = nextPosition->Y;
|
ball->Position.Y = nextPosition->Y;
|
||||||
ball->RayMaxDistance -= coef;
|
ball->RayMaxDistance -= distance;
|
||||||
ball->not_again(edge);
|
ball->not_again(edge);
|
||||||
if (!PinballTable->TiltLockFlag)
|
if (!PinballTable->TiltLockFlag)
|
||||||
{
|
{
|
||||||
loader::play_sound(SoftHitSoundId);
|
loader::play_sound(SoftHitSoundId, ball, "TTripwire");
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ class TTripwire :
|
||||||
public:
|
public:
|
||||||
TTripwire(TPinballTable* table, int groupIndex);
|
TTripwire(TPinballTable* table, int groupIndex);
|
||||||
~TTripwire() override = default;
|
~TTripwire() 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;
|
TEdgeSegment* edge) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,14 +9,12 @@
|
||||||
TWall::TWall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
TWall::TWall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
|
||||||
{
|
{
|
||||||
if (RenderSprite)
|
if (RenderSprite)
|
||||||
render::sprite_set_bitmap(RenderSprite, nullptr);
|
SpriteSet(-1);
|
||||||
if (ListBitmap)
|
|
||||||
BmpPtr = ListBitmap->at(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TWall::Message(int code, float value)
|
int TWall::Message(MessageCode code, float value)
|
||||||
{
|
{
|
||||||
if (code == 1024 && Timer)
|
if (code == MessageCode::Reset && Timer)
|
||||||
{
|
{
|
||||||
timer::kill(Timer);
|
timer::kill(Timer);
|
||||||
TimerExpired(Timer, this);
|
TimerExpired(Timer, this);
|
||||||
|
@ -24,34 +22,23 @@ int TWall::Message(int code, float value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TWall::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge)
|
void TWall::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge)
|
||||||
{
|
{
|
||||||
if (DefaultCollision(ball, nextPosition, direction))
|
if (DefaultCollision(ball, nextPosition, direction))
|
||||||
{
|
{
|
||||||
if (BmpPtr)
|
if (ListBitmap)
|
||||||
{
|
{
|
||||||
render::sprite_set_bitmap(RenderSprite, BmpPtr);
|
SpriteSet(0);
|
||||||
Timer = timer::set(0.1f, this, TimerExpired);
|
Timer = timer::set(0.1f, this, TimerExpired);
|
||||||
}
|
}
|
||||||
control::handler(63, this);
|
control::handler(MessageCode::ControlCollision, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TWall::put_scoring(int index, int score)
|
|
||||||
{
|
|
||||||
if (index < 1)
|
|
||||||
Scores[index] = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TWall::get_scoring(int index)
|
|
||||||
{
|
|
||||||
return index < 1 ? Scores[index] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TWall::TimerExpired(int timerId, void* caller)
|
void TWall::TimerExpired(int timerId, void* caller)
|
||||||
{
|
{
|
||||||
auto wall = static_cast<TWall*>(caller);
|
auto wall = static_cast<TWall*>(caller);
|
||||||
render::sprite_set_bitmap(wall->RenderSprite, nullptr);
|
wall->SpriteSet(-1);
|
||||||
wall->Timer = 0;
|
wall->Timer = 0;
|
||||||
wall->MessageField = 0;
|
wall->MessageField = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,16 @@
|
||||||
|
|
||||||
#include "TCollisionComponent.h"
|
#include "TCollisionComponent.h"
|
||||||
|
|
||||||
struct gdrv_bitmap8;
|
|
||||||
|
|
||||||
class TWall :
|
class TWall :
|
||||||
public TCollisionComponent
|
public TCollisionComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TWall(TPinballTable* table, int groupIndex);
|
TWall(TPinballTable* table, int groupIndex);
|
||||||
int Message(int code, float value) override;
|
int Message(MessageCode code, float value) 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;
|
TEdgeSegment* edge) override;
|
||||||
void put_scoring(int index, int score) override;
|
|
||||||
int get_scoring(int index) override;
|
|
||||||
|
|
||||||
static void TimerExpired(int timerId, void* caller);
|
static void TimerExpired(int timerId, void* caller);
|
||||||
|
|
||||||
int Timer{};
|
int Timer{};
|
||||||
gdrv_bitmap8* BmpPtr{};
|
|
||||||
int Scores[1]{};
|
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class TCollisionComponent;
|
||||||
|
class TBall;
|
||||||
|
enum class MessageCode;
|
||||||
|
class TSink;
|
||||||
class TLight;
|
class TLight;
|
||||||
class TSound;
|
class TSound;
|
||||||
class TPinballTable;
|
class TPinballTable;
|
||||||
class TPinballComponent;
|
class TPinballComponent;
|
||||||
|
enum class Msg : int;
|
||||||
|
|
||||||
struct component_tag_base
|
struct component_tag_base
|
||||||
{
|
{
|
||||||
|
@ -26,9 +31,8 @@ struct component_tag : component_tag_base
|
||||||
static_assert(std::is_base_of<TPinballComponent, T>::value, "T must inherit from TPinballComponent");
|
static_assert(std::is_base_of<TPinballComponent, T>::value, "T must inherit from TPinballComponent");
|
||||||
T* Component;
|
T* Component;
|
||||||
|
|
||||||
component_tag(LPCSTR name, TPinballComponent* component): component_tag_base(name), Component(nullptr)
|
component_tag(LPCSTR name) : component_tag_base(name), Component(nullptr)
|
||||||
{
|
{
|
||||||
component_tag::SetComponent(component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TPinballComponent* GetComponent() override
|
TPinballComponent* GetComponent() override
|
||||||
|
@ -47,14 +51,14 @@ struct component_tag : component_tag_base
|
||||||
|
|
||||||
struct component_control
|
struct component_control
|
||||||
{
|
{
|
||||||
void (* ControlFunc)(int, TPinballComponent*);
|
void (& ControlFunc)(MessageCode, TPinballComponent*);
|
||||||
int ScoreCount;
|
const unsigned int ScoreCount;
|
||||||
int* Scores;
|
const int* Scores;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct component_info
|
struct component_info
|
||||||
{
|
{
|
||||||
component_tag_base* Tag;
|
component_tag_base& Tag;
|
||||||
component_control Control;
|
component_control Control;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,125 +68,128 @@ class control
|
||||||
public:
|
public:
|
||||||
static TPinballTable* TableG;
|
static TPinballTable* TableG;
|
||||||
static component_info score_components[88];
|
static component_info score_components[88];
|
||||||
static component_tag_base* simple_components[142];
|
static component_tag_base* simple_components[145];
|
||||||
static int waiting_deployment_flag;
|
static int waiting_deployment_flag;
|
||||||
static bool table_unlimited_balls;
|
static bool table_unlimited_balls, easyMode;
|
||||||
static int RankRcArray[9], MissionRcArray[17], mission_select_scores[17];
|
static Msg RankRcArray[9], MissionRcArray[17];
|
||||||
static component_tag_base *wormhole_tag_array1[3], *wormhole_tag_array2[3], *wormhole_tag_array3[3];
|
static int mission_select_scores[17];
|
||||||
|
static std::reference_wrapper<TSink*> WormholeSinkArray[3];
|
||||||
|
static std::reference_wrapper<TLight*> WormholeLightArray1[3], WormholeLightArray2[3];
|
||||||
|
|
||||||
static void make_links(TPinballTable* table);
|
static void make_links(TPinballTable* table);
|
||||||
static void ClearLinks();
|
static void ClearLinks();
|
||||||
static TPinballComponent* make_component_link(component_tag_base* tag);
|
static TPinballComponent* make_component_link(component_tag_base& tag);
|
||||||
static void handler(int code, TPinballComponent* cmp);
|
static void handler(MessageCode code, TPinballComponent* cmp);
|
||||||
static void pbctrl_bdoor_controller(char key);
|
static void pbctrl_bdoor_controller(char key);
|
||||||
static void table_add_extra_ball(float count);
|
static void table_add_extra_ball(float count);
|
||||||
static void table_set_bonus_hold();
|
static void table_set_bonus_hold();
|
||||||
static void table_set_bonus();
|
static void table_set_bonus();
|
||||||
static void table_set_jackpot();
|
static void table_set_jackpot();
|
||||||
static void table_set_flag_lights();
|
static void table_set_flag_lights();
|
||||||
static void table_set_multiball();
|
static void table_set_multiball(float time);
|
||||||
static void table_bump_ball_sink_lock();
|
static void table_bump_ball_sink_lock();
|
||||||
static void table_set_replay(float value);
|
static void table_set_replay(float value);
|
||||||
|
static void UnstuckBall(TBall& ball, int dt);
|
||||||
|
static bool CheckBallInControlBounds(const TBall& ball, const TCollisionComponent& cmp);
|
||||||
static void cheat_bump_rank();
|
static void cheat_bump_rank();
|
||||||
static bool light_on(component_tag<TLight>* tag);
|
static int SpecialAddScore(int score, bool mission = false);
|
||||||
static int SpecialAddScore(int score);
|
|
||||||
static int AddRankProgress(int rank);
|
static int AddRankProgress(int rank);
|
||||||
static void AdvanceWormHoleDestination(int flag);
|
static void AdvanceWormHoleDestination(int flag);
|
||||||
|
|
||||||
static void FlipperRebounderControl1(int code, TPinballComponent* caller);
|
static void FlipperRebounderControl1(MessageCode code, TPinballComponent* caller);
|
||||||
static void FlipperRebounderControl2(int code, TPinballComponent* caller);
|
static void FlipperRebounderControl2(MessageCode code, TPinballComponent* caller);
|
||||||
static void RebounderControl(int code, TPinballComponent* caller);
|
static void RebounderControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BumperControl(int code, TPinballComponent* caller);
|
static void BumperControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LeftKickerControl(int code, TPinballComponent* caller);
|
static void LeftKickerControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void RightKickerControl(int code, TPinballComponent* caller);
|
static void RightKickerControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LeftKickerGateControl(int code, TPinballComponent* caller);
|
static void LeftKickerGateControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void RightKickerGateControl(int code, TPinballComponent* caller);
|
static void RightKickerGateControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void DeploymentChuteToEscapeChuteOneWayControl(int code, TPinballComponent* caller);
|
static void DeploymentChuteToEscapeChuteOneWayControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void DeploymentChuteToTableOneWayControl(int code, TPinballComponent* caller);
|
static void DeploymentChuteToTableOneWayControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void DrainBallBlockerControl(int code, TPinballComponent* caller);
|
static void DrainBallBlockerControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LaunchRampControl(int code, TPinballComponent* caller);
|
static void LaunchRampControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LaunchRampHoleControl(int code, TPinballComponent* caller);
|
static void LaunchRampHoleControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void SpaceWarpRolloverControl(int code, TPinballComponent* caller);
|
static void SpaceWarpRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void ReentryLanesRolloverControl(int code, TPinballComponent* caller);
|
static void ReentryLanesRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BumperGroupControl(int code, TPinballComponent* caller);
|
static void BumperGroupControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LaunchLanesRolloverControl(int code, TPinballComponent* caller);
|
static void LaunchLanesRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void OutLaneRolloverControl(int code, TPinballComponent* caller);
|
static void OutLaneRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void ExtraBallLightControl(int code, TPinballComponent* caller);
|
static void ExtraBallLightControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void ReturnLaneRolloverControl(int code, TPinballComponent* caller);
|
static void ReturnLaneRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BonusLaneRolloverControl(int code, TPinballComponent* caller);
|
static void BonusLaneRolloverControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover1Control(int code, TPinballComponent* caller);
|
static void FuelRollover1Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover2Control(int code, TPinballComponent* caller);
|
static void FuelRollover2Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover3Control(int code, TPinballComponent* caller);
|
static void FuelRollover3Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover4Control(int code, TPinballComponent* caller);
|
static void FuelRollover4Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover5Control(int code, TPinballComponent* caller);
|
static void FuelRollover5Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelRollover6Control(int code, TPinballComponent* caller);
|
static void FuelRollover6Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void HyperspaceLightGroupControl(int code, TPinballComponent* caller);
|
static void HyperspaceLightGroupControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void WormHoleControl(int code, TPinballComponent* caller);
|
static void WormHoleControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LeftFlipperControl(int code, TPinballComponent* caller);
|
static void LeftFlipperControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void RightFlipperControl(int code, TPinballComponent* caller);
|
static void RightFlipperControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void JackpotLightControl(int code, TPinballComponent* caller);
|
static void JackpotLightControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BonusLightControl(int code, TPinballComponent* caller);
|
static void BonusLightControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BoosterTargetControl(int code, TPinballComponent* caller);
|
static void BoosterTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MedalLightGroupControl(int code, TPinballComponent* caller);
|
static void MedalLightGroupControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MultiplierLightGroupControl(int code, TPinballComponent* caller);
|
static void MultiplierLightGroupControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void FuelSpotTargetControl(int code, TPinballComponent* caller);
|
static void FuelSpotTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MissionSpotTargetControl(int code, TPinballComponent* caller);
|
static void MissionSpotTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void LeftHazardSpotTargetControl(int code, TPinballComponent* caller);
|
static void LeftHazardSpotTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void RightHazardSpotTargetControl(int code, TPinballComponent* caller);
|
static void RightHazardSpotTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void WormHoleDestinationControl(int code, TPinballComponent* caller);
|
static void WormHoleDestinationControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BlackHoleKickoutControl(int code, TPinballComponent* caller);
|
static void BlackHoleKickoutControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void FlagControl(int code, TPinballComponent* caller);
|
static void FlagControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void GravityWellKickoutControl(int code, TPinballComponent* caller);
|
static void GravityWellKickoutControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate1Control(int code, TPinballComponent* caller);
|
static void SkillShotGate1Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate2Control(int code, TPinballComponent* caller);
|
static void SkillShotGate2Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate3Control(int code, TPinballComponent* caller);
|
static void SkillShotGate3Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate4Control(int code, TPinballComponent* caller);
|
static void SkillShotGate4Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate5Control(int code, TPinballComponent* caller);
|
static void SkillShotGate5Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void SkillShotGate6Control(int code, TPinballComponent* caller);
|
static void SkillShotGate6Control(MessageCode code, TPinballComponent* caller);
|
||||||
static void ShootAgainLightControl(int code, TPinballComponent* caller);
|
static void ShootAgainLightControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void EscapeChuteSinkControl(int code, TPinballComponent* caller);
|
static void EscapeChuteSinkControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MissionControl(int code, TPinballComponent* caller);
|
static void MissionControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void HyperspaceKickOutControl(int code, TPinballComponent* caller);
|
static void HyperspaceKickOutControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void PlungerControl(int code, TPinballComponent* caller);
|
static void PlungerControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MedalTargetControl(int code, TPinballComponent* caller);
|
static void MedalTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void MultiplierTargetControl(int code, TPinballComponent* caller);
|
static void MultiplierTargetControl(MessageCode code, TPinballComponent* caller);
|
||||||
static void BallDrainControl(int code, TPinballComponent* caller);
|
static void BallDrainControl(MessageCode code, TPinballComponent* caller);
|
||||||
|
|
||||||
static void table_control_handler(int code);
|
static void table_control_handler(MessageCode code);
|
||||||
|
|
||||||
static void AlienMenaceController(int code, TPinballComponent* caller);
|
static void AlienMenaceController(MessageCode code, TPinballComponent* caller);
|
||||||
static void AlienMenacePartTwoController(int code, TPinballComponent* caller);
|
static void AlienMenacePartTwoController(MessageCode code, TPinballComponent* caller);
|
||||||
static void BlackHoleThreatController(int code, TPinballComponent* caller);
|
static void BlackHoleThreatController(MessageCode code, TPinballComponent* caller);
|
||||||
static void BugHuntController(int code, TPinballComponent* caller);
|
static void BugHuntController(MessageCode code, TPinballComponent* caller);
|
||||||
static void CosmicPlagueController(int code, TPinballComponent* caller);
|
static void CosmicPlagueController(MessageCode code, TPinballComponent* caller);
|
||||||
static void CosmicPlaguePartTwoController(int code, TPinballComponent* caller);
|
static void CosmicPlaguePartTwoController(MessageCode code, TPinballComponent* caller);
|
||||||
static void DoomsdayMachineController(int code, TPinballComponent* caller);
|
static void DoomsdayMachineController(MessageCode code, TPinballComponent* caller);
|
||||||
static void GameoverController(int code, TPinballComponent* caller);
|
static void GameoverController(MessageCode code, TPinballComponent* caller);
|
||||||
static void LaunchTrainingController(int code, TPinballComponent* caller);
|
static void LaunchTrainingController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromController(int code, TPinballComponent* caller);
|
static void MaelstromController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartEightController(int code, TPinballComponent* caller);
|
static void MaelstromPartEightController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartFiveController(int code, TPinballComponent* caller);
|
static void MaelstromPartFiveController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartFourController(int code, TPinballComponent* caller);
|
static void MaelstromPartFourController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartSevenController(int code, TPinballComponent* caller);
|
static void MaelstromPartSevenController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartSixController(int code, TPinballComponent* caller);
|
static void MaelstromPartSixController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartThreeController(int code, TPinballComponent* caller);
|
static void MaelstromPartThreeController(MessageCode code, TPinballComponent* caller);
|
||||||
static void MaelstromPartTwoController(int code, TPinballComponent* caller);
|
static void MaelstromPartTwoController(MessageCode code, TPinballComponent* caller);
|
||||||
static void PracticeMissionController(int code, TPinballComponent* caller);
|
static void PracticeMissionController(MessageCode code, TPinballComponent* caller);
|
||||||
static void ReconnaissanceController(int code, TPinballComponent* caller);
|
static void ReconnaissanceController(MessageCode code, TPinballComponent* caller);
|
||||||
static void ReentryTrainingController(int code, TPinballComponent* caller);
|
static void ReentryTrainingController(MessageCode code, TPinballComponent* caller);
|
||||||
static void RescueMissionController(int code, TPinballComponent* caller);
|
static void RescueMissionController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SatelliteController(int code, TPinballComponent* caller);
|
static void SatelliteController(MessageCode code, TPinballComponent* caller);
|
||||||
static void ScienceMissionController(int code, TPinballComponent* caller);
|
static void ScienceMissionController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SecretMissionGreenController(int code, TPinballComponent* caller);
|
static void SecretMissionGreenController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SecretMissionRedController(int code, TPinballComponent* caller);
|
static void SecretMissionRedController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SecretMissionYellowController(int code, TPinballComponent* caller);
|
static void SecretMissionYellowController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SelectMissionController(int code, TPinballComponent* caller);
|
static void SelectMissionController(MessageCode code, TPinballComponent* caller);
|
||||||
static void SpaceRadiationController(int code, TPinballComponent* caller);
|
static void SpaceRadiationController(MessageCode code, TPinballComponent* caller);
|
||||||
static void StrayCometController(int code, TPinballComponent* caller);
|
static void StrayCometController(MessageCode code, TPinballComponent* caller);
|
||||||
static void TimeWarpController(int code, TPinballComponent* caller);
|
static void TimeWarpController(MessageCode code, TPinballComponent* caller);
|
||||||
static void TimeWarpPartTwoController(int code, TPinballComponent* caller);
|
static void TimeWarpPartTwoController(MessageCode code, TPinballComponent* caller);
|
||||||
static void UnselectMissionController(int code, TPinballComponent* caller);
|
static void UnselectMissionController(MessageCode code, TPinballComponent* caller);
|
||||||
static void WaitingDeploymentController(int code, TPinballComponent* caller);
|
static void WaitingDeploymentController(MessageCode code, TPinballComponent* caller);
|
||||||
private:
|
private:
|
||||||
static int extraball_light_flag;
|
static int extraball_light_flag;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include "pch.h"
|
||||||
|
#include "font_selection.h"
|
||||||
|
|
||||||
|
#include "options.h"
|
||||||
|
#include "pb.h"
|
||||||
|
#include "score.h"
|
||||||
|
#include "winmain.h"
|
||||||
|
#include "translations.h"
|
||||||
|
|
||||||
|
static const char* popupName = "Font Selection";
|
||||||
|
bool font_selection::ShowDialogFlag = false;
|
||||||
|
char font_selection::DialogInputBuffer[512];
|
||||||
|
|
||||||
|
void font_selection::ShowDialog()
|
||||||
|
{
|
||||||
|
ShowDialogFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_selection::RenderDialog()
|
||||||
|
{
|
||||||
|
if (ShowDialogFlag == true)
|
||||||
|
{
|
||||||
|
strncpy(DialogInputBuffer, options::Options.FontFileName.V.c_str(), sizeof(DialogInputBuffer));
|
||||||
|
ShowDialogFlag = false;
|
||||||
|
if (!ImGui::IsPopupOpen(popupName))
|
||||||
|
{
|
||||||
|
ImGui::OpenPopup(popupName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unused_open = true;
|
||||||
|
if (ImGui::BeginPopupModal(popupName, &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
ImGui::Text("Font file to use: ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::InputText("##Font", DialogInputBuffer, IM_ARRAYSIZE(DialogInputBuffer));
|
||||||
|
|
||||||
|
if (ImGui::Button(pb::get_rc_string(Msg::GenericOk)))
|
||||||
|
{
|
||||||
|
options::Options.FontFileName.V = DialogInputBuffer;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
winmain::Restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(pb::get_rc_string(Msg::GenericCancel)))
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class font_selection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void ShowDialog();
|
||||||
|
static void RenderDialog();
|
||||||
|
private:
|
||||||
|
static bool ShowDialogFlag;
|
||||||
|
static char DialogInputBuffer[512];
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue