├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── cameracommands ├── cameracommand.cpp ├── cameracommand.h ├── gforcecameracommand.cpp ├── gforcecameracommand.h ├── groundrollcameracommand.cpp ├── groundrollcameracommand.h ├── levelheadcameracommand.cpp ├── levelheadcameracommand.h ├── lookaheadcameracommand.cpp ├── lookaheadcameracommand.h ├── pistonenginecameracommand.cpp ├── pistonenginecameracommand.h ├── rotorcameracommand.cpp ├── rotorcameracommand.h ├── taxilookaheadcameracommand.cpp ├── taxilookaheadcameracommand.h ├── touchdowncameracommand.cpp └── touchdowncameracommand.h ├── cameracontrol.cpp ├── cameracontrol.h ├── cameraposition.h ├── collisions ├── helpers.cpp ├── helpers.h ├── interfaces ├── ivisitable.h └── ivisitor.h ├── main.cpp ├── menu.cpp ├── menu.h ├── settings ├── settingsreader.cpp ├── settingsreader.h ├── settingswriter.cpp └── settingswriter.h └── windows.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | doc 4 | .vscode 5 | *.sh 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | Stable release: v1.14 3 | -------------------- 4 | - Compatibility with X-Plane 12 gLoadedCamera 5 | 6 | Stable release: v1.13 7 | -------------------- 8 | - Support for Apple ARM architecture 9 | 10 | Stable release: v1.12 11 | -------------------- 12 | - Y-Axis camera shake for piston engines 13 | - Fix for automatic roll reset 14 | 15 | Stable release: v1.11 16 | -------------------- 17 | - GForceCameraCommand: added a new dataref to control general sensitivity 18 | 19 | Stable release: v1.10 20 | -------------------- 21 | - GForceCameraCommand & GroundRollCameraCommand: fixed the vibrations that may occur while braking on some airplanes 22 | 23 | Stable release: v1.9 24 | -------------------- 25 | - Compatibility with X-Plane 11 glance views 26 | - Removed the 32 build 27 | 28 | Stable release: v1.8 29 | -------------------- 30 | - Compatibility with X-Plane 11 new quick looks 31 | - Minor fixes 32 | 33 | Stable release: v1.7 34 | -------------------- 35 | - Minor fixes 36 | 37 | Stable release: v1.6 38 | -------------------- 39 | - Taxi Lookahead 40 | 41 | Stable release: v1.5 42 | -------------------- 43 | - Bug Fixes 44 | 45 | Stable release: v1.1 46 | -------------------- 47 | - Introduced the LookAhead feature 48 | 49 | Stable release: v1.0 50 | -------------------- 51 | - Better multimonitor compatibility 52 | - Improved ground roll and touch down effects 53 | 54 | Release Candidate 55 | ----------------- 56 | - The maximum value of each effect is now doubled 57 | - Better settings UI 58 | - Improved Mac OS compatibility 59 | - Improved g-force and ground roll effects 60 | - The settings file is saved in the sim preferences folder 61 | - Added the touchdown effect 62 | - The effects are disabled while the sim is paused 63 | 64 | Beta 2 65 | ------ 66 | - Mac version fixed: it was not running at all 67 | - Improved joystick compatibility 68 | 69 | Beta 1 70 | ------ 71 | - Initial release 72 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | 3 | set(CMAKE_C_COMPILER "gcc") 4 | set(CMAKE_CXX_COMPILER "g++") 5 | 6 | #set(CMAKE_CXX_STANDARD 11) 7 | #set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | project (HeadShake) 10 | 11 | # Options 12 | option(CMP_LIN "CMP_LIN" OFF) 13 | option(CMP_WIN_64 "CMP_WIN_64" OFF) 14 | option(CMP_MAC "CMP_MAC" OFF) 15 | 16 | # Set the libraries path: edit this line to specify your custom library path 17 | get_filename_component(SDK_PATH "../SDK/" ABSOLUTE) 18 | message("-- Detecting X-Plane SDK path") 19 | if(NOT EXISTS ${SDK_PATH}) 20 | message(FATAL_ERROR "Missing SDK folder: ${SDK_PATH}") 21 | endif(NOT EXISTS ${SDK_PATH}) 22 | 23 | configure_file("src/collisions" "collisions" COPYONLY) 24 | 25 | # Set the default compiler settings 26 | set (GCC_DEFAULT_FLAGS -std=c++0x -Wall -Wunreachable-code -pipe -Wextra -Wshadow -Wfloat-equal -pedantic -fvisibility=hidden -O2 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -W -DXPLM200 -DXPLM210) 27 | 28 | # Include files 29 | include_directories ("./src") 30 | include_directories ("${SDK_PATH}") 31 | include_directories ("${SDK_PATH}/CHeaders/XPLM") 32 | include_directories ("${SDK_PATH}/CHeaders/Wrappers") 33 | include_directories ("${SDK_PATH}/CHeaders/Widgets") 34 | file(GLOB_RECURSE SOURCES "src/*.cpp") 35 | 36 | if(CMP_LIN) 37 | # ------------------ 38 | # Release - Linux 64 39 | # ------------------ 40 | add_library(LIN_RELEASE_64 SHARED ${SOURCES}) 41 | 42 | target_compile_options(LIN_RELEASE_64 PRIVATE ${GCC_DEFAULT_FLAGS} -m64 -fPIC -DLIN) 43 | set_target_properties(LIN_RELEASE_64 PROPERTIES LINK_FLAGS "-Wl,--version-script=collisions") 44 | set_target_properties(LIN_RELEASE_64 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "../bin/Release/HeadShake/64/") 45 | set_target_properties(LIN_RELEASE_64 PROPERTIES PREFIX "") 46 | set_target_properties(LIN_RELEASE_64 PROPERTIES OUTPUT_NAME "lin.xpl") 47 | set_target_properties(LIN_RELEASE_64 PROPERTIES SUFFIX "") 48 | 49 | # ------------------ 50 | # Debug - Linux 64 51 | # ------------------ 52 | add_library(LIN_DEBUG_64 SHARED ${SOURCES}) 53 | 54 | target_compile_options(LIN_DEBUG_64 PRIVATE ${GCC_DEFAULT_FLAGS} -m64 -fPIC -g -DLIN) 55 | set_target_properties(LIN_DEBUG_64 PROPERTIES LINK_FLAGS "-Wl,--version-script=collisions") 56 | set_target_properties(LIN_DEBUG_64 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "../bin/Debug/HeadShake/64/") 57 | set_target_properties(LIN_DEBUG_64 PROPERTIES PREFIX "") 58 | set_target_properties(LIN_DEBUG_64 PROPERTIES OUTPUT_NAME "lin.xpl") 59 | set_target_properties(LIN_DEBUG_64 PROPERTIES SUFFIX "") 60 | endif(CMP_LIN) 61 | 62 | if(CMP_WIN) 63 | cmake_policy(SET CMP0015 NEW) 64 | set (CMAKE_SYSTEM_NAME Windows) 65 | set (CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 66 | link_directories("${SDK_PATH}/Libraries/Win") 67 | 68 | # -------------------- 69 | # Release - Windows 64 70 | # -------------------- 71 | add_library(WIN_RELEASE_64 SHARED ${SOURCES}) 72 | 73 | find_package(Threads REQUIRED) 74 | target_compile_options(WIN_RELEASE_64 PRIVATE ${GCC_DEFAULT_FLAGS} -s -c -fno-stack-protector -static-libgcc -static-libstdc++ -DXPLM200 -DIBM) 75 | target_link_libraries(WIN_RELEASE_64 XPLM_64 XPWidgets_64 -static-libgcc -static-libstdc++ -static ${CMAKE_THREAD_LIBS_INIT}) 76 | set_target_properties(WIN_RELEASE_64 PROPERTIES LINK_FLAGS "-s -Wl,--version-script=collisions") 77 | set_target_properties(WIN_RELEASE_64 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "../bin/Release/HeadShake/64/") 78 | set_target_properties(WIN_RELEASE_64 PROPERTIES PREFIX "") 79 | set_target_properties(WIN_RELEASE_64 PROPERTIES OUTPUT_NAME "win.xpl") 80 | set_target_properties(WIN_RELEASE_64 PROPERTIES SUFFIX "") 81 | endif(CMP_WIN) 82 | 83 | if (CMP_MAC) 84 | cmake_policy(SET CMP0042 NEW) 85 | set (CMAKE_SYSTEM_NAME Darwin) 86 | set (CMAKE_CXX_COMPILER g++) 87 | set (CMAKE_OSX_ARCHITECTURES "x86_64;arm64") 88 | 89 | # -------------------- 90 | # Release - Mac 64 91 | # -------------------- 92 | add_library(MAC_RELEASE_64 SHARED ${SOURCES}) 93 | 94 | target_compile_options(MAC_RELEASE_64 PRIVATE ${GCC_DEFAULT_FLAGS} -nostdinc++ -I/Library/Developer/CommandLineTools/usr/include/c++/v1 -DXPLM200 -DAPL) 95 | target_link_libraries(MAC_RELEASE_64 "-F${SDK_PATH}/Libraries/Mac" "-framework XPLM" "-framework XPWidgets" "-framework OpenGL" "-nodefaultlibs" "-lc++" "-lc++abi" "-lm" "-lc") 96 | set_target_properties(MAC_RELEASE_64 PROPERTIES LINK_FLAGS "-m64 -fvisibility=hidden") 97 | set_target_properties(MAC_RELEASE_64 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "../bin/Release/HeadShake/64/") 98 | set_target_properties(MAC_RELEASE_64 PROPERTIES PREFIX "") 99 | set_target_properties(MAC_RELEASE_64 PROPERTIES OUTPUT_NAME "mac.xpl") 100 | set_target_properties(MAC_RELEASE_64 PROPERTIES SUFFIX "") 101 | endif (CMP_MAC) 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeadShake plugin for X-Plane 10/11/12 2 | 3 | HeadShake is an X-Plane camera plugin wich adds some nice POV effects while flying in virtual cockpit view. 4 | It is free, opensource and available for Windows, Linux and Mac (Intel and M CPUs). 5 | This plugin was developed by [SimCoders.com](https://www.simcoders.com) and maintained by the community. 6 | 7 | # Download 8 | 9 | You can download the plugin from https://www.simcoders.com/headshake/features-and-download 10 | 11 | # Compile the source code 12 | 13 | To compile the source code you need cmake and g++ (or MinGW). 14 | 15 | Make sure to specify the [SDK](http://www.xsquawkbox.net/xpsdk/mediawiki/Main_Page) folder path in the CMakeLists.txt file. 16 | 17 | Type the following commands in your favourite unix shell. 18 | 19 | ``` 20 | cd xp-plugins-headshake 21 | mkdir build 22 | cd build 23 | cmake -DCMP_LIN=ON .. 24 | make 25 | ``` 26 | 27 | Use `CMP_LIN=ON` to compile the Linux version, `CMP_WIN=ON` to compile the Windows version using MinGW or `CMP_MAC=ON` to compile the Mac OSX version. 28 | -------------------------------------------------------------------------------- /src/cameracommands/cameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include "cameracommands/cameracommand.h" 2 | #include "cameraposition.h" 3 | 4 | CameraCommand::CameraCommand() 5 | { 6 | pEnabled = true; 7 | mBlendTime = 1; 8 | } 9 | 10 | CameraCommand::~CameraCommand() 11 | { 12 | 13 | } 14 | 15 | bool CameraCommand::is_enabled() 16 | { 17 | return pEnabled; 18 | } 19 | 20 | 21 | void CameraCommand::set_enabled(bool enabled) 22 | { 23 | pEnabled = enabled; 24 | } 25 | 26 | void CameraCommand::reset_blend() 27 | { 28 | mBlendTime = 0; 29 | } 30 | 31 | void CameraCommand::toggle() 32 | { 33 | pEnabled = !pEnabled; 34 | } 35 | 36 | void CameraCommand::execute(CameraPosition&, float elapsedTime) 37 | { 38 | if (mBlendTime < 2) { 39 | mBlendTime += elapsedTime; 40 | } 41 | } 42 | 43 | // Executed when the view type changes 44 | void CameraCommand::on_view_changed(int) 45 | { 46 | 47 | } 48 | 49 | void CameraCommand::on_enable() 50 | { 51 | 52 | } 53 | 54 | void CameraCommand::on_disable() 55 | { 56 | 57 | } 58 | 59 | float CameraCommand::get_blend_ratio() const 60 | { 61 | return mBlendTime / 2; 62 | } 63 | -------------------------------------------------------------------------------- /src/cameracommands/cameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERACOMMAND_H 2 | #define CAMERACOMMAND_H 3 | 4 | #include "interfaces/ivisitable.h" 5 | #include "cameraposition.h" 6 | 7 | class CameraCommand : public IVisitable 8 | { 9 | public: 10 | /** Default constructor */ 11 | CameraCommand(); 12 | /** Default destructor */ 13 | virtual ~CameraCommand(); 14 | virtual bool is_enabled(); 15 | virtual void set_enabled(bool); 16 | virtual void toggle(); 17 | virtual void execute(CameraPosition&, float); 18 | virtual void accept(IVisitor&) = 0; 19 | virtual void on_view_changed(int); 20 | virtual void on_enable(); 21 | virtual void on_disable(); 22 | void reset_blend(); 23 | protected: 24 | float get_blend_ratio() const; 25 | bool pEnabled; 26 | private: 27 | float mBlendTime; 28 | }; 29 | 30 | #endif // CAMERACOMMAND_H 31 | -------------------------------------------------------------------------------- /src/cameracommands/gforcecameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cameracommands/gforcecameracommand.h" 9 | #include "helpers.h" 10 | #include "cameraposition.h" 11 | #include "interfaces/ivisitor.h" 12 | 13 | #define MSG_ADD_DATAREF 0x01000000 14 | 15 | GForceCameraCommand::GForceCameraCommand() 16 | { 17 | // Setup the datarefs 18 | mGNormalDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_nrml"); 19 | mGAxialDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_axil"); 20 | mGSideDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_side"); 21 | mRadioAltDataRef = XPLMFindDataRef("sim/flightmodel/position/y_agl"); 22 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 23 | mBrakesDataRef = XPLMFindDataRef("sim/cockpit2/controls/parking_brake_ratio"); 24 | mGSDataRef = XPLMFindDataRef("sim/flightmodel/position/groundspeed"); 25 | // Setup the private vars 26 | mDamper = 5; 27 | mZResponse = 25; 28 | mYawResponse = 25; 29 | mPitchResponse = 25; 30 | mLastZ = 0; 31 | mLastX = 0; 32 | mLastYaw = 0; 33 | mLastPitch = 0; 34 | mLastRoll = 0; 35 | mLastNormG = 1; 36 | mLastAxialG = 0; 37 | mGeneralSensitivity = 1; 38 | } 39 | 40 | GForceCameraCommand::~GForceCameraCommand() 41 | { 42 | 43 | } 44 | 45 | void GForceCameraCommand::on_enable() 46 | { 47 | int datarefEditorId = XPLMFindPluginBySignature("xplanesdk.examples.DataRefEditor"); 48 | 49 | // Publish the drefs 50 | mGeneralSensitivityDataRef = XPLMRegisterDataAccessor( 51 | "simcoders/headshake/gforce/sensitivity", xplmType_Float, 1, NULL, NULL, 52 | [](void* refCon) -> float { 53 | if (refCon) return reinterpret_cast(refCon)->mGeneralSensitivity; 54 | return 0; 55 | }, [](void* refCon, float value) -> void { 56 | if (refCon) { 57 | if (value < 0) { 58 | value = 0; 59 | } 60 | if (value > 1) { 61 | value = 1; 62 | } 63 | reinterpret_cast(refCon)->mGeneralSensitivity = value; 64 | } 65 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this); 66 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/gforce/sensitivity"); 67 | } 68 | 69 | void GForceCameraCommand::on_disable() 70 | { 71 | XPLMUnregisterDataAccessor(mGeneralSensitivityDataRef); 72 | } 73 | 74 | void GForceCameraCommand::execute(CameraPosition &position, float elapsedTime) 75 | { 76 | CameraCommand::execute(position, elapsedTime); 77 | 78 | const float maxAlt = 20, minAlt = 1; // Meters 79 | const unsigned int maxDamper = 30, minDamper = 5; 80 | float acc, currentG, aglAlt; 81 | bool filter = XPLMGetDatai(mOnGroundDataRef) == 1 && XPLMGetDataf(mBrakesDataRef) > 0; 82 | float gs = XPLMGetDataf(mGSDataRef); 83 | 84 | // Restore the initial position 85 | position.z -= mLastZ; 86 | position.y -= -mLastZ / 2.0f; 87 | position.x -= mLastX; 88 | position.yaw -= mLastYaw; 89 | position.pitch -= mLastPitch; 90 | position.roll -= mLastRoll; 91 | 92 | // Reset the state vars 93 | mLastZ = 0; 94 | mLastX = 0; 95 | mLastYaw = 0; 96 | mLastPitch = 0; 97 | mLastRoll = 0; 98 | 99 | // Exit if disabled 100 | if (!pEnabled) { 101 | return; 102 | } 103 | 104 | // Calculate the damper basing on the altitude 105 | aglAlt = XPLMGetDataf(mRadioAltDataRef); 106 | 107 | if (aglAlt > maxAlt) { 108 | mDamper = minDamper; 109 | } else if (aglAlt > minAlt) { 110 | mDamper = minDamper + (maxDamper - minDamper) * (maxAlt - aglAlt) / (maxAlt - minAlt); 111 | } else { 112 | mDamper = maxDamper; 113 | } 114 | 115 | // Push backward/forward (axial) 116 | currentG = gs > 1 ? XPLMGetDataf(mGAxialDataRef) : 0; 117 | if (filter) { 118 | currentG = mLastAxialG + (currentG - mLastAxialG) * 0.1; 119 | } 120 | mLastAxialG = currentG; 121 | mZFilter.insert(mZFilter.begin(), currentG); 122 | if (mZFilter.size() > 10) { 123 | mZFilter.pop_back(); 124 | } 125 | acc = continue_log(average(mZFilter)); 126 | mLastZ -= (acc * mZResponse * mGeneralSensitivity / 500.0f); 127 | mLastPitch -= (acc * mZResponse * mGeneralSensitivity / 7.5f); 128 | position.y += -mLastZ / 2.0f; 129 | position.z += mLastZ; 130 | 131 | // Heading 132 | currentG = gs > 1 ? XPLMGetDataf(mGSideDataRef) : 0; 133 | mYawFilter.insert(mYawFilter.begin(), currentG); 134 | while (mYawFilter.size() > mDamper) 135 | mYawFilter.pop_back(); 136 | acc = continue_log(average(mYawFilter)); 137 | mLastYaw -= (acc * mYawResponse * mGeneralSensitivity / 15); 138 | position.yaw += mLastYaw; 139 | 140 | // Roll 141 | // (works like the heading but with a different response) 142 | mLastRoll -= (acc * mYawResponse * mGeneralSensitivity / 5); 143 | position.roll += mLastRoll; 144 | 145 | // X 146 | // (works like the heading but with a different response) 147 | mLastX -= (acc * mYawResponse * mGeneralSensitivity / 500); 148 | position.x += mLastX; 149 | 150 | // Pitch 151 | currentG = gs > 1 ? XPLMGetDataf(mGNormalDataRef) : 1; 152 | if (filter) { 153 | currentG = mLastNormG + (currentG - mLastNormG) * 0.1; 154 | } 155 | mLastNormG = currentG; 156 | mPitchFilter.insert(mPitchFilter.begin(), currentG - 1); 157 | while (mPitchFilter.size() > mDamper) 158 | mPitchFilter.pop_back(); 159 | acc = average(mPitchFilter); 160 | mLastPitch -= (continue_log(acc) * mPitchResponse * mGeneralSensitivity / 10.0f); 161 | position.pitch += mLastPitch; 162 | } 163 | 164 | void GForceCameraCommand::accept(IVisitor &visitor) 165 | { 166 | visitor.visit(*this); 167 | } 168 | 169 | void GForceCameraCommand::set_pitch_response(float response) 170 | { 171 | mPitchResponse = response; 172 | } 173 | 174 | float GForceCameraCommand::get_pitch_response() 175 | { 176 | return mPitchResponse; 177 | } 178 | 179 | void GForceCameraCommand::set_acceleration_response(float response) 180 | { 181 | mZResponse = response; 182 | } 183 | 184 | float GForceCameraCommand::get_acceleration_response() 185 | { 186 | return mZResponse; 187 | } 188 | 189 | void GForceCameraCommand::set_yaw_response(float response) 190 | { 191 | mYawResponse = response; 192 | } 193 | 194 | float GForceCameraCommand::get_yaw_response() 195 | { 196 | return mYawResponse; 197 | } 198 | -------------------------------------------------------------------------------- /src/cameracommands/gforcecameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef GFORCECAMERACOMMAND_H 2 | #define GFORCECAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "cameracommands/cameracommand.h" 9 | #include "cameraposition.h" 10 | 11 | class GForceCameraCommand : public CameraCommand 12 | { 13 | public: 14 | /** Default constructor */ 15 | GForceCameraCommand(); 16 | /** Default destructor */ 17 | virtual ~GForceCameraCommand(); 18 | void execute(CameraPosition&, float) override; 19 | void accept(IVisitor&); 20 | /** Implementation methods */ 21 | void set_pitch_response(float); 22 | float get_pitch_response(); 23 | void set_acceleration_response(float); 24 | float get_acceleration_response(); 25 | void set_yaw_response(float response); 26 | float get_yaw_response(); 27 | void on_enable() override; 28 | void on_disable() override; 29 | protected: 30 | private: 31 | unsigned int mDamper; 32 | std::vector mPitchFilter; 33 | std::vector mYawFilter; 34 | std::vector mZFilter; 35 | std::vector mAlphaFilter; 36 | float mLastPitch; 37 | float mLastYaw; 38 | float mLastRoll; 39 | float mLastZ; 40 | float mLastX; 41 | float mPitchResponse; 42 | float mYawResponse; 43 | float mZResponse; 44 | float mLastNormG; 45 | float mLastAxialG; 46 | float mGeneralSensitivity; 47 | XPLMDataRef mGNormalDataRef; 48 | XPLMDataRef mGAxialDataRef; 49 | XPLMDataRef mGSideDataRef; 50 | XPLMDataRef mRadioAltDataRef; 51 | XPLMDataRef mOnGroundDataRef; 52 | XPLMDataRef mBrakesDataRef; 53 | XPLMDataRef mGSDataRef; 54 | XPLMDataRef mGeneralSensitivityDataRef; 55 | }; 56 | 57 | #endif // GFORCECAMERACOMMAND_H 58 | -------------------------------------------------------------------------------- /src/cameracommands/groundrollcameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "helpers.h" 7 | #include "cameraposition.h" 8 | #include "interfaces/ivisitor.h" 9 | #include "cameracommands/groundrollcameracommand.h" 10 | 11 | GroundRollCameraCommand::GroundRollCameraCommand() 12 | { 13 | // Setup the datarefs 14 | mGNormalDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_nrml"); 15 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 16 | mGSideDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_side"); 17 | mBrakesDataRef = XPLMFindDataRef("sim/cockpit2/controls/parking_brake_ratio"); 18 | mGSDataRef = XPLMFindDataRef("sim/flightmodel/position/groundspeed"); 19 | // Setup the private vars 20 | mResponse = 25; 21 | mLastPitch = 0; 22 | mLastRoll = 0; 23 | mLastX = 0; 24 | mLastNormG = 1; 25 | } 26 | 27 | GroundRollCameraCommand::~GroundRollCameraCommand() 28 | { 29 | //dtor 30 | } 31 | 32 | void GroundRollCameraCommand::execute(CameraPosition &position, float elapsedTime) 33 | { 34 | CameraCommand::execute(position, elapsedTime); 35 | 36 | float acc, currentNormalG, currentSideG; 37 | 38 | // Restore the initial position 39 | position.pitch -= mLastPitch; 40 | position.roll -= mLastRoll; 41 | position.x -= mLastX; 42 | 43 | // Reset the state vars 44 | mLastPitch = 0; 45 | mLastRoll = 0; 46 | mLastX = 0; 47 | 48 | // Exit if disabled 49 | if (!pEnabled) 50 | return; 51 | 52 | if (XPLMGetDatai(mOnGroundDataRef) && XPLMGetDataf(mGSDataRef) > 1) { 53 | currentNormalG = std::max(0.92f, std::min(1.08f, XPLMGetDataf(mGNormalDataRef))); 54 | if (XPLMGetDataf(mBrakesDataRef) > 0) { 55 | currentNormalG = mLastNormG + (currentNormalG - mLastNormG) * 0.1; 56 | } 57 | mLastNormG = currentNormalG; 58 | currentSideG = std::max(-0.04f, std::min(0.04f, XPLMGetDataf(mGSideDataRef))); 59 | } else { 60 | currentNormalG = 1; 61 | currentSideG = 0; 62 | } 63 | 64 | // Pitch 65 | // Limit the variations between a minimum and a maximum 66 | mPitchFilter.insert(mPitchFilter.begin(), currentNormalG - 1); 67 | if (mPitchFilter.size() > 5) 68 | mPitchFilter.pop_back(); 69 | acc = continue_log(average(mPitchFilter)); 70 | mLastPitch = -(acc * mResponse / 10.0f); 71 | position.pitch += mLastPitch; 72 | 73 | // Roll 74 | mYawFilter.insert(mYawFilter.begin(), currentSideG); 75 | if (mYawFilter.size() > 25) 76 | mYawFilter.pop_back(); 77 | acc = continue_log(average(mYawFilter)); 78 | mLastRoll -= (acc * mResponse / 5.0f); 79 | position.roll += mLastRoll; 80 | 81 | // X 82 | // (works like the roll but with a different response) 83 | mLastX -= (acc * mResponse / 500.0f); 84 | position.x += mLastX; 85 | } 86 | 87 | void GroundRollCameraCommand::accept(IVisitor &visitor) 88 | { 89 | visitor.visit(*this); 90 | } 91 | 92 | void GroundRollCameraCommand::set_response(float response) 93 | { 94 | mResponse = response; 95 | } 96 | 97 | float GroundRollCameraCommand::get_response() 98 | { 99 | return mResponse; 100 | } 101 | -------------------------------------------------------------------------------- /src/cameracommands/groundrollcameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUNDROLLCAMERACOMMAND_H 2 | #define GROUNDROLLCAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "cameracommands/cameracommand.h" 9 | #include "cameraposition.h" 10 | 11 | class GroundRollCameraCommand : public CameraCommand 12 | { 13 | public: 14 | /** Default constructor */ 15 | GroundRollCameraCommand(); 16 | /** Default destructor */ 17 | virtual ~GroundRollCameraCommand(); 18 | void execute(CameraPosition&, float) override; 19 | void accept(IVisitor&); 20 | /** Implementation methods */ 21 | void set_response(float); 22 | float get_response(); 23 | protected: 24 | private: 25 | std::vector mPitchFilter; 26 | std::vector mYawFilter; 27 | float mLastPitch; 28 | float mResponse; 29 | float mLastX; 30 | float mLastRoll; 31 | float mLastNormG; 32 | XPLMDataRef mGNormalDataRef; 33 | XPLMDataRef mOnGroundDataRef; 34 | XPLMDataRef mGSideDataRef; 35 | XPLMDataRef mBrakesDataRef; 36 | XPLMDataRef mGSDataRef; 37 | }; 38 | 39 | #endif // GROUNDROLLCAMERACOMMAND_H 40 | -------------------------------------------------------------------------------- /src/cameracommands/levelheadcameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "helpers.h" 4 | #include "cameraposition.h" 5 | #include "interfaces/ivisitor.h" 6 | #include "cameracommands/levelheadcameracommand.h" 7 | 8 | LevelHeadCameraCommand::LevelHeadCameraCommand() 9 | { 10 | mRollRef = XPLMFindDataRef("sim/flightmodel/position/phi"); 11 | mLastRoll = 0; 12 | mResponse = 100; 13 | mMaxBankAngle = 20; 14 | } 15 | 16 | LevelHeadCameraCommand::~LevelHeadCameraCommand() 17 | { 18 | } 19 | 20 | void LevelHeadCameraCommand::execute(CameraPosition &position, float elapsedTime) 21 | { 22 | CameraCommand::execute(position, elapsedTime); 23 | 24 | float currentBank, targetRoll; 25 | 26 | position.roll += mLastRoll; 27 | 28 | if (!pEnabled) { 29 | mLastRoll = 0; 30 | return; 31 | } 32 | 33 | currentBank = XPLMGetDataf(mRollRef); 34 | 35 | if (currentBank < 0) { 36 | targetRoll = (std::max(-mMaxBankAngle, currentBank)) * (mResponse / 100.0f); 37 | } 38 | else { 39 | targetRoll = (std::min(mMaxBankAngle, currentBank)) * (mResponse / 100.0f); 40 | } 41 | 42 | if (get_blend_ratio() < 1) { 43 | if (mLastRoll > targetRoll) { 44 | mLastRoll -= (mLastRoll - targetRoll) * get_blend_ratio(); 45 | } 46 | else { 47 | mLastRoll += (targetRoll - mLastRoll) * get_blend_ratio(); 48 | } 49 | } 50 | else { 51 | mLastRoll = targetRoll; 52 | } 53 | position.roll -= mLastRoll; 54 | } 55 | 56 | void LevelHeadCameraCommand::accept(IVisitor &visitor) 57 | { 58 | visitor.visit(*this); 59 | } 60 | 61 | void LevelHeadCameraCommand::set_response(float response) 62 | { 63 | mResponse = response; 64 | } 65 | 66 | float LevelHeadCameraCommand::get_response() 67 | { 68 | return mResponse; 69 | } 70 | 71 | void LevelHeadCameraCommand::set_max_bank(float maxBank) 72 | { 73 | mMaxBankAngle = maxBank; 74 | } 75 | 76 | float LevelHeadCameraCommand::get_max_bank() 77 | { 78 | return mMaxBankAngle; 79 | } 80 | -------------------------------------------------------------------------------- /src/cameracommands/levelheadcameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef LEVELHEADCAMERACOMMAND_H 2 | #define LEVELHEADCAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include "cameracommands/cameracommand.h" 7 | 8 | class LevelHeadCameraCommand : public CameraCommand 9 | { 10 | public: 11 | /** Default constructor */ 12 | LevelHeadCameraCommand(); 13 | /** Default destructor */ 14 | virtual ~LevelHeadCameraCommand(); 15 | void execute(CameraPosition&, float) override; 16 | void accept(IVisitor&); 17 | void set_response(float response); 18 | float get_response(); 19 | void set_max_bank(float maxBank); 20 | float get_max_bank(); 21 | protected: 22 | private: 23 | float mLastRoll; 24 | float mResponse; 25 | float mMaxBankAngle; 26 | XPLMDataRef mRollRef; 27 | }; 28 | 29 | #endif // LEVELHEADCAMERACOMMAND_H 30 | -------------------------------------------------------------------------------- /src/cameracommands/lookaheadcameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "helpers.h" 7 | #include "cameraposition.h" 8 | #include "interfaces/ivisitor.h" 9 | #include "cameracommands/lookaheadcameracommand.h" 10 | 11 | LookAheadCameraCommand::LookAheadCameraCommand() 12 | { 13 | // Setup the datarefs 14 | mGNormalDataRef = XPLMFindDataRef("sim/flightmodel/forces/g_nrml"); 15 | mDamper = 5; 16 | mResponse = 45; 17 | mLastAlpha = 0; 18 | mTargetAlpha = 0; 19 | mLastPitch = 0; 20 | } 21 | 22 | LookAheadCameraCommand::~LookAheadCameraCommand() 23 | { 24 | //dtor 25 | } 26 | 27 | void LookAheadCameraCommand::execute(CameraPosition &position, float elapsedTime) 28 | { 29 | CameraCommand::execute(position, elapsedTime); 30 | 31 | float acc, currentG; 32 | 33 | position.pitch -= mLastPitch; 34 | // Reset the state vars 35 | mLastPitch = 0; 36 | 37 | // Exit if disabled 38 | if (!pEnabled) 39 | return; 40 | 41 | // Pitch 42 | currentG = XPLMGetDataf(mGNormalDataRef); 43 | // Work only for positive Gs 44 | if (currentG > 0) { 45 | mPitchFilter.insert(mPitchFilter.begin(), currentG - 1); 46 | while (mPitchFilter.size() > mDamper) 47 | mPitchFilter.pop_back(); 48 | acc = average(mPitchFilter); 49 | if (acc > 4.0f) 50 | mTargetAlpha = 1.0f; 51 | else if ((mTargetAlpha < 0.5f && acc > 2.0f) || (mTargetAlpha > 0.8f && acc < 3.4f)) 52 | mTargetAlpha = 0.5f; 53 | else if (mTargetAlpha > 0.4f && acc < 1.5f) 54 | mTargetAlpha = 0; 55 | } else { 56 | mTargetAlpha = 0; 57 | } 58 | mLastAlpha += (mTargetAlpha - mLastAlpha) / 20.0f; 59 | mLastPitch = mLastAlpha * mResponse; 60 | position.pitch += mLastPitch; 61 | } 62 | 63 | void LookAheadCameraCommand::accept(IVisitor &visitor) 64 | { 65 | visitor.visit(*this); 66 | } 67 | 68 | void LookAheadCameraCommand::set_response(float response) 69 | { 70 | mResponse = response; 71 | } 72 | 73 | float LookAheadCameraCommand::get_response() 74 | { 75 | return mResponse; 76 | } 77 | 78 | // Executed when the view type changes 79 | void LookAheadCameraCommand::on_view_changed(int viewCode) 80 | { 81 | // If the user enters the virtualcokpit, reset the data 82 | if (viewCode == 1026) { 83 | mLastAlpha = 0; 84 | mLastPitch = 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/cameracommands/lookaheadcameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef LOOKAHEADCAMERACOMMAND_H 2 | #define LOOKAHEADCAMERACOMMAND_H 3 | #include 4 | 5 | #include 6 | 7 | #include "cameracommands/cameracommand.h" 8 | 9 | class LookAheadCameraCommand : public CameraCommand 10 | { 11 | public: 12 | /** Default constructor */ 13 | LookAheadCameraCommand(); 14 | void execute(CameraPosition&, float) override; 15 | void accept(IVisitor&); 16 | /** Default destructor */ 17 | virtual ~LookAheadCameraCommand(); 18 | void set_response(float response); 19 | float get_response(); 20 | void on_view_changed(int); 21 | protected: 22 | private: 23 | std::vector mPitchFilter; 24 | XPLMDataRef mGNormalDataRef; 25 | float mLastAlpha; 26 | float mTargetAlpha; 27 | float mLastPitch; 28 | float mResponse; 29 | float mDamper; 30 | }; 31 | 32 | #endif // LOOKAHEADCAMERACOMMAND_H 33 | -------------------------------------------------------------------------------- /src/cameracommands/pistonenginecameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cameracommands/pistonenginecameracommand.h" 11 | #include "cameraposition.h" 12 | #include "interfaces/ivisitor.h" 13 | 14 | #define MSG_ADD_DATAREF 0x01000000 15 | 16 | PistonEngineCameraCommand::PistonEngineCameraCommand() 17 | { 18 | // Load the dataref 19 | mNumEngineDataRef = XPLMFindDataRef("sim/aircraft/engine/acf_num_engines"); 20 | mEngineTypeDataRef = XPLMFindDataRef("sim/aircraft/prop/acf_en_type"); 21 | mEngineRotationDataRef = XPLMFindDataRef("sim/flightmodel2/engines/engine_rotation_angle_deg"); 22 | mEngineRpmDataRef = XPLMFindDataRef("sim/cockpit2/engine/indicators/engine_speed_rpm"); 23 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 24 | // Set the starting shakes 25 | mLastXShake = 0; 26 | mLastYShake = 0; 27 | mResponse = 25; 28 | mOverride = false; 29 | 30 | mOverrideVibrationRatio[0] = 0; 31 | mOverrideVibrationRatio[1] = 0; 32 | mOverrideVibrationRatio[2] = 0; 33 | mOverrideVibrationRatio[3] = 0; 34 | mOverrideVibrationRatio[4] = 0; 35 | mOverrideVibrationRatio[5] = 0; 36 | mOverrideVibrationRatio[6] = 0; 37 | mOverrideVibrationRatio[7] = 0; 38 | 39 | mOverrideVibrationFreq[0] = 50; 40 | mOverrideVibrationFreq[1] = 50; 41 | mOverrideVibrationFreq[2] = 50; 42 | mOverrideVibrationFreq[3] = 50; 43 | mOverrideVibrationFreq[4] = 50; 44 | mOverrideVibrationFreq[5] = 50; 45 | mOverrideVibrationFreq[6] = 50; 46 | mOverrideVibrationFreq[7] = 50; 47 | } 48 | 49 | PistonEngineCameraCommand::~PistonEngineCameraCommand() 50 | { 51 | 52 | } 53 | 54 | void PistonEngineCameraCommand::on_enable() 55 | { 56 | int datarefEditorId = XPLMFindPluginBySignature("xplanesdk.examples.DataRefEditor"); 57 | 58 | // Publish the drefs 59 | mOverrideDataref = XPLMRegisterDataAccessor( 60 | "simcoders/headshake/pistonengine/override_vibration", xplmType_Int, 1, 61 | [](void* refCon) -> int { 62 | if (refCon) return reinterpret_cast(refCon)->mOverride ? 1 : 0; 63 | return 0; 64 | }, [](void* refCon, int value) -> void { 65 | if (refCon) { 66 | reinterpret_cast(refCon)->mOverride = value == 1; 67 | } 68 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this); 69 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/pistonengine/override_vibration"); 70 | 71 | mVibrationRatioDataref = XPLMRegisterDataAccessor( 72 | "simcoders/headshake/pistonengine/vibration_ratio", xplmType_FloatArray, 1, 73 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 74 | [](void *refCon, float *outValues, int inOffset, int inMax) -> int { 75 | if (outValues == NULL) { 76 | return 0; 77 | } 78 | if (refCon) { 79 | int i = 0; 80 | for (i = inOffset; (i < 8) && (i < inOffset+inMax); i++) { 81 | outValues[i] = reinterpret_cast(refCon)->mOverrideVibrationRatio[i]; 82 | } 83 | return i; 84 | } 85 | return 0; 86 | }, [](void *refCon, float *inValues, int inOffset, int inMax) -> void { 87 | if (inValues != NULL) { 88 | if (refCon) { 89 | for (int i = 0; (i + inOffset < 8) && i < inMax; i++) { 90 | reinterpret_cast(refCon)->mOverrideVibrationRatio[inOffset + i] = inValues[i]; 91 | } 92 | } 93 | } 94 | }, NULL, NULL, this, this); 95 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/pistonengine/vibration_ratio"); 96 | 97 | mVibrationFreqDataref = XPLMRegisterDataAccessor( 98 | "simcoders/headshake/pistonengine/vibration_freq", xplmType_FloatArray, 1, 99 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 100 | [](void *refCon, float *outValues, int inOffset, int inMax) -> int { 101 | if (outValues == NULL) { 102 | return 0; 103 | } 104 | if (refCon) { 105 | int i = 0; 106 | for (i = inOffset; (i < 8) && (i < inOffset+inMax); i++) { 107 | outValues[i] = reinterpret_cast(refCon)->mOverrideVibrationFreq[i]; 108 | } 109 | return i; 110 | } 111 | return 0; 112 | }, [](void *refCon, float *inValues, int inOffset, int inMax) -> void { 113 | if (inValues != NULL) { 114 | if (refCon) { 115 | for (int i = 0; (i + inOffset < 8) && i < inMax; i++) { 116 | reinterpret_cast(refCon)->mOverrideVibrationFreq[inOffset + i] = inValues[i]; 117 | } 118 | } 119 | } 120 | }, NULL, NULL, this, this); 121 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/pistonengine/vibration_freq"); 122 | } 123 | 124 | void PistonEngineCameraCommand::on_disable() 125 | { 126 | XPLMUnregisterDataAccessor(mOverrideDataref); 127 | XPLMUnregisterDataAccessor(mVibrationRatioDataref); 128 | XPLMUnregisterDataAccessor(mVibrationFreqDataref); 129 | } 130 | 131 | void PistonEngineCameraCommand::execute(CameraPosition &position, float elapsedTime) 132 | { 133 | CameraCommand::execute(position, elapsedTime); 134 | 135 | const double PI = 3.141592653589793238463; 136 | int engineTypeOutVal[8], engineNum; 137 | float engineRotationOutVal[8]; 138 | float engineRpmOutVal[8]; 139 | float power, xshake = 0, yshake=0, maxYShake = 0, maxXShake = 0, angle; 140 | 141 | // Reset the last position 142 | position.x -= mLastXShake; 143 | position.y -= mLastYShake; 144 | // Reset the last position vars 145 | mLastXShake = 0; 146 | mLastYShake = 0; 147 | // Return if not enabled 148 | if (!pEnabled) 149 | return; 150 | //Read the necessary vars 151 | engineNum = XPLMGetDatai(mNumEngineDataRef); 152 | XPLMGetDatavf(mEngineRotationDataRef, engineRotationOutVal, 0, 7); 153 | XPLMGetDatavf(mEngineRpmDataRef, engineRpmOutVal, 0, 7); 154 | XPLMGetDatavi(mEngineTypeDataRef, engineTypeOutVal, 0, 7); 155 | 156 | // Vary the power using the mResponse value; 157 | power = mResponse / 200.0f; 158 | // Half power if the plane is flying 159 | if (XPLMGetDatai(mOnGroundDataRef) == 0) 160 | power = power / 2.0f; 161 | 162 | // Detect the rpm of each engine and shake! 163 | for (int i = 0; i < engineNum; i++) { 164 | // Do this only on piston engines (injected or carb) 165 | if (engineTypeOutVal[i] == 1 || engineTypeOutVal[i] == 0) { 166 | if (mOverride) { 167 | 168 | // If overridden, use the ref data 169 | xshake = power * (mOverrideVibrationRatio[i] / 1000) * sin(mOverrideVibrationFreq[i] * XPLMGetElapsedTime()); 170 | yshake = (power / 2) * (mOverrideVibrationRatio[i] / 1000) * sin((mOverrideVibrationFreq[i] + 10) * XPLMGetElapsedTime()); 171 | 172 | if (std::abs(xshake) > std::abs(maxXShake)) 173 | maxXShake = xshake; 174 | if (std::abs(yshake) > std::abs(maxYShake)) 175 | maxYShake = yshake; 176 | 177 | } else { 178 | 179 | // Otherwise use the default data 180 | if (engineRpmOutVal[i] > 300) { 181 | 182 | // High rpms 183 | 184 | xshake = power / std::abs(engineRpmOutVal[i]) * sin(50 * XPLMGetElapsedTime()); 185 | yshake = (power / 2) / std::abs(engineRpmOutVal[i]) * sin(60 * XPLMGetElapsedTime()); 186 | 187 | if (std::abs(xshake) > std::abs(maxXShake)) 188 | maxXShake = xshake; 189 | if (std::abs(yshake) > std::abs(maxYShake)) 190 | maxYShake = yshake; 191 | } else if (engineRpmOutVal[i] > 5) { 192 | 193 | // Low rpms 194 | 195 | // Continue if the cylinder is at low compression 196 | // (more than 45° after each 90°) 197 | if ((int)engineRotationOutVal[i] % 90 <= 30) { 198 | angle = (float)((int)engineRotationOutVal[i] % 90); 199 | // Convert the angle to 360° 200 | angle = angle / 30.0f * 360.0f; 201 | // Convert to radians 202 | angle = angle * PI / 180.0f; 203 | maxXShake = power * 0.005f * sin(angle); 204 | maxYShake = maxXShake * 0.5f; 205 | } 206 | } 207 | } 208 | } 209 | } 210 | mLastXShake = maxXShake; 211 | mLastYShake = maxYShake; 212 | // Get the average of the shakes 213 | position.y += mLastYShake; 214 | position.x += mLastXShake; 215 | } 216 | 217 | void PistonEngineCameraCommand::accept(IVisitor &visitor) 218 | { 219 | visitor.visit(*this); 220 | } 221 | -------------------------------------------------------------------------------- /src/cameracommands/pistonenginecameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef PISTONENGINECAMERACOMMAND_H 2 | #define PISTONENGINECAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include "cameracommands/cameracommand.h" 7 | #include "cameraposition.h" 8 | 9 | class PistonEngineCameraCommand : public CameraCommand 10 | { 11 | public: 12 | /** Default constructor */ 13 | PistonEngineCameraCommand(); 14 | /** Default destructor */ 15 | virtual ~PistonEngineCameraCommand(); 16 | void execute(CameraPosition&, float) override; 17 | void accept(IVisitor&); 18 | void set_response(float response) { mResponse = response; } 19 | float get_response() { return mResponse; } 20 | void on_enable() override; 21 | void on_disable() override; 22 | protected: 23 | private: 24 | /** Searched datarefs */ 25 | XPLMDataRef mNumEngineDataRef; 26 | XPLMDataRef mEngineTypeDataRef; 27 | XPLMDataRef mEngineRotationDataRef; 28 | XPLMDataRef mEngineRpmDataRef; 29 | XPLMDataRef mOnGroundDataRef; 30 | /** Published DataRefs **/ 31 | XPLMDataRef mOverrideDataref; 32 | XPLMDataRef mVibrationRatioDataref; 33 | XPLMDataRef mVibrationFreqDataref; 34 | float mOverrideVibrationRatio[8]; 35 | float mOverrideVibrationFreq[8]; 36 | bool mOverride; 37 | float mLastXShake; 38 | float mLastYShake; 39 | float mResponse; 40 | }; 41 | 42 | #endif // PISTONENGINECAMERACOMMAND_H 43 | -------------------------------------------------------------------------------- /src/cameracommands/rotorcameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "cameracommands/rotorcameracommand.h" 8 | #include "cameraposition.h" 9 | #include "interfaces/ivisitor.h" 10 | 11 | RotorCameraCommand::RotorCameraCommand() 12 | { 13 | // Setup the datarefs 14 | mRotorRpmDataRef = XPLMFindDataRef("sim/flightmodel2/engines/prop_rotation_speed_rad_sec"); 15 | mRotorAngleDataRef = XPLMFindDataRef("sim/flightmodel2/engines/prop_rotation_angle_deg"); 16 | mPropTypeDataRef = XPLMFindDataRef("sim/aircraft/prop/acf_prop_type"); 17 | mNumBladesDataRef = XPLMFindDataRef("sim/aircraft/prop/acf_num_blades"); 18 | mPropMassDataRef = XPLMFindDataRef("sim/aircraft/prop/acf_prop_mass"); 19 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 20 | // Define the default values 21 | mResponse = 25; 22 | mLastYaw = 0; 23 | } 24 | 25 | RotorCameraCommand::~RotorCameraCommand() 26 | { 27 | 28 | } 29 | 30 | void RotorCameraCommand::execute(CameraPosition &position, float elapsedTime) 31 | { 32 | CameraCommand::execute(position, elapsedTime); 33 | 34 | const double PI = 3.141592653589793238463; 35 | float rotorRpmOutVal[8], rotorAngleOutVal[8], numBladesOutVal[8], propMassOutVal[8]; 36 | int propTypeOutVal[8], count = 0; 37 | float shake = 0, angle, rpm, power; 38 | 39 | position.x -= mLastYaw; 40 | position.y -= 0.1 * mLastYaw; 41 | mLastYaw = 0; 42 | if (!pEnabled) 43 | return; 44 | 45 | // Read the values 46 | XPLMGetDatavf(mRotorRpmDataRef, rotorRpmOutVal, 0, 7); 47 | XPLMGetDatavf(mRotorAngleDataRef, rotorAngleOutVal, 0, 7); 48 | XPLMGetDatavf(mPropMassDataRef, propMassOutVal, 0, 7); 49 | XPLMGetDatavi(mPropTypeDataRef, propTypeOutVal, 0, 7); 50 | XPLMGetDatavf(mNumBladesDataRef, numBladesOutVal, 0, 7); 51 | // Use half power if the heli is in flight 52 | if (XPLMGetDatai(mOnGroundDataRef) == 0) 53 | power = 0.00050; 54 | else 55 | power = 0.00075; 56 | 57 | // Detect the angle and rpm of each engine and shake! 58 | for (int i = 0; i < 8; i++) { 59 | // Convert speed to rpms 60 | rpm = (rotorRpmOutVal[i] * 180 / PI); // deg/s 61 | rpm = (rpm / 360) * 60; // rpms 62 | // Only rotating rotors with mass (as some helicopters as no mass rotors!) 63 | if (propTypeOutVal[i] == 3 && rpm > 1 && propMassOutVal[i] > 0) { 64 | if (rpm > 250) { 65 | // High rpms 66 | // shake it basing on the angle which varies at the rotor frequency 67 | shake += power * (mResponse / 50.0f) * sin(XPLMGetElapsedTime() * 25 * numBladesOutVal[i]); 68 | } else if (rpm > 120) { 69 | // Medium rpms 70 | // shake it basing on the angle which varies at the rotor frequency 71 | shake += (power / 1.5f) * (mResponse / 50.0f) * sin(XPLMGetElapsedTime() * 18 * numBladesOutVal[i]); 72 | } else { 73 | // Low rpms 74 | angle = rotorAngleOutVal[i] * numBladesOutVal[i]; 75 | // Convert to radians 76 | angle = angle * PI / 180.0f; 77 | shake += (power / 3) * (mResponse / 50.0f) * sin(angle); 78 | } 79 | count++; 80 | } 81 | } 82 | if (count > 0) { 83 | mLastYaw = shake / (float)count; 84 | position.x += mLastYaw; 85 | position.y += 0.1 * mLastYaw; 86 | } 87 | } 88 | 89 | void RotorCameraCommand::accept(IVisitor &visitor) 90 | { 91 | visitor.visit(*this); 92 | } 93 | -------------------------------------------------------------------------------- /src/cameracommands/rotorcameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef ROTORCAMERACOMMAND_H 2 | #define ROTORCAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include "cameracommands/cameracommand.h" 7 | #include "cameraposition.h" 8 | 9 | class RotorCameraCommand : public CameraCommand 10 | { 11 | public: 12 | /** Default constructor */ 13 | RotorCameraCommand(); 14 | /** Default destructor */ 15 | virtual ~RotorCameraCommand(); 16 | void execute(CameraPosition&, float) override; 17 | void accept(IVisitor&); 18 | void set_response(float response) { mResponse = response; } 19 | float get_response() { return mResponse; } 20 | protected: 21 | private: 22 | XPLMDataRef mRotorRpmDataRef; 23 | XPLMDataRef mRotorAngleDataRef; 24 | XPLMDataRef mPropTypeDataRef; 25 | XPLMDataRef mNumBladesDataRef; 26 | XPLMDataRef mPropMassDataRef; 27 | XPLMDataRef mOnGroundDataRef; 28 | float mLastYaw; 29 | float mResponse; 30 | }; 31 | 32 | #endif // ROTORCAMERACOMMAND_H 33 | -------------------------------------------------------------------------------- /src/cameracommands/taxilookaheadcameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "helpers.h" 7 | #include "cameraposition.h" 8 | #include "interfaces/ivisitor.h" 9 | #include "cameracommands/taxilookaheadcameracommand.h" 10 | 11 | TaxiLookAheadCameraCommand::TaxiLookAheadCameraCommand() 12 | { 13 | // Setup the datarefs 14 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 15 | mTurnDataRef = XPLMFindDataRef("sim/flightmodel/position/R"); 16 | mRudderDataRef = XPLMFindDataRef("sim/joystick/yoke_heading_ratio"); 17 | mGroundSpeedDataRef = XPLMFindDataRef("sim/flightmodel/position/groundspeed"); 18 | 19 | // Setup the private vars 20 | mRudderResponse = 25; 21 | mLeanResponse = 25; 22 | mTurnResponse = 25; 23 | mLastY = 0; 24 | mLastZ = 0; 25 | mLastYaw = 0; 26 | mLastRoll = 0; 27 | } 28 | 29 | TaxiLookAheadCameraCommand::~TaxiLookAheadCameraCommand() 30 | { 31 | //dtor 32 | } 33 | 34 | void TaxiLookAheadCameraCommand::execute(CameraPosition &position, float elapsedTime) 35 | { 36 | CameraCommand::execute(position, elapsedTime); 37 | 38 | (void)position; 39 | 40 | float blend, acc, lean, leanInput, rudderInput, turnInput, groundSpeedAcc, currentTurn, currentRudder, currentGroundSpeed; 41 | 42 | // Restore the initial yaw & roll 43 | position.y -= mLastY; 44 | position.z -= mLastZ; 45 | position.yaw -= mLastYaw; 46 | position.roll -= mLastRoll; 47 | 48 | // Exit if disabled 49 | if (!pEnabled) 50 | { 51 | // Reset the state vars 52 | mLastY = 0; 53 | mLastZ = 0; 54 | mLastYaw = 0; 55 | mLastRoll = 0; 56 | return; 57 | } 58 | 59 | if (XPLMGetDatai(mOnGroundDataRef)) { 60 | currentTurn = XPLMGetDataf(mTurnDataRef); 61 | currentRudder = XPLMGetDataf(mRudderDataRef); 62 | currentGroundSpeed = XPLMGetDataf(mGroundSpeedDataRef); 63 | } else { 64 | currentTurn = 0; 65 | currentRudder = 0; 66 | currentGroundSpeed = 0; 67 | } 68 | 69 | // Rudder & lean input 70 | mRudderFilter.insert(mRudderFilter.begin(), currentRudder); 71 | if( mRudderFilter.size() > 30 ) 72 | mRudderFilter.pop_back(); 73 | rudderInput = average(mRudderFilter) * ( mRudderResponse / 100.0f ) * 45.0f; 74 | leanInput = average(mRudderFilter) * ( mLeanResponse / 100.0f ) * 0.75f; 75 | 76 | // Turn 77 | mTurnFilter.insert(mTurnFilter.begin(), currentTurn); 78 | if( mTurnFilter.size() > 30 ) 79 | mTurnFilter.pop_back(); 80 | turnInput = average(mTurnFilter) * ( mTurnResponse / 25.0f ); 81 | 82 | // Ground speed 83 | mGroundSpeedFilter.insert(mGroundSpeedFilter.begin(), currentGroundSpeed); 84 | if( mGroundSpeedFilter.size() > 10 ) 85 | mGroundSpeedFilter.pop_back(); 86 | groundSpeedAcc = average(mGroundSpeedFilter); 87 | 88 | // Blend the effect out based on the ground speed (max of 20m/sec) 89 | blend = ( 1.0f - std::max( 0.0f, std::min( groundSpeedAcc / 20.0f, 1.0f ) ) ); 90 | 91 | // Mix the turn & rudder effects 92 | acc = ( turnInput + rudderInput ) * 0.5f * blend; 93 | 94 | // Lean forwards (and to some degree upwards/off the seat) 95 | lean = ( leanInput * leanInput ) * blend; 96 | 97 | // Cache y, z, yaw & roll 98 | mLastY = lean * 0.25f; 99 | mLastZ = -lean; 100 | mLastYaw = acc; 101 | mLastRoll = acc * 0.125f; 102 | 103 | position.y += mLastY; 104 | position.z += mLastZ; 105 | position.yaw += mLastYaw; 106 | position.roll += mLastRoll; 107 | } 108 | 109 | void TaxiLookAheadCameraCommand::accept(IVisitor &visitor) 110 | { 111 | visitor.visit(*this); 112 | } 113 | 114 | void TaxiLookAheadCameraCommand::set_rudder_response(float response) 115 | { 116 | mRudderResponse = response; 117 | } 118 | 119 | float TaxiLookAheadCameraCommand::get_rudder_response() const 120 | { 121 | return mRudderResponse; 122 | } 123 | 124 | void TaxiLookAheadCameraCommand::set_turn_response(float response) 125 | { 126 | mTurnResponse = response; 127 | } 128 | 129 | float TaxiLookAheadCameraCommand::get_turn_response() const 130 | { 131 | return mTurnResponse; 132 | } 133 | 134 | void TaxiLookAheadCameraCommand::set_lean_response(float response) 135 | { 136 | mLeanResponse = response; 137 | } 138 | 139 | float TaxiLookAheadCameraCommand::get_lean_response() const 140 | { 141 | return mLeanResponse; 142 | } 143 | -------------------------------------------------------------------------------- /src/cameracommands/taxilookaheadcameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef TAXILOOKAHEADCAMERACOMMAND_H 2 | #define TAXILOOKAHEADCAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "cameracommands/cameracommand.h" 9 | #include "cameraposition.h" 10 | 11 | class TaxiLookAheadCameraCommand : public CameraCommand 12 | { 13 | public: 14 | /** Default constructor */ 15 | TaxiLookAheadCameraCommand(); 16 | /** Default destructor */ 17 | virtual ~TaxiLookAheadCameraCommand(); 18 | void execute(CameraPosition&, float) override; 19 | void accept(IVisitor&); 20 | /** Implementation methods */ 21 | void set_rudder_response(float); 22 | float get_rudder_response() const; 23 | void set_turn_response(float); 24 | float get_turn_response() const; 25 | void set_lean_response(float); 26 | float get_lean_response() const; 27 | protected: 28 | private: 29 | std::vector mRudderFilter; 30 | std::vector mTurnFilter; 31 | std::vector mGroundSpeedFilter; 32 | float mLastZ; 33 | float mLastY; 34 | float mLastYaw; 35 | float mLastRoll; 36 | float mTurnResponse; 37 | float mRudderResponse; 38 | float mLeanResponse; 39 | XPLMDataRef mOnGroundDataRef; 40 | XPLMDataRef mTurnDataRef; 41 | XPLMDataRef mGroundSpeedDataRef; 42 | XPLMDataRef mRudderDataRef; 43 | }; 44 | 45 | #endif // GROUNDROLLCAMERACOMMAND_H 46 | -------------------------------------------------------------------------------- /src/cameracommands/touchdowncameracommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef APL 3 | #include 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cameracommands/touchdowncameracommand.h" 11 | #include "helpers.h" 12 | #include "cameraposition.h" 13 | #include "interfaces/ivisitor.h" 14 | 15 | TouchdownCameraCommand::TouchdownCameraCommand() 16 | { 17 | mVerticalSpeedDataRef = XPLMFindDataRef("sim/flightmodel/position/vh_ind_fpm"); 18 | mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any"); 19 | mRadioAltDataRef = XPLMFindDataRef("sim/flightmodel/position/y_agl"); 20 | mVspeed = 0; 21 | mTouchdownTime = 0; 22 | mTouchdown = true; 23 | mPrevOnGround = true; 24 | mResponse = 25; 25 | mLastPitch = 0; 26 | mLastRoll = 0; 27 | mLastX = 0; 28 | } 29 | 30 | TouchdownCameraCommand::~TouchdownCameraCommand() 31 | { 32 | //dtor 33 | } 34 | 35 | void TouchdownCameraCommand::execute(CameraPosition &position, float elapsedTime) 36 | { 37 | CameraCommand::execute(position, elapsedTime); 38 | 39 | float currentTime, touchdownshake, fastshake; 40 | const double PI = 3.141592653589793238463; 41 | 42 | // Restore the initial position 43 | position.pitch -= mLastPitch; 44 | position.roll -= mLastRoll; 45 | position.x -= mLastX; 46 | 47 | // Reset the state vars 48 | mLastPitch = 0; 49 | mLastRoll = 0; 50 | mLastX = 0; 51 | 52 | // Exit if disabled 53 | if (!pEnabled) 54 | return; 55 | 56 | // Reset if not on ground 57 | if (!XPLMGetDatai(mOnGroundDataRef)) { 58 | // Reset only after climbing 1m agl 59 | if (XPLMGetDataf(mRadioAltDataRef) > 1) 60 | mPrevOnGround = false; 61 | } 62 | 63 | // Touchdown effect 64 | if (!mPrevOnGround && XPLMGetDatai(mOnGroundDataRef)) { 65 | // At touchdown register the time 66 | mPrevOnGround = true; 67 | mTouchdownTime = XPLMGetElapsedTime(); 68 | mVspeed = XPLMGetDataf(mVerticalSpeedDataRef); 69 | // The vspeed defines the power between 0 and 1 70 | // Maximum vspeed in -500 ft/min 71 | mVspeed = std::abs(mVspeed); 72 | mVspeed = std::max(100.0f, std::min(500.0f, mVspeed)) / 500.0f; 73 | } else { 74 | // If we are in the period between touchdown an touchdown + 1.5s 75 | // animate the touchdown effect 76 | currentTime = XPLMGetElapsedTime() - mTouchdownTime; 77 | if (currentTime > 0 && currentTime < 1.5f) { 78 | touchdownshake = (mVspeed * mResponse / 100.0f) * (std::sin(currentTime * PI * 7.0f) / (currentTime * 50.0f)); 79 | fastshake = (mVspeed * mResponse / 100.0f) * (std::sin(currentTime * 50.0f) / (currentTime * 50.0f)); 80 | mLastX = fastshake * 0.01f; 81 | mLastRoll = fastshake * 15.0f; 82 | mLastPitch = touchdownshake * 20.0f; 83 | } 84 | } 85 | position.pitch += mLastPitch; 86 | position.roll += mLastRoll; 87 | position.x += mLastX; 88 | } 89 | 90 | void TouchdownCameraCommand::accept(IVisitor &visitor) 91 | { 92 | visitor.visit(*this); 93 | } 94 | 95 | void TouchdownCameraCommand::set_response(float response) 96 | { 97 | mResponse = response; 98 | } 99 | 100 | float TouchdownCameraCommand::get_response() 101 | { 102 | return mResponse; 103 | } 104 | -------------------------------------------------------------------------------- /src/cameracommands/touchdowncameracommand.h: -------------------------------------------------------------------------------- 1 | #ifndef TOUCHDOWNCAMERACOMMAND_H 2 | #define TOUCHDOWNCAMERACOMMAND_H 3 | 4 | #include 5 | 6 | #include "cameracommands/cameracommand.h" 7 | 8 | class TouchdownCameraCommand : public CameraCommand 9 | { 10 | public: 11 | /** Default constructor */ 12 | TouchdownCameraCommand(); 13 | /** Default destructor */ 14 | virtual ~TouchdownCameraCommand(); 15 | void execute(CameraPosition&, float) override; 16 | void accept(IVisitor&); 17 | /** Implementation methods */ 18 | void set_response(float); 19 | float get_response(); 20 | protected: 21 | private: 22 | bool mPrevOnGround; 23 | bool mTouchdown; 24 | float mResponse; 25 | float mTouchdownTime; 26 | float mVspeed; 27 | float mLastPitch; 28 | float mLastRoll; 29 | float mLastX; 30 | XPLMDataRef mVerticalSpeedDataRef; 31 | XPLMDataRef mOnGroundDataRef; 32 | XPLMDataRef mRadioAltDataRef; 33 | }; 34 | 35 | #endif // TOUCHDOWNCAMERACOMMAND_H 36 | -------------------------------------------------------------------------------- /src/cameracontrol.cpp: -------------------------------------------------------------------------------- 1 | #include // defines NULL 2 | #include 3 | #include 4 | #ifdef APL 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "cameracontrol.h" 13 | #include "cameraposition.h" 14 | #include "interfaces/ivisitor.h" 15 | #include "cameracommands/gforcecameracommand.h" 16 | #include "cameracommands/lookaheadcameracommand.h" 17 | #include "cameracommands/groundrollcameracommand.h" 18 | #include "cameracommands/taxilookaheadcameracommand.h" 19 | #include "cameracommands/pistonenginecameracommand.h" 20 | #include "cameracommands/rotorcameracommand.h" 21 | #include "cameracommands/touchdowncameracommand.h" 22 | #include "cameracommands/levelheadcameracommand.h" 23 | 24 | #define MSG_ADD_DATAREF 0x01000000 25 | 26 | CameraControl* CameraControl::mInstance = NULL; 27 | CameraControl* CameraControl::get_instance() 28 | { 29 | if (!mInstance) 30 | mInstance = new CameraControl; 31 | return mInstance; 32 | } 33 | 34 | CameraControl::CameraControl() 35 | { 36 | XPLMGetVersions(&mXpVersion, nullptr, nullptr); 37 | // Instance the new commands 38 | mCommands.push_back(new GForceCameraCommand); 39 | mCommands.push_back(new LookAheadCameraCommand); 40 | mCommands.push_back(new PistonEngineCameraCommand); 41 | mCommands.push_back(new RotorCameraCommand); 42 | mCommands.push_back(new GroundRollCameraCommand); 43 | mCommands.push_back(new TaxiLookAheadCameraCommand); 44 | mCommands.push_back(new TouchdownCameraCommand); 45 | mCommands.push_back(new LevelHeadCameraCommand); 46 | mFreezed1 = false; 47 | mFreezed2 = false; 48 | mError = false; 49 | mMultimonitorCompatibility = false; 50 | mPositionInited = false; 51 | mOverride = false; 52 | mEnabled = true; 53 | mLastCameraType = 0; 54 | mLastJoyPitch = 0; 55 | mLastJoyYaw = 0; 56 | mCameraOffset.x = mCameraOffset.y = mCameraOffset.z = mCameraOffset.roll = mCameraOffset.pitch = mCameraOffset.yaw = 0; 57 | // Set commands 58 | mEnabledCommand = XPLMCreateCommand("simcoders/headshake/toggle_headshake", "Enable/Disable HeadShake"); 59 | mGforceToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_gforce", "Enable/Disable g-force effects"); 60 | mLookAheadToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_lookahead", "Enable/Disable look ahead effects"); 61 | mPistonVibToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_pistonvib", "Enable/Disable piston engine vibrations"); 62 | mRotorVibToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_rotorvib", "Enable/Disable rotor vibrations"); 63 | mGroundRollToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_groundroll", "Enable/Disable ground roll vibrations"); 64 | mTaxiLookToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_taxilook", "Enable/Disable taxi lookahead"); 65 | mTouchdownToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_touchdown", "Enable/Disable touchdown effect"); 66 | mLevelHeadToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_levelhead", "Enable/Disable level head effect"); 67 | mStopCommand = XPLMCreateCommand("simcoders/headshake/stop", "Stop HeadShake. It will restart once the view is not controlled anymore by another plugin."); 68 | // Fill the datarefs 69 | mCinemaVeriteDataRef = XPLMFindDataRef("sim/graphics/view/cinema_verite"); 70 | mG_LoadedCameraDataRef = XPLMFindDataRef("sim/graphics/view/gloaded_internal_cam"); 71 | mPausedDataRef = XPLMFindDataRef("sim/time/paused"); 72 | mViewTypeDataRef = XPLMFindDataRef("sim/graphics/view/view_type"); 73 | mHeadHeadingDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_psi"); 74 | mHeadPitchDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_the"); 75 | if(mXpVersion >= 1102) mHeadRollDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_phi"); 76 | else mHeadRollDataRef = XPLMFindDataRef("sim/graphics/view/field_of_view_roll_deg"); 77 | mHeadXDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_x"); 78 | mHeadYDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_y"); 79 | mHeadZDataRef = XPLMFindDataRef("sim/graphics/view/pilots_head_z"); 80 | mJoyAxisAssignments = XPLMFindDataRef("sim/joystick/joystick_axis_assignments"); 81 | mJoyAxisValues = XPLMFindDataRef("sim/joystick/joystick_axis_values"); 82 | // Fill the commands 83 | // These are the commands that stop the plugin for a while when called 84 | mStopCommands.push_back(mStopCommand); 85 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_0")); 86 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_1")); 87 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_2")); 88 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_3")); 89 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_4")); 90 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_5")); 91 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_6")); 92 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_7")); 93 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_8")); 94 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_9")); 95 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_10")); 96 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_11")); 97 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_12")); 98 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_13")); 99 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_14")); 100 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_15")); 101 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_16")); 102 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_17")); 103 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_18")); 104 | mStopCommands.push_back(XPLMFindCommand("sim/view/quick_look_19")); 105 | mStopCommands.push_back(XPLMFindCommand("sim/view/3d_cockpit_cmnd_look")); 106 | mStopCommands.push_back(XPLMFindCommand("sim/view/3d_cockpit_toggle")); 107 | mStopCommands.push_back(XPLMFindCommand("sim/view/left_135")); 108 | mStopCommands.push_back(XPLMFindCommand("sim/view/right_135")); 109 | mStopCommands.push_back(XPLMFindCommand("sim/view/left_45")); 110 | mStopCommands.push_back(XPLMFindCommand("sim/view/right_45")); 111 | mStopCommands.push_back(XPLMFindCommand("sim/view/left_90")); 112 | mStopCommands.push_back(XPLMFindCommand("sim/view/right_90")); 113 | mStopCommands.push_back(XPLMFindCommand("sim/view/default_view")); 114 | mStopCommands.push_back(XPLMFindCommand("sim/view/back")); 115 | mStopCommands.push_back(XPLMFindCommand("sim/view/glance_left")); 116 | mStopCommands.push_back(XPLMFindCommand("sim/view/glance_right")); 117 | mStopCommands.push_back(XPLMFindCommand("sim/view/straight_down")); 118 | mStopCommands.push_back(XPLMFindCommand("sim/view/straight_up")); 119 | mStopCommands.push_back(XPLMFindCommand("sim/view/up_left")); 120 | mStopCommands.push_back(XPLMFindCommand("sim/view/up_right")); 121 | mStopCommands.push_back(XPLMFindCommand("sim/general/left")); 122 | mStopCommands.push_back(XPLMFindCommand("sim/general/right")); 123 | mStopCommands.push_back(XPLMFindCommand("sim/general/up")); 124 | mStopCommands.push_back(XPLMFindCommand("sim/general/down")); 125 | mStopCommands.push_back(XPLMFindCommand("sim/general/forward")); 126 | mStopCommands.push_back(XPLMFindCommand("sim/general/backward")); 127 | mStopCommands.push_back(XPLMFindCommand("sim/general/left_fast")); 128 | mStopCommands.push_back(XPLMFindCommand("sim/general/right_fast")); 129 | mStopCommands.push_back(XPLMFindCommand("sim/general/up_fast")); 130 | mStopCommands.push_back(XPLMFindCommand("sim/general/down_fast")); 131 | mStopCommands.push_back(XPLMFindCommand("sim/general/forward_fast")); 132 | mStopCommands.push_back(XPLMFindCommand("sim/general/backward_fast")); 133 | mStopCommands.push_back(XPLMFindCommand("sim/general/left_slow")); 134 | mStopCommands.push_back(XPLMFindCommand("sim/general/right_slow")); 135 | mStopCommands.push_back(XPLMFindCommand("sim/general/up_slow")); 136 | mStopCommands.push_back(XPLMFindCommand("sim/general/down_slow")); 137 | mStopCommands.push_back(XPLMFindCommand("sim/general/forward_slow")); 138 | mStopCommands.push_back(XPLMFindCommand("sim/general/backward_slow")); 139 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_left")); 140 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_right")); 141 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_up")); 142 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_down")); 143 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_left_fast")); 144 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_right_fast")); 145 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_up_fast")); 146 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_down_fast")); 147 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_left_slow")); 148 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_right_slow")); 149 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_up_slow")); 150 | mStopCommands.push_back(XPLMFindCommand("sim/general/rot_down_slow")); 151 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_up")); 152 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_up_right")); 153 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_right")); 154 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_down_right")); 155 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_down")); 156 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_down_left")); 157 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_left")); 158 | mStopCommands.push_back(XPLMFindCommand("sim/general/hat_switch_up_left")); 159 | 160 | mStopCommandsSize = mStopCommands.size(); 161 | } 162 | 163 | CameraControl::~CameraControl() 164 | { 165 | // Delete the commands and free the memory 166 | for (auto command : mCommands) { 167 | delete command; 168 | } 169 | mCommands.clear(); 170 | // Delete the stopcommands and free the memory 171 | mStopCommands.clear(); 172 | } 173 | 174 | // Static private: command callback that freezes the system when requested 175 | int CameraControl::freeze(XPLMCommandRef, XPLMCommandPhase, void*) 176 | { 177 | CameraControl::mInstance->freeze(); 178 | return 1; 179 | } 180 | 181 | // Static private: command callback that toggle the system when requested 182 | int CameraControl::toggle_hs(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 183 | { 184 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->set_enabled(!CameraControl::mInstance->get_enabled()); 185 | 186 | return 0; 187 | } 188 | 189 | int CameraControl::toggle_gforce(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 190 | { 191 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(0)->toggle(); 192 | return 0; 193 | } 194 | 195 | int CameraControl::toggle_lookahead(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 196 | { 197 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(1)->toggle(); 198 | return 0; 199 | } 200 | 201 | int CameraControl::toggle_pistonvib(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 202 | { 203 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(2)->toggle(); 204 | return 0; 205 | } 206 | 207 | int CameraControl::toggle_rotorvib(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 208 | { 209 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(3)->toggle(); 210 | return 0; 211 | } 212 | 213 | int CameraControl::toggle_groundroll(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 214 | { 215 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(4)->toggle(); 216 | return 0; 217 | } 218 | 219 | int CameraControl::toggle_taxilook(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 220 | { 221 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(5)->toggle(); 222 | return 0; 223 | } 224 | 225 | int CameraControl::toggle_touchdown(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 226 | { 227 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(6)->toggle(); 228 | return 0; 229 | } 230 | 231 | int CameraControl::toggle_levelhead(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 232 | { 233 | if (inPhase == xplm_CommandBegin) CameraControl::mInstance->mCommands.at(7)->toggle(); 234 | return 0; 235 | } 236 | 237 | void CameraControl::set_enabled(bool enabled) 238 | { 239 | if (enabled) { 240 | mInitialPos.pitch = XPLMGetDataf(mHeadPitchDataRef); 241 | mInitialPos.yaw = XPLMGetDataf(mHeadHeadingDataRef); 242 | mInitialPos.roll = XPLMGetDataf(mHeadRollDataRef); 243 | mInitialPos.x = XPLMGetDataf(mHeadXDataRef); 244 | mInitialPos.y = XPLMGetDataf(mHeadYDataRef); 245 | mInitialPos.z = XPLMGetDataf(mHeadZDataRef); 246 | } 247 | else { 248 | XPLMSetDataf(mHeadPitchDataRef, mInitialPos.pitch); 249 | XPLMSetDataf(mHeadHeadingDataRef, mInitialPos.yaw); 250 | XPLMSetDataf(mHeadRollDataRef, mInitialPos.roll); 251 | XPLMSetDataf(mHeadXDataRef, mInitialPos.x); 252 | XPLMSetDataf(mHeadYDataRef, mInitialPos.y); 253 | XPLMSetDataf(mHeadZDataRef, mInitialPos.z); 254 | } 255 | mEnabled = enabled; 256 | } 257 | 258 | bool CameraControl::get_enabled() const 259 | { 260 | return mEnabled; 261 | } 262 | 263 | void CameraControl::on_enable() 264 | { 265 | int datarefEditorId = XPLMFindPluginBySignature("xplanesdk.examples.DataRefEditor"); 266 | XPLMDataRef dref; 267 | mPositionInited = false; 268 | 269 | // Publish the override and offset datarefs 270 | dref = XPLMRegisterDataAccessor( 271 | "simcoders/headshake/override", xplmType_Int, 1, 272 | [](void* refCon) -> int { 273 | if (refCon) return ((CameraControl*)refCon)->get_override() ? 1 : 0; 274 | return 0; 275 | }, [](void* refCon, int value) -> void { 276 | if (refCon) { 277 | ((CameraControl*)refCon)->set_override(value == 1); 278 | } 279 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this); 280 | mDrefs.push_back(dref); 281 | dref = XPLMRegisterDataAccessor( 282 | "simcoders/headshake/offset_x", xplmType_Float, 0, NULL, NULL, 283 | [](void* refCon) -> float { 284 | if (refCon) return ((CameraControl*)refCon)->get_offset().x; 285 | return 0.0f; 286 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 287 | mDrefs.push_back(dref); 288 | dref = XPLMRegisterDataAccessor( 289 | "simcoders/headshake/offset_y", xplmType_Float, 0, NULL, NULL, 290 | [](void* refCon) -> float { 291 | if (refCon) return ((CameraControl*)refCon)->get_offset().y; 292 | return 0.0f; 293 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 294 | mDrefs.push_back(dref); 295 | dref = XPLMRegisterDataAccessor( 296 | "simcoders/headshake/offset_z", xplmType_Float, 0, NULL, NULL, 297 | [](void* refCon) -> float { 298 | if (refCon) return ((CameraControl*)refCon)->get_offset().z; 299 | return 0.0f; 300 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 301 | mDrefs.push_back(dref); 302 | 303 | dref = XPLMRegisterDataAccessor( 304 | "simcoders/headshake/offset_yaw", xplmType_Float, 0, NULL, NULL, 305 | [](void* refCon) -> float { 306 | if (refCon) return ((CameraControl*)refCon)->get_offset().yaw; 307 | return 0.0f; 308 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 309 | mDrefs.push_back(dref); 310 | 311 | dref = XPLMRegisterDataAccessor( 312 | "simcoders/headshake/offset_pitch", xplmType_Float, 0, NULL, NULL, 313 | [](void* refCon) -> float { 314 | if (refCon) return ((CameraControl*)refCon)->get_offset().pitch; 315 | return 0.0f; 316 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 317 | mDrefs.push_back(dref); 318 | 319 | dref = XPLMRegisterDataAccessor( 320 | "simcoders/headshake/offset_roll", xplmType_Float, 0, NULL, NULL, 321 | [](void* refCon) -> float { 322 | if (refCon) return ((CameraControl*)refCon)->get_offset().roll; 323 | return 0.0f; 324 | }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, NULL); 325 | mDrefs.push_back(dref); 326 | // Register the datarefs in the dataref editor 327 | if (datarefEditorId != XPLM_NO_PLUGIN_ID){ 328 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/override"); 329 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_x"); 330 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_y"); 331 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_z"); 332 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_yaw"); 333 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_pitch"); 334 | XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/offset_roll"); 335 | } 336 | 337 | // Register the commands 338 | for (unsigned int i = 0; i < mStopCommandsSize; i++) { 339 | if (mStopCommands.at(i) != NULL) { 340 | XPLMRegisterCommandHandler(mStopCommands.at(i), CameraControl::freeze, true, 0); 341 | } 342 | } 343 | 344 | // Send the on_enable to the commands 345 | for (auto command : mCommands) { 346 | command->on_enable(); 347 | } 348 | 349 | // Register the X-Plane commands 350 | XPLMRegisterCommandHandler(mEnabledCommand, CameraControl::toggle_hs, true, 0); 351 | XPLMRegisterCommandHandler(mGforceToggleCommand, CameraControl::toggle_gforce, true, 0); 352 | XPLMRegisterCommandHandler(mLookAheadToggleCommand, CameraControl::toggle_lookahead, true, 0); 353 | XPLMRegisterCommandHandler(mPistonVibToggleCommand, CameraControl::toggle_pistonvib, true, 0); 354 | XPLMRegisterCommandHandler(mRotorVibToggleCommand, CameraControl::toggle_rotorvib, true, 0); 355 | XPLMRegisterCommandHandler(mGroundRollToggleCommand, CameraControl::toggle_groundroll, true, 0); 356 | XPLMRegisterCommandHandler(mTaxiLookToggleCommand, CameraControl::toggle_taxilook, true, 0); 357 | XPLMRegisterCommandHandler(mTouchdownToggleCommand, CameraControl::toggle_touchdown, true, 0); 358 | XPLMRegisterCommandHandler(mLevelHeadToggleCommand, CameraControl::toggle_levelhead, true, 0); 359 | } 360 | 361 | void CameraControl::on_disable() 362 | { 363 | // Unpublish the offset datarefs 364 | for (unsigned int i = 0; i < mDrefs.size(); i++) { 365 | XPLMUnregisterDataAccessor(mDrefs.at(i)); 366 | } 367 | mDrefs.clear(); 368 | // Restore the initial camera position 369 | if (mPositionInited && !mOverride) { 370 | XPLMSetDataf(mHeadPitchDataRef, mInitialPos.pitch); 371 | XPLMSetDataf(mHeadHeadingDataRef, mInitialPos.yaw); 372 | XPLMSetDataf(mHeadRollDataRef, mInitialPos.roll); 373 | XPLMSetDataf(mHeadXDataRef, mInitialPos.x); 374 | XPLMSetDataf(mHeadYDataRef, mInitialPos.y); 375 | XPLMSetDataf(mHeadZDataRef, mInitialPos.z); 376 | } 377 | // Unregister the commands 378 | for (unsigned int i = 0; i < mStopCommandsSize; i++) { 379 | if (mStopCommands.at(i) != NULL) { 380 | XPLMUnregisterCommandHandler(mStopCommands.at(i), CameraControl::freeze, true, 0); 381 | } 382 | } 383 | // Send the on_enable to the commands 384 | for (auto command : mCommands) { 385 | command->on_disable(); 386 | } 387 | // Unregister the toggle commands 388 | XPLMUnregisterCommandHandler(mEnabledCommand, CameraControl::toggle_hs, true, 0); 389 | XPLMUnregisterCommandHandler(mGforceToggleCommand, CameraControl::toggle_gforce, true, 0); 390 | XPLMUnregisterCommandHandler(mLookAheadToggleCommand, CameraControl::toggle_lookahead, true, 0); 391 | XPLMUnregisterCommandHandler(mPistonVibToggleCommand, CameraControl::toggle_pistonvib, true, 0); 392 | XPLMUnregisterCommandHandler(mRotorVibToggleCommand, CameraControl::toggle_rotorvib, true, 0); 393 | XPLMUnregisterCommandHandler(mGroundRollToggleCommand, CameraControl::toggle_groundroll, true, 0); 394 | XPLMUnregisterCommandHandler(mTaxiLookToggleCommand, CameraControl::toggle_taxilook, true, 0); 395 | XPLMUnregisterCommandHandler(mTouchdownToggleCommand, CameraControl::toggle_touchdown, true, 0); 396 | XPLMUnregisterCommandHandler(mLevelHeadToggleCommand, CameraControl::toggle_levelhead, true, 0); 397 | } 398 | 399 | void CameraControl::reset_view() 400 | { 401 | if (mPositionInited && !mOverride) { 402 | XPLMSetDataf(mHeadPitchDataRef, mInitialPos.pitch); 403 | XPLMSetDataf(mHeadHeadingDataRef, mInitialPos.yaw); 404 | XPLMSetDataf(mHeadRollDataRef, mInitialPos.roll); 405 | XPLMSetDataf(mHeadXDataRef, mInitialPos.x); 406 | XPLMSetDataf(mHeadYDataRef, mInitialPos.y); 407 | XPLMSetDataf(mHeadZDataRef, mInitialPos.z); 408 | } 409 | } 410 | 411 | void CameraControl::freeze() 412 | { 413 | mFreezed1 = true; 414 | mFreezed2 = true; 415 | } 416 | 417 | // Control the camera calling all the active modules 418 | float CameraControl::control(float elapsedTime) 419 | { 420 | if (!mEnabled) { 421 | return 1; 422 | } 423 | 424 | CameraPosition currentPos, calculatedPos; 425 | int cameraType = XPLMGetDatai(mViewTypeDataRef); 426 | 427 | // If the camera type is changed, notify the objects passing the new camera type 428 | if (cameraType != mLastCameraType) { 429 | mLastCameraType = cameraType; 430 | for (auto command : mCommands) { 431 | command->on_view_changed(cameraType); 432 | } 433 | } 434 | // Make sure that we're in the virtual cockpit, cinema verite is not active and the sim is not paused 435 | // Note: if gLoaded camera is defined then we don't care about the state of CinemaVerite 436 | mError = (mG_LoadedCameraDataRef == nullptr) ? XPLMGetDatai(mCinemaVeriteDataRef) > 0 : false; 437 | if (cameraType != 1026 || XPLMGetDatai(mPausedDataRef) || mError) { 438 | return 1; 439 | } 440 | 441 | // If not overridden, read the current camera position 442 | if (!mOverride) { 443 | currentPos.pitch = XPLMGetDataf(mHeadPitchDataRef); 444 | currentPos.yaw = XPLMGetDataf(mHeadHeadingDataRef); 445 | currentPos.roll = XPLMGetDataf(mHeadRollDataRef); 446 | currentPos.x = XPLMGetDataf(mHeadXDataRef); 447 | currentPos.y = XPLMGetDataf(mHeadYDataRef); 448 | currentPos.z = XPLMGetDataf(mHeadZDataRef); 449 | } 450 | 451 | if (!mPositionInited && (std::abs(currentPos.x) > 0 || std::abs(currentPos.y) > 0 || std::abs(currentPos.y) > 0)) { 452 | // Save the initial camera position 453 | mInitialPos = currentPos; 454 | mPositionInited = true; 455 | } 456 | 457 | // If the current position has changed since the last time we set it, 458 | // start monitoring it and wait for it to set 459 | if (mFreezed2) { 460 | if (!mFreezed1 && currentPos == mLastPos) 461 | mFreezed2 = false; 462 | mFreezed1 = false; 463 | mLastPos = currentPos; 464 | 465 | for (auto command : mCommands) { 466 | command->reset_blend(); 467 | } 468 | 469 | return -2; 470 | } 471 | 472 | for (auto command : mCommands) { 473 | command->execute(calculatedPos, elapsedTime); 474 | } 475 | 476 | // If not overridden, update the camera position 477 | if (!mOverride) { 478 | // Detect the joystick axis used for camera movements 479 | float yawValue = 0, pitchValue = 0; 480 | int values[100]; 481 | XPLMGetDatavi(mJoyAxisAssignments, values, 0, 100); 482 | for (unsigned int i = 0; i < 100; i++) { 483 | if (values[i] == 41) { 484 | XPLMGetDatavf(mJoyAxisValues, &yawValue, i, 1); 485 | } 486 | else if (values[i] == 42) { 487 | XPLMGetDatavf(mJoyAxisValues, &pitchValue, i, 1); 488 | } 489 | } 490 | 491 | if (yawValue - mLastJoyYaw < -0.001 || yawValue - mLastJoyYaw > 0.001 || pitchValue - mLastJoyPitch < -0.001 || pitchValue - mLastJoyPitch > 0.001) { 492 | mLastJoyPitch = pitchValue; 493 | mLastJoyYaw = yawValue; 494 | return -1; 495 | } 496 | 497 | mLastJoyPitch = pitchValue; 498 | mLastJoyYaw = yawValue; 499 | 500 | currentPos = currentPos + calculatedPos; 501 | // Set the current camera position 502 | XPLMSetDataf(mHeadPitchDataRef, std::max(std::min(currentPos.pitch, 89.0f), -89.0f)); // Limit the pitch to -89°/+89° 503 | XPLMSetDataf(mHeadHeadingDataRef, currentPos.yaw); 504 | // Do not write the roll if the multimonitor compatibility is turned on 505 | if (mXpVersion < 1102) { 506 | if (!mMultimonitorCompatibility) { 507 | XPLMSetDataf(mHeadRollDataRef, currentPos.roll); 508 | } 509 | } else { 510 | XPLMSetDataf(mHeadRollDataRef, currentPos.roll); 511 | } 512 | XPLMSetDataf(mHeadXDataRef, currentPos.x); 513 | XPLMSetDataf(mHeadYDataRef, currentPos.y); 514 | XPLMSetDataf(mHeadZDataRef, currentPos.z); 515 | } 516 | // Set the position offset 517 | mCameraOffset = mCameraOffset + calculatedPos; 518 | // Save the updated position 519 | mLastPos = currentPos; 520 | 521 | return -1; 522 | } 523 | 524 | CameraPosition CameraControl::get_offset() 525 | { 526 | return mCameraOffset; 527 | } 528 | 529 | void CameraControl::set_override(bool value) 530 | { 531 | mOverride = value; 532 | } 533 | 534 | bool CameraControl::get_override() const 535 | { 536 | return mOverride; 537 | } 538 | 539 | void CameraControl::accept(IVisitor &visitor) 540 | { 541 | visitor.visit(*this); 542 | for (auto command : mCommands) { 543 | command->accept(visitor); 544 | } 545 | } 546 | 547 | bool CameraControl::error() 548 | { 549 | return mError; 550 | } 551 | 552 | bool CameraControl::get_multimonitor_compatibility() const 553 | { 554 | return mMultimonitorCompatibility; 555 | } 556 | 557 | void CameraControl::set_multimonitor_compatibility(bool compatibility) 558 | { 559 | mMultimonitorCompatibility = compatibility; 560 | } 561 | 562 | int CameraControl::get_xp_version() 563 | { 564 | return mXpVersion; 565 | } 566 | -------------------------------------------------------------------------------- /src/cameracontrol.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERACONTROL_H 2 | #define CAMERACONTROL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "cameracommands/cameracommand.h" 10 | #include "cameraposition.h" 11 | #include "interfaces/ivisitable.h" 12 | 13 | struct CameraPosition; 14 | class CameraControl : IVisitable 15 | { 16 | public: 17 | static CameraControl* get_instance(); 18 | float control(float); 19 | void accept(IVisitor&); 20 | void on_enable(); 21 | void on_disable(); 22 | bool get_multimonitor_compatibility() const; 23 | void set_multimonitor_compatibility(bool); 24 | bool error(); 25 | CameraPosition get_offset(); 26 | bool get_override() const; 27 | int get_xp_version(); 28 | void set_override(bool); 29 | void set_enabled(bool); 30 | void reset_view(); 31 | bool get_enabled() const; 32 | protected: 33 | private: 34 | /** Singleton */ 35 | CameraControl(); // Private so that it can not be called 36 | CameraControl(CameraControl const&){}; // copy constructor is private 37 | CameraControl& operator=(CameraControl const&){ return *mInstance; }; // assignment operator is private 38 | 39 | static int freeze(XPLMCommandRef, XPLMCommandPhase, void*); 40 | static int toggle_hs(XPLMCommandRef, XPLMCommandPhase, void*); 41 | static int toggle_gforce(XPLMCommandRef, XPLMCommandPhase, void*); 42 | static int toggle_lookahead(XPLMCommandRef, XPLMCommandPhase, void*); 43 | static int toggle_pistonvib(XPLMCommandRef, XPLMCommandPhase, void*); 44 | static int toggle_rotorvib(XPLMCommandRef, XPLMCommandPhase, void*); 45 | static int toggle_groundroll(XPLMCommandRef, XPLMCommandPhase, void*); 46 | static int toggle_taxilook(XPLMCommandRef, XPLMCommandPhase, void*); 47 | static int toggle_touchdown(XPLMCommandRef, XPLMCommandPhase, void*); 48 | static int toggle_levelhead(XPLMCommandRef, XPLMCommandPhase, void*); 49 | 50 | static CameraControl* mInstance; 51 | virtual ~CameraControl(); 52 | /** Implementation */ 53 | void freeze(); 54 | std::vector mCommands; 55 | CameraPosition mLastPos; 56 | CameraPosition mInitialPos; 57 | CameraPosition mCameraOffset; 58 | int mLastCameraType; 59 | int mXpVersion; 60 | bool mPositionInited; 61 | bool mFreezed1; 62 | bool mFreezed2; 63 | bool mEnabled; 64 | bool mError; 65 | bool mMultimonitorCompatibility; 66 | bool mOverride; 67 | float mLastJoyPitch; 68 | float mLastJoyYaw; 69 | /** Published DataRefs **/ 70 | XPLMDataRef mOverrideDataRef; 71 | XPLMDataRef mXDataRef; 72 | XPLMDataRef mYDataRef; 73 | XPLMDataRef mZDataRef; 74 | XPLMDataRef mYawDataRef; 75 | XPLMDataRef mPitchDataRef; 76 | XPLMDataRef mRollDataRef; 77 | /** DataRefs */ 78 | XPLMDataRef mPausedDataRef; 79 | XPLMDataRef mCinemaVeriteDataRef; 80 | XPLMDataRef mG_LoadedCameraDataRef; 81 | XPLMDataRef mViewTypeDataRef; 82 | XPLMDataRef mHeadHeadingDataRef; 83 | XPLMDataRef mHeadPitchDataRef; 84 | XPLMDataRef mHeadRollDataRef; 85 | XPLMDataRef mHeadXDataRef; 86 | XPLMDataRef mHeadYDataRef; 87 | XPLMDataRef mHeadZDataRef; 88 | XPLMDataRef mJoyAxisValues; 89 | XPLMDataRef mJoyAxisAssignments; 90 | /** Commands */ 91 | XPLMCommandRef mEnabledCommand; 92 | XPLMCommandRef mStopCommand; 93 | XPLMCommandRef mGforceToggleCommand; 94 | XPLMCommandRef mLookAheadToggleCommand; 95 | XPLMCommandRef mPistonVibToggleCommand; 96 | XPLMCommandRef mRotorVibToggleCommand; 97 | XPLMCommandRef mGroundRollToggleCommand; 98 | XPLMCommandRef mTaxiLookToggleCommand; 99 | XPLMCommandRef mTouchdownToggleCommand; 100 | XPLMCommandRef mLevelHeadToggleCommand; 101 | std::vector mStopCommands; 102 | std::vector mDrefs; 103 | unsigned int mStopCommandsSize; 104 | }; 105 | 106 | #endif // CAMERACONTROL_H 107 | -------------------------------------------------------------------------------- /src/cameraposition.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERAPOSITION_H 2 | #define CAMERAPOSITION_H 3 | 4 | #include 5 | #include 6 | 7 | #define EPSILON 1e-12 8 | 9 | struct CameraPosition 10 | { 11 | public: 12 | float pitch; 13 | float roll; 14 | float yaw; 15 | float x; 16 | float y; 17 | float z; 18 | 19 | CameraPosition() 20 | { 21 | pitch = 0; 22 | roll = 0; 23 | yaw = 0; 24 | x = 0; 25 | y =0; 26 | z = 0; 27 | } 28 | 29 | CameraPosition operator-(const CameraPosition &other) 30 | { 31 | CameraPosition pos; 32 | 33 | pos.x = x - other.x; 34 | pos.y = y - other.y; 35 | pos.z = z - other.z; 36 | pos.yaw = yaw - other.yaw; 37 | pos.pitch = pitch - other.pitch; 38 | pos.roll = roll - other.roll; 39 | 40 | return pos; 41 | } 42 | 43 | CameraPosition operator+(const CameraPosition &other) 44 | { 45 | CameraPosition pos; 46 | 47 | pos.x = x + other.x; 48 | pos.y = y + other.y; 49 | pos.z = z + other.z; 50 | pos.yaw = yaw + other.yaw; 51 | pos.pitch = pitch + other.pitch; 52 | pos.roll = roll + other.roll; 53 | 54 | return pos; 55 | } 56 | 57 | bool operator==(const CameraPosition& other) 58 | { 59 | return fabs(roll - other.roll) < EPSILON && 60 | fabs(pitch - other.pitch) < EPSILON && 61 | fabs(yaw - other.yaw) < EPSILON && 62 | fabs(x - other.x) < EPSILON && 63 | fabs(y - other.y) < EPSILON && 64 | fabs(z - other.z) < EPSILON; 65 | }; 66 | 67 | bool operator!=(const CameraPosition& other) 68 | { 69 | return !(*this == other); 70 | }; 71 | 72 | char* to_string() 73 | { 74 | char* buf = new char[1024]; 75 | sprintf(buf, "pitch=%f roll=%f yaw=%f x=%f y=%f z=%f\n", pitch, roll, yaw, x, y, z); 76 | return buf; 77 | } 78 | protected: 79 | private: 80 | }; 81 | 82 | #endif // CAMERAPOSITION_H 83 | -------------------------------------------------------------------------------- /src/collisions: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | XPluginStart; 4 | XPluginStop; 5 | XPluginEnable; 6 | XPluginDisable; 7 | XPluginReceiveMessage; 8 | local: 9 | *; 10 | }; 11 | -------------------------------------------------------------------------------- /src/helpers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | File: helpers.cpp 3 | 4 | This file contains some useful mathematic functions. 5 | 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | 19 | /* 20 | Define a continue log function. 21 | y = -log(|x - 1|) when x < 0 22 | y = log(|x + 1|) when x >= 0 23 | */ 24 | float continue_log(float x) 25 | { 26 | if (x < 0) 27 | return -log(std::fabs(x - 1)); 28 | else 29 | return log(x + 1); 30 | } 31 | 32 | /* 33 | Get the average of the vector. 34 | */ 35 | float average(const std::vector &v) 36 | { 37 | float average = 0; 38 | unsigned int i, arrSize = v.size(); 39 | 40 | if (arrSize == 0) 41 | return 0; 42 | 43 | for (i = 0; i < arrSize; i++) { 44 | average += v.at(i); 45 | } 46 | return average / arrSize; 47 | } 48 | 49 | /* 50 | Get a weighted average of the vector. 51 | The first element is the more weighted. 52 | */ 53 | float weighted_average(const std::vector &v) 54 | { 55 | float average = 0; 56 | float count = 0; 57 | float farrSize = (float)v.size(); 58 | unsigned int iarrSize = v.size(); 59 | unsigned int i; 60 | 61 | if (iarrSize == 0) 62 | return 0; 63 | 64 | for (i = 0; i < iarrSize; i++) { 65 | average += v.at(i) * (farrSize - i); 66 | count += (farrSize - i); 67 | } 68 | return average / count; 69 | } 70 | 71 | /* 72 | Get the settings file path 73 | */ 74 | bool get_settings_filepath(char *path) 75 | { 76 | XPLMGetPrefsPath(path); 77 | // for an SDK bug which is there since 6.70, the returned path is not 78 | // the folder's path but the path of the file "Set X-Plane.prf". So let's 79 | // strip out the filename to get the folder name. 80 | XPLMExtractFileAndPath(path); 81 | // Add a directory separator to the end 82 | // (the function above modifies path with no trailing directory separator) 83 | strcat(path, XPLMGetDirectorySeparator()); 84 | // Add the correct filename 85 | strcat(path, "HeadShake.prf"); 86 | return true; 87 | } 88 | -------------------------------------------------------------------------------- /src/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef MHELPERS_H 2 | #define MHELPERS_H 3 | 4 | /* 5 | File: helpers.h 6 | 7 | This file contains some useful mathematic functions. 8 | 9 | */ 10 | 11 | #include 12 | 13 | float continue_log(float x); 14 | 15 | /* 16 | Get the average of the vector. 17 | The first element is the more weighted. 18 | */ 19 | float average(const std::vector &v); 20 | 21 | /* 22 | Get a weighted average of the vector. 23 | The first element is the more weighted. 24 | */ 25 | float weighted_average(const std::vector &v); 26 | 27 | /* 28 | Get the settings file path 29 | */ 30 | bool get_settings_filepath(char *path); 31 | 32 | #endif // HELPERS_H 33 | -------------------------------------------------------------------------------- /src/interfaces/ivisitable.h: -------------------------------------------------------------------------------- 1 | #ifndef IVISITABLE_H 2 | #define IVISITABLE_H 3 | 4 | class IVisitor; 5 | class IVisitable 6 | { 7 | public: 8 | /** Default constructor */ 9 | IVisitable() {} 10 | /** Default destructor */ 11 | virtual ~IVisitable() {} 12 | virtual void accept(IVisitor&) = 0; 13 | }; 14 | 15 | #endif // IVISITABLE_H 16 | -------------------------------------------------------------------------------- /src/interfaces/ivisitor.h: -------------------------------------------------------------------------------- 1 | #ifndef IVISITOR_H 2 | #define IVISITOR_H 3 | 4 | class CameraControl; 5 | class GForceCameraCommand; 6 | class LookAheadCameraCommand; 7 | class PistonEngineCameraCommand; 8 | class RotorCameraCommand; 9 | class GroundRollCameraCommand; 10 | class TaxiLookAheadCameraCommand; 11 | class TouchdownCameraCommand; 12 | class LevelHeadCameraCommand; 13 | 14 | class IVisitor 15 | { 16 | public: 17 | /** Default constructor */ 18 | IVisitor() {} 19 | /** Default destructor */ 20 | virtual ~IVisitor() {} 21 | virtual void visit(CameraControl&) = 0; 22 | virtual void visit(GForceCameraCommand&) = 0; 23 | virtual void visit(LookAheadCameraCommand&) = 0; 24 | virtual void visit(PistonEngineCameraCommand&) = 0; 25 | virtual void visit(RotorCameraCommand&) = 0; 26 | virtual void visit(GroundRollCameraCommand&) = 0; 27 | virtual void visit(TaxiLookAheadCameraCommand&) = 0; 28 | virtual void visit(TouchdownCameraCommand&) = 0; 29 | virtual void visit(LevelHeadCameraCommand&) = 0; 30 | }; 31 | 32 | #endif // IVISITOR_H 33 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * main.cpp 3 | * 4 | */ 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "cameracontrol.h" 14 | #include "menu.h" 15 | #include "settings/settingswriter.h" 16 | #include "settings/settingsreader.h" 17 | 18 | // Define the callbacks 19 | float FlightLoopCallback(float elapsedMe, float elapsedSim, int counter, void *refcon); 20 | 21 | PLUGIN_API int XPluginStart( 22 | char * outName, 23 | char * outSig, 24 | char * outDesc) 25 | { 26 | // Define the plugin properties 27 | strcpy(outName, "SimCoders - HeadShake 1.14.0"); 28 | strcpy(outSig, "com.simcoders.headshake"); 29 | strcpy(outDesc, "A plugin that moves the view in the virtual cockpit basing on the g force values."); 30 | // Setup the features 31 | XPLMEnableFeature("XPLM_USE_NATIVE_PATHS", 1); 32 | return 1; 33 | } 34 | 35 | PLUGIN_API void XPluginStop(void) 36 | { 37 | CameraControl::get_instance()->reset_view(); 38 | } 39 | 40 | PLUGIN_API int XPluginEnable(void) 41 | { 42 | SettingsReader *reader; 43 | 44 | // Enable the camera 45 | CameraControl::get_instance()->on_enable(); 46 | // Read the settings 47 | reader = new SettingsReader; 48 | CameraControl::get_instance()->accept(*reader); 49 | delete reader; 50 | // Create the menu entry 51 | Menu::get_instance()->create_menu_entry(*(CameraControl::get_instance())); 52 | // Register the callbacks 53 | XPLMRegisterFlightLoopCallback(FlightLoopCallback, -1, 0); 54 | return 1; 55 | } 56 | 57 | PLUGIN_API void XPluginDisable(void) 58 | { 59 | SettingsWriter *writer; 60 | 61 | // Unregister the callbacks 62 | XPLMUnregisterFlightLoopCallback(FlightLoopCallback, 0); 63 | CameraControl::get_instance()->on_disable(); 64 | // Save the settings 65 | writer = new SettingsWriter; 66 | writer->open(); 67 | CameraControl::get_instance()->accept(*writer); 68 | writer->close(); 69 | delete writer; 70 | 71 | Menu::get_instance()->destroy_menu_entry(); 72 | } 73 | 74 | PLUGIN_API void XPluginReceiveMessage( 75 | XPLMPluginID, 76 | long, 77 | void*) 78 | { 79 | 80 | } 81 | 82 | float FlightLoopCallback( 83 | float elapsedTime, // Time since the last call 84 | float, // Time since the last loop 85 | int, // Loop counter 86 | void*) 87 | { 88 | return CameraControl::get_instance()->control(elapsedTime); 89 | } 90 | -------------------------------------------------------------------------------- /src/menu.cpp: -------------------------------------------------------------------------------- 1 | #if !defined(XPLM210) 2 | #define XPLM210 3 | #endif 4 | 5 | #include // defines NULL 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "menu.h" 16 | #include "cameracommands/gforcecameracommand.h" 17 | #include "cameracommands/lookaheadcameracommand.h" 18 | #include "cameracommands/pistonenginecameracommand.h" 19 | #include "cameracommands/rotorcameracommand.h" 20 | #include "cameracommands/groundrollcameracommand.h" 21 | #include "cameracommands/taxilookaheadcameracommand.h" 22 | #include "cameracommands/touchdowncameracommand.h" 23 | #include "cameracommands/levelheadcameracommand.h" 24 | 25 | // Custom messages 26 | #define UPDATE_GFORCE_PITCH 1 27 | #define UPDATE_GFORCE_YAW 2 28 | #define UPDATE_GFORCE_ACCELERATION 3 29 | #define UPDATE_GFORCE_LOOKAHEAD 4 30 | #define UPDATE_GROUNDROLL 5 31 | #define UPDATE_TAXI_LOOKAHEAD 6 32 | #define UPDATE_PISTONENGINE 7 33 | #define UPDATE_ROTOR 8 34 | #define UPDATE_TOUCHDOWN 9 35 | #define UPDATE_LEVELHEAD_RESP 10 36 | #define UPDATE_LEVELHEAD_BANK 11 37 | 38 | /** 39 | |------------------------------- 40 | | Singleton and menu loader 41 | |------------------------------- 42 | */ 43 | 44 | Menu* Menu::mInstance = NULL; 45 | Menu* Menu::get_instance() 46 | { 47 | if (!mInstance) 48 | mInstance = new Menu; 49 | return mInstance; 50 | } 51 | 52 | // Create the main menu entry SimCoders->HeadShake 53 | void Menu::create_menu_entry(CameraControl& control) 54 | { 55 | mCameraControl = &control; 56 | mId = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "SimCoders - HeadShake", 0, 1); 57 | XPLMMenuID subMenuId = XPLMCreateMenu("Settings", XPLMFindPluginsMenu(), mId, [](void*, void*) -> void { 58 | // When clicked, start visiting the control and the commands and draw the widgets 59 | Menu::mInstance->show(); 60 | }, 0); 61 | XPLMAppendMenuItem(subMenuId, "Settings", (void*)1, 1); 62 | 63 | mToggleCommand = XPLMCreateCommand("simcoders/headshake/toggle_menu", "Open/Close the settings menu"); 64 | XPLMRegisterCommandHandler(mToggleCommand, Menu::toggle, true, 0); 65 | } 66 | 67 | void Menu::destroy_menu_entry() 68 | { 69 | XPLMRemoveMenuItem(XPLMFindPluginsMenu(), mId); 70 | XPLMUnregisterCommandHandler(mToggleCommand, Menu::toggle, true, 0); 71 | } 72 | 73 | // Static: used to toggle the menu 74 | int Menu::toggle(XPLMCommandRef, XPLMCommandPhase inPhase, void*) 75 | { 76 | if (inPhase == xplm_CommandBegin) { 77 | if (Menu::mInstance->mWidgetId == nullptr) { 78 | Menu::mInstance->show(); 79 | } 80 | else { 81 | XPDestroyWidget(Menu::mInstance->mWidgetId, 1); 82 | Menu::mInstance->mWidgetId = nullptr; 83 | } 84 | } 85 | return 1; 86 | } 87 | 88 | void Menu::show() 89 | { 90 | mCameraControl->accept(*mInstance); 91 | } 92 | 93 | /** 94 | |---------------------------------- 95 | | Widget creation and handling 96 | |---------------------------------- 97 | */ 98 | 99 | Menu::Menu() 100 | { 101 | // Define the main window width 102 | // The other values are calculated at run time 103 | mWidth = 1050; 104 | mAdsHeight = 100; 105 | mWidgetId = nullptr; 106 | } 107 | 108 | Menu::~Menu() 109 | { 110 | 111 | } 112 | 113 | // Create the main window and save the window id 114 | void Menu::visit(CameraControl& control) 115 | { 116 | int w, h, x1, x2; 117 | XPWidgetID subw, enableButton; 118 | XPLMGetScreenSize(&w, &h); 119 | mHeight = (control.error() ? 490 : 440) + mAdsHeight; 120 | mLeft = (w - mWidth) / 2; 121 | mTop = (h + mHeight) / 2; 122 | mRight = mLeft + mWidth; 123 | mBottom = mTop - mHeight; 124 | mShowAds = XPLMFindPluginBySignature("com.simcoders.rep") == XPLM_NO_PLUGIN_ID; 125 | 126 | // Create the Main Widget window 127 | mWidgetId = XPCreateWidget(mLeft, mTop, mRight, mBottom, 1, "SimCoders - HeadShake", 1, 0, xpWidgetClass_MainWindow); 128 | XPSetWidgetProperty(mWidgetId, xpProperty_MainWindowHasCloseBoxes, 1); 129 | XPAddWidgetCallback(mWidgetId, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 130 | // When the close buttons are clicked, close the window 131 | if (inMessage == xpMessage_CloseButtonPushed) { 132 | XPDestroyWidget(inWidget, 1); 133 | Menu::mInstance->mWidgetId = nullptr; 134 | return 1; 135 | } 136 | return 0; 137 | }); 138 | // On error show a warning caption 139 | if (control.error()) { 140 | mTop -= 50; 141 | XPCreateWidget(mLeft + 10, mTop, mRight - 10, mTop - 1, 1, "WARNING: This plugin does not work when Cinema Verite is turned on.", 0, mWidgetId, xpWidgetClass_Caption); 142 | } 143 | 144 | // Set the REP reference 145 | int y = mTop - 23; 146 | x1 = mLeft + 20; 147 | x2 = mRight - 20; 148 | subw = XPCreateWidget(mLeft + 10, y, mRight - 10, y - mAdsHeight, 1, "Reality Expansion Pack Integration", 0, mWidgetId, xpWidgetClass_SubWindow); 149 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 150 | // Add the description 151 | XPCreateWidget(x1 - 5, y, x2, y - 30, 1, "REALITY EXPANSION PACK INTEGRATION", 0, mWidgetId, xpWidgetClass_Caption); 152 | if (mShowAds) { 153 | XPCreateWidget(x1 - 5, y, x2, y - 90, 1, "HeadShake can work in pair with the Reality Expansion Pack, our hyper-realistic addon for X-Plane.", 0, mWidgetId, xpWidgetClass_Caption); 154 | XPCreateWidget(x1 - 5, y, x2, y - 120, 1, "It adds a damage & maintenance system, realistic flight dynamics and much more to X-Plane.", 0, mWidgetId, xpWidgetClass_Caption); 155 | XPCreateWidget(x1 - 5, y, x2, y - 150, 1, "Get more informations at https://www.simcoders.com/rep", 0, mWidgetId, xpWidgetClass_Caption); 156 | } 157 | else { 158 | XPCreateWidget(x1 - 5, y, x2, y - 85, 1, "HeadShake is correctly working in pair with the Reality Expansion Pack.", 0, mWidgetId, xpWidgetClass_Caption); 159 | XPCreateWidget(x1 - 5, y, x2, y - 130, 1, "Thank you for being a REP licence holder. We are a small company and your help is very much appreciated.", 0, mWidgetId, xpWidgetClass_Caption); 160 | XPCreateWidget(x1 - 5, y, x2, y - 160, 1, "We wish you blue skies with REP and HeadShake!", 0, mWidgetId, xpWidgetClass_Caption); 161 | } 162 | 163 | // Move all the widgets down to make space for the ads 164 | mTop -= mAdsHeight; 165 | 166 | // Create the general "Enable/Disable" menu 167 | y = mTop - 30; 168 | x1 = mLeft + 10; 169 | x2 = x1 + mWidth / 3 - 20; 170 | subw = XPCreateWidget(x1, y, x2, y - 158, 1, "General", 0, mWidgetId, xpWidgetClass_SubWindow); 171 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 172 | x1 += 10; 173 | x2 -= 10; 174 | y -= 10; 175 | // Add the compatibility checkbox 176 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable HeadShake", 0, mWidgetId, xpWidgetClass_Button); 177 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 178 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 179 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, control.get_enabled()); 180 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 181 | int inExit; 182 | if (inMessage == xpMsg_ButtonStateChanged) { 183 | Menu::mInstance->mCameraControl->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 184 | return 1; 185 | } 186 | return 0; 187 | }); 188 | y -= 10; 189 | // Add the description 190 | XPCreateWidget(x1, y, x1 + 10, y - 30, 1, "This is the general toggle for HeadShake.", 0, mWidgetId, xpWidgetClass_Caption); 191 | XPCreateWidget(x1, y, x1 + 10, y - 60, 1, "Enable it if you want to use this plugin.", 0, mWidgetId, xpWidgetClass_Caption); 192 | XPCreateWidget(x1, y, x1 + 10, y - 120, 1, "The following commands are also available:", 0, mWidgetId, xpWidgetClass_Caption); 193 | XPCreateWidget(x1, y, x1 + 10, y - 150, 1, " - simcoders/headshake/toggle_headshake", 0, mWidgetId, xpWidgetClass_Caption); 194 | XPCreateWidget(x1, y, x1 + 10, y - 180, 1, " - simcoders/headshake/toggle_menu", 0, mWidgetId, xpWidgetClass_Caption); 195 | 196 | visit_multimonitor(control.get_multimonitor_compatibility()); 197 | } 198 | 199 | // Create and handle the gforce subwindow 200 | void Menu::visit(GForceCameraCommand& command) 201 | { 202 | XPWidgetID subw; 203 | int x1, x2, y = mTop - 188 - 10; 204 | XPWidgetID enableButton; 205 | XPWidgetID pitchScrollbar; 206 | XPWidgetID pitchLabel; 207 | XPWidgetID accelerationScrollbar; 208 | XPWidgetID accelerationLabel; 209 | XPWidgetID yawScrollbar; 210 | XPWidgetID yawLabel; 211 | char buffer[64]; 212 | 213 | mGforceCameraCommand = &command; 214 | 215 | subw = XPCreateWidget(mLeft + 10, y, mLeft + (mWidth / 3) - 10, y - 150, 1, "GForce Effect Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 216 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 217 | 218 | x1 = mLeft + 20; 219 | x2 = mLeft + mWidth / 3 - 20; 220 | 221 | // Add the enable checkbox 222 | y = y - 10; 223 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the g-force effects", 0, mWidgetId, xpWidgetClass_Button); 224 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 225 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 226 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 227 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 228 | int inExit; 229 | if (inMessage == xpMsg_ButtonStateChanged) { 230 | Menu::mInstance->mGforceCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 231 | return 1; 232 | } 233 | return 0; 234 | }); 235 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_gforce", 0, mWidgetId, xpWidgetClass_Caption); 236 | // Add the pitch sensitivity label + scrollbar 237 | y = y - 20; 238 | sprintf(buffer, "Pitch response: %.0f", mGforceCameraCommand->get_pitch_response()); 239 | pitchLabel = XPCreateWidget(x1 - 5, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 240 | // On message received update the label 241 | XPAddWidgetCallback(pitchLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 242 | if (inMessage == xpMsg_UserStart + UPDATE_GFORCE_PITCH) { 243 | char mbuffer[32]; 244 | sprintf(mbuffer, "Pitch response: %.0f", Menu::mInstance->mGforceCameraCommand->get_pitch_response()); 245 | XPSetWidgetDescriptor(inWidget, mbuffer); 246 | return 1; 247 | } 248 | return 0; 249 | }); 250 | y = y - 20; 251 | pitchScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 252 | XPSetWidgetProperty(pitchScrollbar, xpProperty_ScrollBarMin, 1); 253 | XPSetWidgetProperty(pitchScrollbar, xpProperty_ScrollBarMax, 100); 254 | XPSetWidgetProperty(pitchScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_pitch_response()); 255 | XPSetWidgetProperty(pitchScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 256 | XPAddWidgetCallback(pitchScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 257 | int inExit; 258 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 259 | Menu::mInstance->mGforceCameraCommand->set_pitch_response((float)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 260 | // Update the label 261 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_GFORCE_PITCH, xpMode_Recursive, 0, 0); 262 | return 1; 263 | } 264 | return 0; 265 | }); 266 | 267 | // Add the yaw sensitivity label + scrollbar 268 | y = y - 20; 269 | sprintf(buffer, "Yaw response: %.0f", mGforceCameraCommand->get_yaw_response()); 270 | yawLabel = XPCreateWidget(x1 - 5, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 271 | // On message received update the label 272 | XPAddWidgetCallback(yawLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 273 | if (inMessage == xpMsg_UserStart + UPDATE_GFORCE_YAW) { 274 | char mbuffer[32]; 275 | sprintf(mbuffer, "Yaw response: %.0f", Menu::mInstance->mGforceCameraCommand->get_yaw_response()); 276 | XPSetWidgetDescriptor(inWidget, mbuffer); 277 | return 1; 278 | } 279 | return 0; 280 | }); 281 | y = y - 20; 282 | yawScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 283 | XPSetWidgetProperty(yawScrollbar, xpProperty_ScrollBarMin, 1); 284 | XPSetWidgetProperty(yawScrollbar, xpProperty_ScrollBarMax, 100); 285 | XPSetWidgetProperty(yawScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_yaw_response()); 286 | XPSetWidgetProperty(yawScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 287 | XPAddWidgetCallback(yawScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 288 | int inExit; 289 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 290 | Menu::mInstance->mGforceCameraCommand->set_yaw_response((float)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 291 | // Update the label 292 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_GFORCE_YAW, xpMode_Recursive, 0, 0); 293 | return 1; 294 | } 295 | return 0; 296 | }); 297 | 298 | // Add the acceleration sensitivity label + scrollbar 299 | y = y - 20; 300 | sprintf(buffer, "Speed change response: %.0f", mGforceCameraCommand->get_acceleration_response()); 301 | accelerationLabel = XPCreateWidget(x1 - 5, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 302 | // On message received update the label 303 | XPAddWidgetCallback(accelerationLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 304 | if (inMessage == xpMsg_UserStart + UPDATE_GFORCE_ACCELERATION) { 305 | char mbuffer[32]; 306 | sprintf(mbuffer, "Speed change response: %.0f", Menu::mInstance->mGforceCameraCommand->get_acceleration_response()); 307 | XPSetWidgetDescriptor(inWidget, mbuffer); 308 | return 1; 309 | } 310 | return 0; 311 | }); 312 | y = y - 20; 313 | accelerationScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 314 | XPSetWidgetProperty(accelerationScrollbar, xpProperty_ScrollBarMin, 1); 315 | XPSetWidgetProperty(accelerationScrollbar, xpProperty_ScrollBarMax, 100); 316 | XPSetWidgetProperty(accelerationScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_acceleration_response()); 317 | XPSetWidgetProperty(accelerationScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 318 | XPAddWidgetCallback(accelerationScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 319 | int inExit; 320 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 321 | Menu::mInstance->mGforceCameraCommand->set_acceleration_response((float)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 322 | // Update the label 323 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_GFORCE_ACCELERATION, xpMode_Recursive, 0, 0); 324 | return 1; 325 | } 326 | return 0; 327 | }); 328 | } 329 | 330 | void Menu::visit(TouchdownCameraCommand& command) 331 | { 332 | XPWidgetID subw; 333 | int x1, x2; 334 | // Set the subwindow top like main top minus the gforce height minus some padding 335 | int y = mTop - 188 - 10 - 70 - 10 - 70 - 10; 336 | XPWidgetID enableButton; 337 | XPWidgetID responseScrollbar; 338 | XPWidgetID responseLabel; 339 | char buffer[16]; 340 | 341 | mTouchdownCameraCommand = &command; 342 | subw = XPCreateWidget(mLeft + 10, y, mLeft + mWidth / 3 - 10, y - 70, 1, "Touchdown effect Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 343 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 344 | 345 | x1 = mLeft + 20; 346 | x2 = mLeft + mWidth / 3 - 20; 347 | 348 | // Add the enable checkbox 349 | y = y - 10; 350 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the touchdown effect", 0, mWidgetId, xpWidgetClass_Button); 351 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 352 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 353 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 354 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 355 | int inExit; 356 | if (inMessage == xpMsg_ButtonStateChanged) { 357 | Menu::mInstance->mTouchdownCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 358 | return 1; 359 | } 360 | return 0; 361 | }); 362 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_touchdown", 0, mWidgetId, xpWidgetClass_Caption); 363 | // Add the response label + scrollbar 364 | y = y - 20; 365 | sprintf(buffer, "Response: %.0f", mTouchdownCameraCommand->get_response()); 366 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 367 | // On message received update the label 368 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 369 | if (inMessage == xpMsg_UserStart + UPDATE_TOUCHDOWN) { 370 | char mbuffer[16]; 371 | sprintf(mbuffer, "Response: %.0f", Menu::mInstance->mTouchdownCameraCommand->get_response()); 372 | XPSetWidgetDescriptor(inWidget, mbuffer); 373 | return 1; 374 | } 375 | return 0; 376 | }); 377 | y = y - 20; 378 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 379 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 380 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 381 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 382 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 383 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 384 | int inExit; 385 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 386 | Menu::mInstance->mTouchdownCameraCommand->set_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 387 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_TOUCHDOWN, xpMode_Recursive, 0, 0); 388 | return 1; 389 | } 390 | return 0; 391 | }); 392 | } 393 | 394 | void Menu::visit(TaxiLookAheadCameraCommand& command) 395 | { 396 | XPWidgetID subw; 397 | int x1, x2, y; 398 | XPWidgetID enableButton; 399 | XPWidgetID responseScrollbar; 400 | XPWidgetID responseLabel; 401 | char buffer[32]; 402 | 403 | x1 = mLeft + mWidth / 3 + 10; 404 | x2 = x1 + mWidth / 3 - 20; 405 | 406 | mTaxiLookAheadCameraCommand = &command; 407 | subw = XPCreateWidget(x1, mTop - 30, x2, mTop - 188, 1, "Taxi LookAhead Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 408 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 409 | 410 | x1 += 10; 411 | x2 -= 10; 412 | 413 | // Add the enable checkbox 414 | y = mTop - 40; 415 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the taxi lookahead", 0, mWidgetId, xpWidgetClass_Button); 416 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 417 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 418 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 419 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 420 | int inExit; 421 | if (inMessage == xpMsg_ButtonStateChanged) { 422 | Menu::mInstance->mTaxiLookAheadCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 423 | return 1; 424 | } 425 | return 0; 426 | }); 427 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_taxilook", 0, mWidgetId, xpWidgetClass_Caption); 428 | // Add the rudder response label + scrollbar 429 | y = y - 20; 430 | sprintf(buffer, "Rudder response: %.0f", mTaxiLookAheadCameraCommand->get_rudder_response()); 431 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 432 | // On message received update the label 433 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 434 | if (inMessage == xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD) { 435 | char mbuffer[32]; 436 | sprintf(mbuffer, "Rudder response: %.0f", Menu::mInstance->mTaxiLookAheadCameraCommand->get_rudder_response()); 437 | XPSetWidgetDescriptor(inWidget, mbuffer); 438 | return 1; 439 | } 440 | return 0; 441 | }); 442 | y = y - 20; 443 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 444 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 445 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 446 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_rudder_response()); 447 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 448 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 449 | int inExit; 450 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 451 | Menu::mInstance->mTaxiLookAheadCameraCommand->set_rudder_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 452 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD, xpMode_Recursive, 0, 0); 453 | return 1; 454 | } 455 | return 0; 456 | }); 457 | 458 | // Add the turn response label + scrollbar 459 | y = y - 20; 460 | sprintf(buffer, "Turn response: %.0f", mTaxiLookAheadCameraCommand->get_turn_response()); 461 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 462 | // On message received update the label 463 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 464 | if (inMessage == xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD) { 465 | char mbuffer[32]; 466 | sprintf(mbuffer, "Turn response: %.0f", Menu::mInstance->mTaxiLookAheadCameraCommand->get_turn_response()); 467 | XPSetWidgetDescriptor(inWidget, mbuffer); 468 | return 1; 469 | } 470 | return 0; 471 | }); 472 | y = y - 20; 473 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 474 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 475 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 476 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_turn_response()); 477 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 478 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 479 | int inExit; 480 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 481 | Menu::mInstance->mTaxiLookAheadCameraCommand->set_turn_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 482 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD, xpMode_Recursive, 0, 0); 483 | return 1; 484 | } 485 | return 0; 486 | }); 487 | 488 | // Add the lean response label + scrollbar 489 | y = y - 20; 490 | sprintf(buffer, "Lean Response: %.0f", mTaxiLookAheadCameraCommand->get_lean_response()); 491 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 492 | // On message received update the label 493 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 494 | if (inMessage == xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD) { 495 | char mbuffer[32]; 496 | sprintf(mbuffer, "Lean Response: %.0f", Menu::mInstance->mTaxiLookAheadCameraCommand->get_lean_response()); 497 | XPSetWidgetDescriptor(inWidget, mbuffer); 498 | return 1; 499 | } 500 | return 0; 501 | }); 502 | y = y - 20; 503 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 504 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 505 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 506 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_lean_response()); 507 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 508 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 509 | int inExit; 510 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 511 | Menu::mInstance->mTaxiLookAheadCameraCommand->set_lean_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 512 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_TAXI_LOOKAHEAD, xpMode_Recursive, 0, 0); 513 | return 1; 514 | } 515 | return 0; 516 | }); 517 | } 518 | 519 | void Menu::visit(LookAheadCameraCommand& command) 520 | { 521 | XPWidgetID subw; 522 | XPWidgetID lookaheadScrollbar; 523 | XPWidgetID lookaheadLabel; 524 | XPWidgetID lookaheadEnabledButton; 525 | char buffer[64]; 526 | int x1, x2; 527 | // Set the subwindow top like main top minus the gforce height 528 | int y = mTop - 188 - 10; 529 | 530 | x1 = mLeft + mWidth / 3 + 10; 531 | x2 = x1 + mWidth / 3 - 20; 532 | 533 | mLookAheadCameraCommand = &command; 534 | subw = XPCreateWidget(x1, y, x2, y - 70, 1, "LookAhead Effect Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 535 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 536 | 537 | x1 += 10; 538 | x2 -= 10; 539 | 540 | // Add the enable checkbox 541 | y = y - 10; 542 | lookaheadEnabledButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the look ahead effects", 0, mWidgetId, xpWidgetClass_Button); 543 | XPSetWidgetProperty(lookaheadEnabledButton, xpProperty_ButtonType, xpRadioButton); 544 | XPSetWidgetProperty(lookaheadEnabledButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 545 | XPSetWidgetProperty(lookaheadEnabledButton, xpProperty_ButtonState, command.is_enabled()); 546 | XPAddWidgetCallback(lookaheadEnabledButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 547 | int inExit; 548 | if (inMessage == xpMsg_ButtonStateChanged) { 549 | Menu::mInstance->mLookAheadCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 550 | return 1; 551 | } 552 | return 0; 553 | }); 554 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_lookahead", 0, mWidgetId, xpWidgetClass_Caption); 555 | // Add the sensitivity label + scrollbar 556 | y = y - 20; 557 | sprintf(buffer, "Lookahead maximum angle: %.0f degrees", mLookAheadCameraCommand->get_response()); 558 | lookaheadLabel = XPCreateWidget(x1 - 5, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 559 | // On message received update the label 560 | XPAddWidgetCallback(lookaheadLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 561 | if (inMessage == xpMsg_UserStart + UPDATE_GFORCE_LOOKAHEAD) { 562 | char mbuffer[64]; 563 | sprintf(mbuffer, "Lookahead maximum angle: %.0f degrees", Menu::mInstance->mLookAheadCameraCommand->get_response()); 564 | XPSetWidgetDescriptor(inWidget, mbuffer); 565 | return 1; 566 | } 567 | return 0; 568 | }); 569 | y = y - 20; 570 | lookaheadScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 571 | XPSetWidgetProperty(lookaheadScrollbar, xpProperty_ScrollBarMin, 1); 572 | XPSetWidgetProperty(lookaheadScrollbar, xpProperty_ScrollBarMax, 90); 573 | XPSetWidgetProperty(lookaheadScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 574 | XPSetWidgetProperty(lookaheadScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 575 | XPAddWidgetCallback(lookaheadScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 576 | int inExit; 577 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 578 | Menu::mInstance->mLookAheadCameraCommand->set_response((float)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 579 | // Update the label 580 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_GFORCE_LOOKAHEAD, xpMode_Recursive, 0, 0); 581 | return 1; 582 | } 583 | return 0; 584 | }); 585 | } 586 | 587 | void Menu::visit(GroundRollCameraCommand& command) 588 | { 589 | XPWidgetID subw; 590 | int x1, x2; 591 | // Set the subwindow top like main top minus the taxi lookahead height minus padding minus look ahead minus padding 592 | int y = mTop - 188 - 10 - 70 - 10; 593 | XPWidgetID enableButton; 594 | XPWidgetID responseScrollbar; 595 | XPWidgetID responseLabel; 596 | char buffer[16]; 597 | 598 | x1 = mLeft + mWidth / 3 + 10; 599 | x2 = x1 + mWidth / 3 - 20; 600 | 601 | mGroundRollCameraCommand = &command; 602 | subw = XPCreateWidget(x1, y, x2, y - 70, 1, "Ground Roll Shaking Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 603 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 604 | 605 | x1 += 10; 606 | x2 -= 10; 607 | 608 | // Add the enable checkbox 609 | y = y - 10; 610 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the ground roll vibrations", 0, mWidgetId, xpWidgetClass_Button); 611 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 612 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 613 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 614 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 615 | int inExit; 616 | if (inMessage == xpMsg_ButtonStateChanged) { 617 | Menu::mInstance->mGroundRollCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 618 | return 1; 619 | } 620 | return 0; 621 | }); 622 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_groundroll", 0, mWidgetId, xpWidgetClass_Caption); 623 | // Add the response label + scrollbar 624 | y = y - 20; 625 | sprintf(buffer, "Response: %.0f", mGroundRollCameraCommand->get_response()); 626 | responseLabel = XPCreateWidget(x1 - 5, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 627 | // On message received update the label 628 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 629 | if (inMessage == xpMsg_UserStart + UPDATE_GROUNDROLL) { 630 | char mbuffer[16]; 631 | sprintf(mbuffer, "Response: %.0f", Menu::mInstance->mGroundRollCameraCommand->get_response()); 632 | XPSetWidgetDescriptor(inWidget, mbuffer); 633 | return 1; 634 | } 635 | return 0; 636 | }); 637 | y = y - 20; 638 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 639 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 640 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 641 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 642 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 643 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 644 | int inExit; 645 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 646 | Menu::mInstance->mGroundRollCameraCommand->set_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 647 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_GROUNDROLL, xpMode_Recursive, 0, 0); 648 | return 1; 649 | } 650 | return 0; 651 | }); 652 | } 653 | 654 | // Create and handle the piston engine subwindow 655 | void Menu::visit(PistonEngineCameraCommand & command) 656 | { 657 | XPWidgetID subw; 658 | int x1, x2; 659 | // Set the subwindow top like main top minus the taxi lookahead height minus some padding and the look ahead 660 | // height minus the ground roll height 661 | int y = mTop - 188 - 10 - 70 - 10 - 70 - 10; 662 | XPWidgetID enableButton; 663 | XPWidgetID responseScrollbar; 664 | XPWidgetID responseLabel; 665 | char buffer[16]; 666 | 667 | mPistonEngineCameraCommand = &command; 668 | 669 | x1 = mLeft + mWidth / 3 + 10; 670 | x2 = x1 + mWidth / 3 - 20; 671 | 672 | subw = XPCreateWidget(x1, y, x2, y - 70, 1, "Piston Engine Vibrations Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 673 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 674 | 675 | x1 += 10; 676 | x2 -= 10; 677 | 678 | // Add the enable checkbox 679 | y = y - 10; 680 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the piston engine vibrations", 0, mWidgetId, xpWidgetClass_Button); 681 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 682 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 683 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 684 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 685 | int inExit; 686 | if (inMessage == xpMsg_ButtonStateChanged) { 687 | Menu::mInstance->mPistonEngineCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 688 | return 1; 689 | } 690 | return 0; 691 | }); 692 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_pistonvib", 0, mWidgetId, xpWidgetClass_Caption); 693 | // Add the response label + scrollbar 694 | y = y - 20; 695 | sprintf(buffer, "Response: %.0f", mPistonEngineCameraCommand->get_response()); 696 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 697 | // On message received update the label 698 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 699 | if (inMessage == xpMsg_UserStart + UPDATE_PISTONENGINE) { 700 | char mbuffer[16]; 701 | sprintf(mbuffer, "Response: %.0f", Menu::mInstance->mPistonEngineCameraCommand->get_response()); 702 | XPSetWidgetDescriptor(inWidget, mbuffer); 703 | return 1; 704 | } 705 | return 0; 706 | }); 707 | y = y - 20; 708 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 709 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 710 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 711 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 712 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 713 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 714 | int inExit; 715 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 716 | Menu::mInstance->mPistonEngineCameraCommand->set_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 717 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_PISTONENGINE, xpMode_Recursive, 0, 0); 718 | return 1; 719 | } 720 | return 0; 721 | }); 722 | } 723 | 724 | void Menu::visit(LevelHeadCameraCommand& command) 725 | { 726 | XPWidgetID subw; 727 | int x1, x2, y; 728 | XPWidgetID enableButton; 729 | XPWidgetID responseScrollbar; 730 | XPWidgetID responseLabel; 731 | char buffer[32]; 732 | 733 | x2 = mRight - 20; 734 | x1 = mRight - mWidth / 3 + 10; 735 | 736 | mLevelHeadCameraCommand = &command; 737 | subw = XPCreateWidget(x1, mTop - 30, x2, mTop - 150, 1, "Level head Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 738 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 739 | 740 | x1 += 10; 741 | x2 -= 10; 742 | 743 | // Add the enable checkbox 744 | y = mTop - 40; 745 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the level head effect", 0, mWidgetId, xpWidgetClass_Button); 746 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 747 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 748 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 749 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 750 | int inExit; 751 | if (inMessage == xpMsg_ButtonStateChanged) { 752 | Menu::mInstance->mLevelHeadCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 753 | return 1; 754 | } 755 | return 0; 756 | }); 757 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_levelhead", 0, mWidgetId, xpWidgetClass_Caption); 758 | // Add the response label + scrollbar 759 | y = y - 20; 760 | sprintf(buffer, "Response: %.0f", mLevelHeadCameraCommand->get_response()); 761 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 762 | // On message received update the label 763 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 764 | if (inMessage == xpMsg_UserStart + UPDATE_LEVELHEAD_RESP) { 765 | char mbuffer[32]; 766 | sprintf(mbuffer, "Response: %.0f", Menu::mInstance->mLevelHeadCameraCommand->get_response()); 767 | XPSetWidgetDescriptor(inWidget, mbuffer); 768 | return 1; 769 | } 770 | return 0; 771 | }); 772 | y = y - 20; 773 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 774 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 775 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 776 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 777 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 778 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 779 | int inExit; 780 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 781 | Menu::mInstance->mLevelHeadCameraCommand->set_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 782 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_LEVELHEAD_RESP, xpMode_Recursive, 0, 0); 783 | return 1; 784 | } 785 | return 0; 786 | }); 787 | 788 | // Add the max bank label + scrollbar 789 | y = y - 20; 790 | sprintf(buffer, "Maximum bank angle: %.0f degrees", mLevelHeadCameraCommand->get_max_bank()); 791 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 792 | // On message received update the label 793 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 794 | if (inMessage == xpMsg_UserStart + UPDATE_LEVELHEAD_BANK) { 795 | char mbuffer[32]; 796 | sprintf(mbuffer, "Maximum bank angle: %.0f degrees", Menu::mInstance->mLevelHeadCameraCommand->get_max_bank()); 797 | XPSetWidgetDescriptor(inWidget, mbuffer); 798 | return 1; 799 | } 800 | return 0; 801 | }); 802 | y = y - 20; 803 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 804 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 805 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 90); 806 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_max_bank()); 807 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 808 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 809 | int inExit; 810 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 811 | Menu::mInstance->mLevelHeadCameraCommand->set_max_bank((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 812 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_LEVELHEAD_BANK, xpMode_Recursive, 0, 0); 813 | return 1; 814 | } 815 | return 0; 816 | }); 817 | } 818 | 819 | void Menu::visit(RotorCameraCommand & command) 820 | { 821 | XPWidgetID subw; 822 | int x1, x2; 823 | // Set the subwindow top like main top minus the level head height minus some long padding 824 | int y = mTop - 150 - 10; 825 | XPWidgetID enableButton; 826 | XPWidgetID responseScrollbar; 827 | XPWidgetID responseLabel; 828 | char buffer[16]; 829 | 830 | x1 = mRight - mWidth / 3 + 10; 831 | x2 = mRight - 20; 832 | 833 | mRotorCameraCommand = &command; 834 | subw = XPCreateWidget(x1, y, x2, y - 70, 1, "Rotor Vibrations Settings", 0, mWidgetId, xpWidgetClass_SubWindow); 835 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 836 | 837 | x1 += 10; 838 | x2 -= 10; 839 | 840 | // Add the enable checkbox 841 | y = y - 10; 842 | enableButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable the rotor vibrations", 0, mWidgetId, xpWidgetClass_Button); 843 | XPSetWidgetProperty(enableButton, xpProperty_ButtonType, xpRadioButton); 844 | XPSetWidgetProperty(enableButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 845 | XPSetWidgetProperty(enableButton, xpProperty_ButtonState, command.is_enabled()); 846 | XPAddWidgetCallback(enableButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 847 | int inExit; 848 | if (inMessage == xpMsg_ButtonStateChanged) { 849 | Menu::mInstance->mRotorCameraCommand->set_enabled(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 850 | return 1; 851 | } 852 | return 0; 853 | }); 854 | XPCreateWidget(x1 + 14, y, x1 + 24, y - 32, 1, "Command: simcoders/headshake/toggle_rotorvib", 0, mWidgetId, xpWidgetClass_Caption); 855 | // Add the response label + scrollbar 856 | y = y - 20; 857 | sprintf(buffer, "Response: %.0f", mRotorCameraCommand->get_response()); 858 | responseLabel = XPCreateWidget(x1, y, x2, y - 18, 1, buffer, 0, mWidgetId, xpWidgetClass_Caption); 859 | // On message received update the label 860 | XPAddWidgetCallback(responseLabel, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 861 | if (inMessage == xpMsg_UserStart + UPDATE_ROTOR) { 862 | char mbuffer[16]; 863 | sprintf(mbuffer, "Response: %.0f", Menu::mInstance->mRotorCameraCommand->get_response()); 864 | XPSetWidgetDescriptor(inWidget, mbuffer); 865 | return 1; 866 | } 867 | return 0; 868 | }); 869 | y = y - 20; 870 | responseScrollbar = XPCreateWidget(x1 + 5, y, x2 - 5, y - 18, 1, "", 0, mWidgetId, xpWidgetClass_ScrollBar); 871 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMin, 1); 872 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarMax, 100); 873 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarSliderPosition, (int)command.get_response()); 874 | XPSetWidgetProperty(responseScrollbar, xpProperty_ScrollBarType, xpScrollBarTypeSlider); 875 | XPAddWidgetCallback(responseScrollbar, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 876 | int inExit; 877 | if (inMessage == xpMsg_ScrollBarSliderPositionChanged) { 878 | Menu::mInstance->mRotorCameraCommand->set_response((int)(XPGetWidgetProperty(inWidget, xpProperty_ScrollBarSliderPosition, &inExit))); 879 | XPSendMessageToWidget(Menu::mInstance->mWidgetId, xpMsg_UserStart + UPDATE_ROTOR, xpMode_Recursive, 0, 0); 880 | return 1; 881 | } 882 | return 0; 883 | }); 884 | } 885 | 886 | void Menu::visit_multimonitor(bool multimonitorCompatibility) 887 | { 888 | // Create the multimonitor compatibility menu 889 | // Place it at the mBottom 890 | // Set the subwindow top from main top minus level head height minus rotor height minus some padding 891 | int y = mTop - 150 - 10 - 70 - 10; 892 | int x1 = mRight - mWidth / 3 + 10; 893 | int x2 = mRight - 20; 894 | 895 | XPWidgetID subw = XPCreateWidget(x1, y, x2, y - 90, 1, "Compatibility", 0, mWidgetId, xpWidgetClass_SubWindow); 896 | XPSetWidgetProperty(subw, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow); 897 | 898 | x1 += 10; 899 | x2 -= 10; 900 | y -= 10; 901 | 902 | // Add the compatibility checkbox 903 | XPWidgetID compatibilityButton = XPCreateWidget(x1, y, x1 + 10, y - 10, 1, " Enable multimonitor compatibility", 0, mWidgetId, xpWidgetClass_Button); 904 | XPSetWidgetProperty(compatibilityButton, xpProperty_ButtonType, xpRadioButton); 905 | XPSetWidgetProperty(compatibilityButton, xpProperty_ButtonBehavior, xpButtonBehaviorCheckBox); 906 | XPSetWidgetProperty(compatibilityButton, xpProperty_ButtonState, multimonitorCompatibility); 907 | XPAddWidgetCallback(compatibilityButton, [](XPWidgetMessage inMessage, XPWidgetID inWidget, intptr_t, intptr_t) -> int { 908 | int inExit; 909 | if (inMessage == xpMsg_ButtonStateChanged) { 910 | Menu::mInstance->mCameraControl->set_multimonitor_compatibility(XPGetWidgetProperty(inWidget, xpProperty_ButtonState, &inExit)); 911 | return 1; 912 | } 913 | return 0; 914 | }); 915 | 916 | y -= 20; 917 | // Add the description 918 | XPCreateWidget(x1, y, x1 + 10, y - 30, 1, "Enable this if you use more screens.", 0, mWidgetId, xpWidgetClass_Caption); 919 | XPCreateWidget(x1, y, x1 + 10, y - 60, 1, "This is for X-Plane versions before 11.02.", 0, mWidgetId, xpWidgetClass_Caption); 920 | XPCreateWidget(x1, y, x1 + 10, y - 90, 1, "It has no effect on X-Plane 11.02+.", 0, mWidgetId, xpWidgetClass_Caption); 921 | } -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_H 2 | #define MENU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "interfaces/ivisitor.h" 10 | #include "interfaces/ivisitable.h" 11 | #include "cameracontrol.h" 12 | 13 | class Menu : public IVisitor 14 | { 15 | public: 16 | static Menu* get_instance(); 17 | void create_menu_entry(CameraControl&); 18 | void destroy_menu_entry(); 19 | void show(); 20 | // Visitors 21 | void visit(CameraControl&); 22 | void visit(GForceCameraCommand&); 23 | void visit(LookAheadCameraCommand&); 24 | void visit(PistonEngineCameraCommand&); 25 | void visit(RotorCameraCommand&); 26 | void visit(GroundRollCameraCommand&); 27 | void visit(TaxiLookAheadCameraCommand&); 28 | void visit(TouchdownCameraCommand&); 29 | void visit(LevelHeadCameraCommand&); 30 | void visit_multimonitor(bool multimonitorCompatibility); 31 | protected: 32 | private: 33 | /** 34 | Singleton 35 | */ 36 | Menu(); // Private so that it can not be called 37 | Menu(Menu const&) {}; // copy constructor is private 38 | Menu& operator=(Menu const&) { return *mInstance; }; // assignment operator is private 39 | static Menu* mInstance; 40 | virtual ~Menu(); 41 | static int toggle(XPLMCommandRef, XPLMCommandPhase, void*); 42 | 43 | /** 44 | Vars 45 | */ 46 | XPWidgetID mWidgetId; 47 | XPLMCommandRef mToggleCommand; 48 | int mId; 49 | int mWidth; 50 | int mHeight; 51 | int mLeft; 52 | int mTop; 53 | int mRight; 54 | int mBottom; 55 | int mAdsHeight; 56 | bool mShowAds; 57 | CameraControl* mCameraControl; 58 | GForceCameraCommand* mGforceCameraCommand; 59 | LookAheadCameraCommand* mLookAheadCameraCommand; 60 | PistonEngineCameraCommand* mPistonEngineCameraCommand; 61 | RotorCameraCommand* mRotorCameraCommand; 62 | GroundRollCameraCommand* mGroundRollCameraCommand; 63 | TaxiLookAheadCameraCommand* mTaxiLookAheadCameraCommand; 64 | TouchdownCameraCommand* mTouchdownCameraCommand; 65 | LevelHeadCameraCommand* mLevelHeadCameraCommand; 66 | }; 67 | 68 | #endif // MENU_H 69 | -------------------------------------------------------------------------------- /src/settings/settingsreader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "settings/settingsreader.h" 10 | #include "cameracontrol.h" 11 | #include "cameracommands/gforcecameracommand.h" 12 | #include "cameracommands/lookaheadcameracommand.h" 13 | #include "cameracommands/pistonenginecameracommand.h" 14 | #include "cameracommands/groundrollcameracommand.h" 15 | #include "cameracommands/taxilookaheadcameracommand.h" 16 | #include "cameracommands/rotorcameracommand.h" 17 | #include "cameracommands/touchdowncameracommand.h" 18 | #include "cameracommands/levelheadcameracommand.h" 19 | #include "helpers.h" 20 | 21 | SettingsReader::SettingsReader() 22 | { 23 | char path[1024]; 24 | std::ifstream file; 25 | std::string key, value, temp; 26 | std::size_t position; 27 | 28 | if (!get_settings_filepath(path)) 29 | return; 30 | file.open(path); 31 | if (!file.is_open()) 32 | return; 33 | while (std::getline(file, temp)) { 34 | position = temp.find("="); 35 | if (position != std::string::npos) 36 | mMap.insert(std::pair(temp.substr(0, position), temp.substr(position + 1))); 37 | } 38 | file.close(); 39 | } 40 | 41 | SettingsReader::~SettingsReader() 42 | { 43 | 44 | } 45 | 46 | void SettingsReader::visit(CameraControl &control) 47 | { 48 | if (mMap.find("cameracontrol.multimonitorcompat.enabled") != mMap.end()) 49 | control.set_multimonitor_compatibility(mMap["cameracontrol.multimonitorcompat.enabled"].compare("1") == 0); 50 | if (mMap.find("cameracontrol.enabled") != mMap.end()) 51 | control.set_enabled(mMap["cameracontrol.enabled"].compare("1") == 0); 52 | } 53 | 54 | void SettingsReader::visit(GForceCameraCommand &command) 55 | { 56 | if (mMap.find("cameracommand.gforce.enabled") != mMap.end()) 57 | command.set_enabled(mMap["cameracommand.gforce.enabled"].compare("1") == 0); 58 | if (mMap.find("cameracommand.gforce.pitch.response") != mMap.end()) 59 | command.set_pitch_response(atof(mMap["cameracommand.gforce.pitch.response"].c_str())); 60 | if (mMap.find("cameracommand.gforce.yaw.response") != mMap.end()) 61 | command.set_yaw_response(atof(mMap["cameracommand.gforce.yaw.response"].c_str())); 62 | if (mMap.find("cameracommand.gforce.acceleration.response") != mMap.end()) 63 | command.set_acceleration_response(atof(mMap["cameracommand.gforce.acceleration.response"].c_str())); 64 | } 65 | 66 | void SettingsReader::visit(LookAheadCameraCommand &command) 67 | { 68 | if (mMap.find("cameracommand.lookahead.enabled") != mMap.end()) 69 | command.set_enabled(mMap["cameracommand.lookahead.enabled"].compare("1") == 0); 70 | if (mMap.find("cameracommand.lookahead.response") != mMap.end()) 71 | command.set_response(atof(mMap["cameracommand.lookahead.response"].c_str())); 72 | } 73 | 74 | void SettingsReader::visit(PistonEngineCameraCommand &command) 75 | { 76 | if (mMap.find("cameracommand.pistonengine.enabled") != mMap.end()) 77 | command.set_enabled(mMap["cameracommand.pistonengine.enabled"].compare("1") == 0); 78 | if (mMap.find("cameracommand.pistonengine.response") != mMap.end()) 79 | command.set_response(atof(mMap["cameracommand.pistonengine.response"].c_str())); 80 | } 81 | 82 | void SettingsReader::visit(GroundRollCameraCommand &command) 83 | { 84 | if (mMap.find("cameracommand.groundroll.enabled") != mMap.end()) 85 | command.set_enabled(mMap["cameracommand.groundroll.enabled"].compare("1") == 0); 86 | if (mMap.find("cameracommand.groundroll.response") != mMap.end()) 87 | command.set_response(atof(mMap["cameracommand.groundroll.response"].c_str())); 88 | } 89 | 90 | void SettingsReader::visit(TaxiLookAheadCameraCommand &command) 91 | { 92 | if (mMap.find("cameracommand.taxilookahead.enabled") != mMap.end()) 93 | command.set_enabled(mMap["cameracommand.taxilookahead.enabled"].compare("1") == 0); 94 | if (mMap.find("cameracommand.taxilookahead.rudder.response") != mMap.end()) 95 | command.set_rudder_response(atof(mMap["cameracommand.taxilookahead.rudder.response"].c_str())); 96 | if (mMap.find("cameracommand.taxilookahead.turn.response") != mMap.end()) 97 | command.set_turn_response(atof(mMap["cameracommand.taxilookahead.turn.response"].c_str())); 98 | if (mMap.find("cameracommand.taxilookahead.lean.response") != mMap.end()) 99 | command.set_lean_response(atof(mMap["cameracommand.taxilookahead.lean.response"].c_str())); 100 | } 101 | 102 | void SettingsReader::visit(TouchdownCameraCommand &command) 103 | { 104 | if (mMap.find("cameracommand.touchdown.enabled") != mMap.end()) 105 | command.set_enabled(mMap["cameracommand.touchdown.enabled"].compare("1") == 0); 106 | if (mMap.find("cameracommand.touchdown.response") != mMap.end()) 107 | command.set_response(atof(mMap["cameracommand.touchdown.response"].c_str())); 108 | } 109 | 110 | void SettingsReader::visit(RotorCameraCommand &command) 111 | { 112 | if (mMap.find("cameracommand.rotor.enabled") != mMap.end()) 113 | command.set_enabled(mMap["cameracommand.rotor.enabled"].compare("1") == 0); 114 | if (mMap.find("cameracommand.rotor.response") != mMap.end()) 115 | command.set_response(atof(mMap["cameracommand.rotor.response"].c_str())); 116 | } 117 | 118 | void SettingsReader::visit(LevelHeadCameraCommand &command) 119 | { 120 | if (mMap.find("cameracommand.levelhead.enabled") != mMap.end()) 121 | command.set_enabled(mMap["cameracommand.levelhead.enabled"].compare("1") == 0); 122 | 123 | if (mMap.find("cameracommand.levelhead.response") != mMap.end()) 124 | command.set_response(atof(mMap["cameracommand.levelhead.response"].c_str())); 125 | 126 | if (mMap.find("cameracommand.levelhead.maxbank") != mMap.end()) 127 | command.set_max_bank(atof(mMap["cameracommand.levelhead.maxbank"].c_str())); 128 | } 129 | -------------------------------------------------------------------------------- /src/settings/settingsreader.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSREADER_H 2 | #define SETTINGSREADER_H 3 | 4 | #include "interfaces/ivisitor.h" 5 | 6 | #include 7 | #include 8 | 9 | class SettingsReader : public IVisitor 10 | { 11 | public: 12 | /** Default constructor */ 13 | SettingsReader(); 14 | /** Default destructor */ 15 | virtual ~SettingsReader(); 16 | void visit(CameraControl&); 17 | void visit(GForceCameraCommand&); 18 | void visit(LookAheadCameraCommand&); 19 | void visit(PistonEngineCameraCommand&); 20 | void visit(RotorCameraCommand&); 21 | void visit(GroundRollCameraCommand&); 22 | void visit(TaxiLookAheadCameraCommand&); 23 | void visit(TouchdownCameraCommand&); 24 | void visit(LevelHeadCameraCommand&); 25 | protected: 26 | private: 27 | std::map mMap; 28 | }; 29 | 30 | #endif // SETTINGSREADER_H 31 | -------------------------------------------------------------------------------- /src/settings/settingswriter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "settings/settingswriter.h" 6 | #include "cameracontrol.h" 7 | #include "cameracommands/gforcecameracommand.h" 8 | #include "cameracommands/lookaheadcameracommand.h" 9 | #include "cameracommands/pistonenginecameracommand.h" 10 | #include "cameracommands/groundrollcameracommand.h" 11 | #include "cameracommands/taxilookaheadcameracommand.h" 12 | #include "cameracommands/rotorcameracommand.h" 13 | #include "cameracommands/touchdowncameracommand.h" 14 | #include "cameracommands/levelheadcameracommand.h" 15 | #include "helpers.h" 16 | 17 | SettingsWriter::SettingsWriter() 18 | { 19 | mFile = NULL; 20 | } 21 | 22 | SettingsWriter::~SettingsWriter() 23 | { 24 | 25 | } 26 | 27 | void SettingsWriter::open() 28 | { 29 | char path[1024]; 30 | if (!get_settings_filepath(path)) { 31 | XPLMDebugString("HeadShake: Unable to get the prefs folder path.\n"); 32 | return; 33 | } 34 | mFile = fopen(path, "w+"); 35 | if (mFile == NULL) { 36 | XPLMDebugString("HeadShake: Unable to open (write) file "); 37 | XPLMDebugString(path); 38 | } 39 | } 40 | 41 | void SettingsWriter::close() 42 | { 43 | if (mFile != NULL) 44 | fclose(mFile); 45 | } 46 | 47 | void SettingsWriter::visit(CameraControl &control) 48 | { 49 | if (mFile != NULL) { 50 | fprintf(mFile, "cameracontrol.multimonitorcompat.enabled=%s\n", control.get_multimonitor_compatibility() ? "1" : "0"); 51 | fprintf(mFile, "cameracontrol.enabled=%s\n", control.get_enabled() ? "1" : "0"); 52 | } 53 | } 54 | 55 | void SettingsWriter::visit(GForceCameraCommand &command) 56 | { 57 | if (mFile != NULL) { 58 | fprintf(mFile, "cameracommand.gforce.enabled=%s\n", command.is_enabled() ? "1" : "0"); 59 | fprintf(mFile, "cameracommand.gforce.pitch.response=%.0f\n", command.get_pitch_response()); 60 | fprintf(mFile, "cameracommand.gforce.yaw.response=%.0f\n", command.get_yaw_response()); 61 | fprintf(mFile, "cameracommand.gforce.acceleration.response=%.0f\n", command.get_acceleration_response()); 62 | } 63 | } 64 | 65 | void SettingsWriter::visit(LookAheadCameraCommand &command) 66 | { 67 | if (mFile != NULL) { 68 | fprintf(mFile, "cameracommand.lookahead.enabled=%s\n", command.is_enabled() ? "1" : "0"); 69 | fprintf(mFile, "cameracommand.lookahead.response=%.0f\n", command.get_response()); 70 | } 71 | } 72 | 73 | void SettingsWriter::visit(PistonEngineCameraCommand &command) 74 | { 75 | if (mFile != NULL) { 76 | fprintf(mFile, "cameracommand.pistonengine.enabled=%s\n", command.is_enabled() ? "1" : "0"); 77 | fprintf(mFile, "cameracommand.pistonengine.response=%.0f\n", command.get_response()); 78 | } 79 | } 80 | 81 | void SettingsWriter::visit(GroundRollCameraCommand &command) 82 | { 83 | if (mFile != NULL) { 84 | fprintf(mFile, "cameracommand.groundroll.enabled=%s\n", command.is_enabled() ? "1" : "0"); 85 | fprintf(mFile, "cameracommand.groundroll.response=%.0f\n", command.get_response()); 86 | } 87 | } 88 | 89 | void SettingsWriter::visit(TaxiLookAheadCameraCommand &command) 90 | { 91 | if (mFile != NULL) { 92 | fprintf(mFile, "cameracommand.taxilookahead.enabled=%s\n", command.is_enabled() ? "1" : "0"); 93 | fprintf(mFile, "cameracommand.taxilookahead.rudder.response=%.0f\n", command.get_rudder_response()); 94 | fprintf(mFile, "cameracommand.taxilookahead.turn.response=%.0f\n", command.get_turn_response()); 95 | fprintf(mFile, "cameracommand.taxilookahead.lean.response=%.0f\n", command.get_lean_response()); 96 | } 97 | } 98 | 99 | void SettingsWriter::visit(TouchdownCameraCommand &command) 100 | { 101 | if (mFile != NULL) { 102 | fprintf(mFile, "cameracommand.touchdown.enabled=%s\n", command.is_enabled() ? "1" : "0"); 103 | fprintf(mFile, "cameracommand.touchdown.response=%.0f\n", command.get_response()); 104 | } 105 | } 106 | 107 | void SettingsWriter::visit(RotorCameraCommand &command) 108 | { 109 | if (mFile != NULL) { 110 | fprintf(mFile, "cameracommand.rotor.enabled=%s\n", command.is_enabled() ? "1" : "0"); 111 | fprintf(mFile, "cameracommand.rotor.response=%.0f\n", command.get_response()); 112 | } 113 | } 114 | 115 | void SettingsWriter::visit(LevelHeadCameraCommand &command) 116 | { 117 | if (mFile != NULL) { 118 | fprintf(mFile, "cameracommand.levelhead.enabled=%s\n", command.is_enabled() ? "1" : "0"); 119 | fprintf(mFile, "cameracommand.levelhead.response=%.0f\n", command.get_response()); 120 | fprintf(mFile, "cameracommand.levelhead.maxbank=%.0f\n", command.get_max_bank()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/settings/settingswriter.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSWRITER_H 2 | #define SETTINGSWRITER_H 3 | 4 | #include 5 | 6 | #include "interfaces/ivisitor.h" 7 | 8 | class SettingsWriter : public IVisitor 9 | { 10 | public: 11 | /** Default constructor */ 12 | SettingsWriter(); 13 | /** Default destructor */ 14 | virtual ~SettingsWriter(); 15 | void open(); 16 | void close(); 17 | void visit(CameraControl&); 18 | void visit(GForceCameraCommand&); 19 | void visit(LookAheadCameraCommand&); 20 | void visit(PistonEngineCameraCommand&); 21 | void visit(RotorCameraCommand&); 22 | void visit(GroundRollCameraCommand&); 23 | void visit(TaxiLookAheadCameraCommand&); 24 | void visit(TouchdownCameraCommand&); 25 | void visit(LevelHeadCameraCommand&); 26 | protected: 27 | private: 28 | FILE *mFile; 29 | }; 30 | 31 | #endif // SETTINGSWRITER_H 32 | -------------------------------------------------------------------------------- /src/windows.cpp: -------------------------------------------------------------------------------- 1 | #if IBM 2 | #include 3 | BOOL APIENTRY DllMain( HANDLE, 4 | DWORD ul_reason_for_call, 5 | LPVOID 6 | ) 7 | { 8 | switch (ul_reason_for_call) 9 | { 10 | case DLL_PROCESS_ATTACH: 11 | case DLL_THREAD_ATTACH: 12 | case DLL_THREAD_DETACH: 13 | case DLL_PROCESS_DETACH: 14 | break; 15 | } 16 | return TRUE; 17 | } 18 | #endif 19 | --------------------------------------------------------------------------------