Advertisement
Sabersense

Array Prop

Nov 26th, 2024
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 38.94 KB | None | 0 0
  1. /*
  2. ============================================================
  3. ================== SABERSENSE™ PROP FILE ===================
  4. ============================================================
  5.  
  6. Built on SA22C programming by Matthew McGeary.
  7. Modified by Chris Carter with help and contributions
  8. from Brian Connor and Fredrik Hubinette.
  9. November 2024.
  10. Version 55.
  11.  
  12.  
  13. ============================================================
  14. ========= BUTTON SYSTEM PRINCIPLES AND LOGIC NOTES =========
  15.  
  16. The Sabersense™ button prop file has been engineered for
  17. simplicity and usability. Complex gesture controls and
  18. features like Battle Mode have been removed.
  19.  
  20. The Sabersense™ button prop features harmonized controls
  21. between one-button and two-button operation without
  22. compromising the greater usability of two-button operation.
  23. Where practicable, the same controls apply to both states,
  24. with two-button operation benefitting from some extra
  25. features. Where possible without causing conflicts, some
  26. one-button controls also appear in two-button mode,
  27. despite two-button having its own controls for the same feature.
  28.  
  29. The overall intention is that users need only remember a
  30. minimum set of control principles in order to access
  31. all functions. As such, the logic is that the same button
  32. presses do the same thing within the various states,
  33. subject to inevitable and obvious variants.
  34.  
  35. Hence:
  36. ONE AND TWO BUTTON:
  37. Single click MAIN always lights the blade...
  38. Short click lights blade with sound
  39. Long click lights blade mute
  40.  
  41. Double click MAIN always plays a sound file...
  42. Character Quote or Music track with blade OFF
  43. Character Quote or Force Effect with blade ON
  44.  
  45. Holding down MAIN (1-button) or AUX (2-button)
  46. and waiting with blade OFF always skips to specific preset...
  47. Hilt pointing up - first preset
  48. Hilt horizontal - middle preset
  49. Hilt pointing down - last preset
  50.  
  51. Triple-clicking MAIN always enters a control menu
  52. Colour change with blade ON
  53. BladeID with blade OFF
  54.  
  55. TWO BUTTON ONLY:
  56. Short clicking AUX with blade OFF always moves to a different preset,
  57. forward with hilt pointing up, backwards with hilt pointing down...
  58. Single short click - one preset
  59. Double short click - five presets
  60. Triple short click - ten presets
  61.  
  62. Holding MAIN and short clicking AUX always enters a control menu...
  63. Colour change with blade ON
  64. Volume menu with blade OFF
  65.  
  66.  
  67. ==========================================================
  68. =================== 1 BUTTON CONTROLS ====================
  69.  
  70. MAIN FUNCTIONS
  71. Activate blade Short click while OFF. *
  72. Activate blade mute Long click while OFF (hold for one second then release).
  73. Deactivate blade Hold and wait until blade is off while ON.
  74.  
  75. FUNCTIONS WITH BLADE OFF
  76. Next preset Double click and hold while OFF pointing blade tip up.
  77. Previous preset Double click and hold while OFF pointing blade tip down.
  78. Skip to first preset Press and hold until it switches, hilt pointing upwards.
  79. Skip to middle preset Press and hold until it switches, hilt horizontal.
  80. Skip to last preset Press and hold until it switches, hilt pointing downwards.
  81. Play Character Quote Fast double-click, hilt pointing up. Quotes play sequentially. **
  82. To reset sequential quotes, change font or run BladeID.
  83. Play Music Track Fast double-click, hilt pointing down. **
  84. Speak battery voltage Fast four clicks while OFF.
  85. Run BladeID/Array Select Fast triple-click while OFF. (Applicable installs only).
  86. Enter/Exit VOLUME MENU Hold and clash while OFF.
  87. Volume up Click while in VOLUME MENU, hilt pointing up.
  88. Volume down Click while in VOLUME MENU, hilt pointing down.
  89. Volume adjusts in increments per click.
  90. You must exit VOLUME MENU to resume using lightsaber normally.
  91.  
  92. FUNCTIONS WITH BLADE ON
  93. Blade lockup Hold and hit clash while ON.
  94. Blade tip drag Hold and hit clash while ON pointing the blade tip down.
  95. Force Effect Fast double-click, hilt pointing down.
  96. Plays random Force effect (often character quote).
  97. Character Quote Fast double-click, hilt pointing up.
  98. Plays sequential character quote.
  99. Lightning block Double click and hold while ON.
  100. Melt Hold and thrust forward clash while ON. (Selected fonts only).
  101. Blaster blocks Short click/double click/triple click while ON.
  102. Enter multi-blast mode Hold while swinging for one second and release.
  103. To trigger blaster block, swing saber while in multi-blast mode.
  104. To exit, hold while swinging for one second and release.
  105.  
  106. COLOUR CHANGE FUNCTIONS WITH BLADE ON
  107. Enter COLOUR MENU Fast triple-click while ON.
  108. Announcement confirms you are in the COLOUR MENU.
  109. Cycle to next colour Rotate hilt whilst in COLOUR MENU until desired colour is reached.
  110. Exit COLOUR MENU Fast triple-click OR hold and wait.
  111. Announcement confirms you are exiting COLOUR MENU.
  112. You must exit COLOUR MENU to resume using lightsaber normally.
  113.  
  114.  
  115. ============================================================
  116. ===================== 2 BUTTON CONTROLS ====================
  117.  
  118. MAIN FUNCTIONS
  119. Activate blade Short click MAIN. *
  120. Activate blade mute Long click MAIN (hold for one second then release).
  121. Deactivate blade Press and hold MAIN and wait until blade is off.
  122.  
  123. FUNCTIONS WITH BLADE OFF
  124. Next preset Short click AUX, hilt pointing upwards.
  125. Previous preset Short click AUX, hilt pointing downwards.
  126. Previous preset Hold AUX and short click MAIN.
  127. (Duplicate legacy command).
  128. Skip to first preset Press and hold any button until it switches, hilt upwards.
  129. Skip to middle preset Press and hold any button until it switches, hilt horizontal.
  130. Skip to last preset Press and hold any button until it switches, hilt downwards.
  131. Skip forward 5 presets Fast double-click AUX, hilt pointing upwards.
  132. Skip back 5 presets Fast double-click AUX, hilt pointing downwards.
  133. Skip forward 10 presets Fast triple-click AUX, hilt pointing upwards.
  134. Skip back 10 presets Fast triple-click AUX, hilt pointing downwards.
  135. Play Character Quote Fast double-click MAIN, pointing up. Quotes play sequentially. **
  136. To reset sequential quotes, change font or run BladeID.
  137. Play Music Track Fast double-click MAIN, pointing down. **
  138. Speak battery voltage Fast four clicks MAIN or hold hold AUX for one second and let go.
  139. Run BladeID/Array Select Fast triple-click while OFF. (Applicable installs only).
  140. Enter/Exit VOLUME MENU Hold MAIN then quickly click AUX and release both simultaneously.
  141. Volume up Click MAIN while in VOLUME MENU, hilt pointing up.
  142. Volume down Click MAIN while in VOLUME MENU, hilt pointing down, OR click
  143. AUX while in VOLUME MENU.
  144. Volume adjusts in increments per click.
  145. You must exit VOLUME MENU to resume using saber normally.
  146.  
  147. FUNCTIONS WITH BLADE ON
  148. Blade lockup Press and hold AUX.
  149. Blade tip drag Press and hold AUX while blade is pointing down.
  150. Force Effect Fast double-click MAIN, hilt pointing down.
  151. Plays random Force effect (often character quote).
  152. Play Character Quote Fast double-click MAIN, hilt pointing up.
  153. Plays sequential character quote.
  154. Lightning block Double-click MAIN and hold.
  155. Melt Hold MAIN and push blade tip against wall (stab).
  156. Blaster blocks Short click AUX.
  157. Enter multi-blast mode Hold MAIN while swinging for one second and release.
  158. Saber will play two quick blasts confirming mode.
  159. Swing blade to trigger blaster block.
  160. To exit multi-blast mode, fast single click AUX.
  161.  
  162. COLOUR CHANGE FUNCTIONS WITH BLADE ON
  163. Enter/Exit COLOUR MENU Hold MAIN then quickly click AUX and release both
  164. buttons simultaneously. Or fast triple-click MAIN.
  165. Announcement confirms you are in the COLOUR MENU.
  166. Cycle to next colour Rotate hilt whilst in COLOUR MENU until desired colour is reached.
  167. Most Sabersense presets have 12 colour options.
  168. Alt Exit COLOUR MENU Hold MAIN and wait.
  169. Announcement confirms you are exiting COLOUR MENU.
  170. You must exit COLOUR MENU to resume using lightsaber normally.
  171.  
  172. * = Gesture ignitions also available via defines.
  173. ** = Audio player orientations can be reversed using SABERSENSE_FLIP_AUDIO_PLAYERS define.
  174.  
  175.  
  176. ===========================================================
  177. ========================= DEFINES =========================
  178.  
  179. #define SABERSENSE_ARRAY_SELECTOR
  180. Replaces regular BladeID and allows cycling between different
  181. blade/preset arrays regardless of actual BladeID status.
  182.  
  183. #define SABERSENSE_NUM_ARRAYS
  184. Required when using SABERSENSE_ARRAY_SELECTOR to tell the
  185. system how many blade arrays there are.
  186.  
  187. #define SABERSENSE_FLIP_AUDIO_PLAYERS
  188. Reverses UP/DOWN orientation for playing FORCE, QUOTE and
  189. MUSIC TRACK audio files. Default (no define) is UP for
  190. sequential QUOTE with blade ON and OFF, and DOWN for random
  191. FORCE effect (ON) and music TRACK (OFF). Define acts on
  192. both ON and OFF states for consistency.
  193.  
  194. #define SABERSENSE_OS7_LEGACY_SUPPORT
  195. Required when using this prop file with ProffieOS 7.x.
  196. Must NOT be used with ProffieOS 8.x or later.
  197.  
  198. #define SABERSENSE_NO_LOCKUP_HOLD
  199. Applicable to two-button mode only, reverts to lockup being
  200. triggered by clash while holding aux.
  201.  
  202. GESTURE CONTROLS
  203. There are four gesture types: Twist, Stab, Swing and Thrust.
  204. Gesture controls bypass all preon effects.
  205. Below are the options to add to the config to enable the various gestures. MM.
  206. #define SABERSENSE_TWIST_ON
  207. #define SABERSENSE_TWIST_OFF
  208. #define SABERSENSE_STAB_ON
  209. #define SABERSENSE_SWING_ON
  210. #define SABERSENSE_THRUST_ON
  211.  
  212. ============================================================
  213. ============================================================
  214. */
  215. #ifndef PROPS_SABER_SABERSENSE_BUTTONS_H
  216. #define PROPS_SABER_SABERSENSE_BUTTONS_H
  217.  
  218.  
  219. #ifdef SABERSENSE_ARRAY_SELECTOR
  220. #ifndef SABERSENSE_NUM_ARRAYS // No default number of arrays - must be defined in config.
  221. #error "SABERSENSE_NUM_ARRAYS must be defined in the config file."
  222. #endif
  223.  
  224. // Function to play the corresponding sound.
  225. void PlaySound(const char* sound) {
  226. RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
  227. if (player) {
  228. if (!player->PlayInCurrentDir(sound)) {
  229. player->Play(sound);
  230. }
  231. }
  232. }
  233.  
  234. struct SabersenseArraySelector {
  235. static int return_value; // Tracks the current array index.
  236. static const int sabersense_num_arrays = SABERSENSE_NUM_ARRAYS;
  237. static bool shouldPlaySound; // Flag to control if sound should be played.
  238. static unsigned long lastSwitchTime; // Track time of the last array switch.
  239. static const unsigned long soundDelay = 200; // Delay in ms before playing sound after switch.
  240. static bool isSwitchingInProgress; // Flag to indicate if array switch is in progress.
  241.  
  242. float id() {
  243. return return_value;
  244. }
  245.  
  246. // Forward cycle through arrays...
  247. static void cycleForward() {
  248. isSwitchingInProgress = true; // Mark that the switch is in progress.
  249. return_value = (return_value + 1) % sabersense_num_arrays;
  250. shouldPlaySound = true;
  251. lastSwitchTime = millis();
  252. isSwitchingInProgress = false; // Mark that the switch is complete.
  253. }
  254.  
  255. // Backward cycle through arrays.
  256. static void cycleBackward() {
  257. isSwitchingInProgress = true; // Mark that the switch is in progress
  258. return_value = (return_value - 1 + sabersense_num_arrays) % sabersense_num_arrays; // Ensures wrap.
  259. shouldPlaySound = true;
  260. lastSwitchTime = millis();
  261. isSwitchingInProgress = false; // Mark that the switch is complete.
  262. }
  263.  
  264. // Function to play corresponding sound file based on array index.
  265. static void playArraySound(int arrayIndex) {
  266. const char* soundFile = nullptr;
  267. switch (arrayIndex) {
  268. // Maximum of eight arrays possible.
  269. case 0: soundFile = "array1.wav"; break;
  270. case 1: soundFile = "array2.wav"; break;
  271. case 2: soundFile = "array3.wav"; break;
  272. case 3: soundFile = "array4.wav"; break;
  273. case 4: soundFile = "array5.wav"; break;
  274. case 5: soundFile = "array6.wav"; break;
  275. case 6: soundFile = "array7.wav"; break;
  276. case 7: soundFile = "array8.wav"; break;
  277. default: return; // No sound for invalid array index.
  278. }
  279. PlaySound(soundFile); // Calls custom PlaySound function.
  280. }
  281.  
  282. // Function to check enough time has passed to play the sound.
  283. static void checkAndPlaySound() {
  284. if (shouldPlaySound && !isSwitchingInProgress && (millis() - lastSwitchTime >= soundDelay)) {
  285. playArraySound(return_value); // Play sound for the current array.
  286. shouldPlaySound = false; // Reset flag after sound is played.
  287. }
  288. }
  289.  
  290. // Loop function, needs to be called periodically.
  291. static void loop() {
  292. checkAndPlaySound(); // Check if sound should be played after switching arrays.
  293. }
  294. };
  295.  
  296. // Static member initialization
  297. int SabersenseArraySelector::return_value = 0; // Start with the first array (index 0).
  298. bool SabersenseArraySelector::shouldPlaySound = false; // Flag to control sound playback.
  299. unsigned long SabersenseArraySelector::lastSwitchTime = 0; // Track time of array switch.
  300. bool SabersenseArraySelector::isSwitchingInProgress = false; // Flag for switch status.
  301.  
  302. #undef BLADE_ID_CLASS_INTERNAL
  303. #define BLADE_ID_CLASS_INTERNAL SabersenseArraySelector
  304. #undef BLADE_ID_CLASS
  305. #define BLADE_ID_CLASS SabersenseArraySelector
  306. #endif
  307.  
  308.  
  309. #include "prop_base.h"
  310. #include "../sound/hybrid_font.h"
  311.  
  312. #undef PROP_TYPE
  313. #define PROP_TYPE SabersenseButtons
  314.  
  315. #ifndef MOTION_TIMEOUT
  316. #define MOTION_TIMEOUT 60 * 15 * 1000
  317. #endif
  318.  
  319. #ifndef SABERSENSE_SWING_ON_SPEED
  320. #define SABERSENSE_SWING_ON_SPEED 250
  321. #endif
  322.  
  323. #ifndef SABERSENSE_LOCKUP_DELAY
  324. #define SABERSENSE_LOCKUP_DELAY 200
  325. #endif
  326.  
  327. #ifndef SABERSENSE_FORCE_PUSH_LENGTH
  328. #define SABERSENSE_FORCE_PUSH_LENGTH 5
  329. #endif
  330.  
  331. #ifndef BUTTON_DOUBLE_CLICK_TIMEOUT
  332. #define BUTTON_DOUBLE_CLICK_TIMEOUT 300
  333. #endif
  334.  
  335. #ifndef BUTTON_SHORT_CLICK_TIMEOUT
  336. #define BUTTON_SHORT_CLICK_TIMEOUT 300
  337. #endif
  338.  
  339. #ifndef BUTTON_HELD_TIMEOUT
  340. #define BUTTON_HELD_TIMEOUT 300
  341. #endif
  342.  
  343. #ifndef BUTTON_HELD_MEDIUM_TIMEOUT
  344. #define BUTTON_HELD_MEDIUM_TIMEOUT 1000
  345. #endif
  346.  
  347. #ifndef BUTTON_HELD_LONG_TIMEOUT
  348. #define BUTTON_HELD_LONG_TIMEOUT 2000
  349. #endif
  350.  
  351. #ifdef SABERSENSE_SWING_ON
  352. #define SABERSENSE_SWING_GESTURE
  353. #endif
  354.  
  355. #ifdef SABERSENSE_STAB_ON
  356. #define SABERSENSE_STAB_GESTURE
  357. #endif
  358.  
  359. #ifdef SABERSENSE_TWIST_ON
  360. #define SABERSENSE_TWIST_GESTURE
  361. #endif
  362.  
  363. #ifdef SABERSENSE_THRUST_ON
  364. #define SABERSENSE_THRUST_GESTURE
  365. #endif
  366.  
  367. EFFECT(dim); // for EFFECT_POWERSAVE
  368. EFFECT(battery); // for EFFECT_BATTERY_LEVEL
  369. EFFECT(vmbegin); // for Begin Volume Menu
  370. EFFECT(vmend); // for End Volume Menu
  371. EFFECT(volup); // for increse volume
  372. EFFECT(voldown); // for decrease volume
  373. EFFECT(volmin); // for minimum volume reached
  374. EFFECT(volmax); // for maximum volume reached
  375. EFFECT(faston); // for EFFECT_FAST_ON
  376. EFFECT(blstbgn); // for Begin Multi-Blast
  377. EFFECT(blstend); // for End Multi-Blast
  378. #ifdef SABERSENSE_OS7_LEGACY_SUPPORT
  379. EFFECT(quote); // for playing quotes. Required for ProffieOS 7.x.
  380. #endif
  381.  
  382.  
  383. // The Saber class implements the basic states and actions
  384. // for the saber.
  385. class SabersenseButtons : public PROP_INHERIT_PREFIX PropBase {
  386. public:
  387. SabersenseButtons() : PropBase() {}
  388. const char* name() override { return "SabersenseButtons"; }
  389.  
  390. void Loop() override {
  391. PropBase::Loop();
  392. DetectTwist();
  393. Vec3 mss = fusor.mss();
  394. if (SaberBase::IsOn()) {
  395. DetectSwing();
  396. if (auto_lockup_on_ &&
  397. !swinging_ &&
  398. fusor.swing_speed() > 120 &&
  399. millis() - clash_impact_millis_ > SABERSENSE_LOCKUP_DELAY &&
  400. SaberBase::Lockup()) {
  401. SaberBase::DoEndLockup();
  402. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  403. auto_lockup_on_ = false;
  404. }
  405. if (auto_melt_on_ &&
  406. !swinging_ &&
  407. fusor.swing_speed() > 60 &&
  408. millis() - clash_impact_millis_ > SABERSENSE_LOCKUP_DELAY &&
  409. SaberBase::Lockup()) {
  410. SaberBase::DoEndLockup();
  411. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  412. auto_melt_on_ = false;
  413. }
  414.  
  415. // EVENT_PUSH
  416. if (fabs(mss.x) < 3.0 &&
  417. mss.y * mss.y + mss.z * mss.z > 70 &&
  418. fusor.swing_speed() < 30 &&
  419. fabs(fusor.gyro().x) < 10) {
  420. if (millis() - push_begin_millis_ > SABERSENSE_FORCE_PUSH_LENGTH) {
  421. Event(BUTTON_NONE, EVENT_PUSH);
  422. push_begin_millis_ = millis();
  423. }
  424. } else {
  425. push_begin_millis_ = millis();
  426. }
  427. } else {
  428. // EVENT_SWING - Swing On gesture control to allow fine tuning of speed needed to ignite
  429. if (millis() - saber_off_time_ < MOTION_TIMEOUT) {
  430. SaberBase::RequestMotion();
  431. if (swinging_ && fusor.swing_speed() < 90) {
  432. swinging_ = false;
  433. }
  434. if (!swinging_ && fusor.swing_speed() > SABERSENSE_SWING_ON_SPEED) {
  435. swinging_ = true;
  436. Event(BUTTON_NONE, EVENT_SWING);
  437. }
  438. }
  439. // EVENT_THRUST
  440. if (mss.y * mss.y + mss.z * mss.z < 16.0 &&
  441. mss.x > 14 &&
  442. fusor.swing_speed() < 150) {
  443. if (millis() - thrust_begin_millis_ > 15) {
  444. Event(BUTTON_NONE, EVENT_THRUST);
  445. thrust_begin_millis_ = millis();
  446. }
  447. } else {
  448. thrust_begin_millis_ = millis();
  449. }
  450. }
  451. }
  452.  
  453. // Volume Menu
  454. void VolumeUp() {
  455. STDOUT.println("Volume up");
  456. if (dynamic_mixer.get_volume() < VOLUME) {
  457. dynamic_mixer.set_volume(std::min<int>(VOLUME + VOLUME * 0.1,
  458. dynamic_mixer.get_volume() + VOLUME * 0.10));
  459. if (SFX_volup) {
  460. hybrid_font.PlayCommon(&SFX_volup);
  461. } else {
  462. beeper.Beep(0.5, 2000);
  463. }
  464. STDOUT.print("Volume Up - Current Volume: ");
  465. STDOUT.println(dynamic_mixer.get_volume());
  466. } else {
  467. // Cycle through ends of Volume Menu option
  468. #ifdef VOLUME_MENU_CYCLE
  469. if (!max_vol_reached_) {
  470. if (SFX_volmax) {
  471. hybrid_font.PlayCommon(&SFX_volmax);
  472. } else {
  473. beeper.Beep(0.5, 3000);
  474. }
  475. STDOUT.print("Maximum Volume: ");
  476. max_vol_reached_ = true;
  477. } else {
  478. dynamic_mixer.set_volume(std::max<int>(VOLUME * 0.1,
  479. dynamic_mixer.get_volume() - VOLUME * 0.90));
  480. if (SFX_volmin) {
  481. hybrid_font.PlayCommon(&SFX_volmin);
  482. } else {
  483. beeper.Beep(0.5, 1000);
  484. }
  485. STDOUT.print("Minimum Volume: ");
  486. max_vol_reached_ = false;
  487. }
  488. #else
  489. if (SFX_volmax) {
  490. hybrid_font.PlayCommon(&SFX_volmax);
  491. } else {
  492. beeper.Beep(0.5, 3000);
  493. }
  494. STDOUT.print("Maximum Volume: ");
  495. #endif
  496. }
  497. }
  498.  
  499. void VolumeDown() {
  500. STDOUT.println("Volume Down");
  501. if (dynamic_mixer.get_volume() > (0.10 * VOLUME)) {
  502. dynamic_mixer.set_volume(std::max<int>(VOLUME * 0.1,
  503. dynamic_mixer.get_volume() - VOLUME * 0.10));
  504. if (SFX_voldown) {
  505. hybrid_font.PlayCommon(&SFX_voldown);
  506. } else {
  507. beeper.Beep(0.5, 2000);
  508. }
  509. STDOUT.print("Volume Down - Current Volume: ");
  510. STDOUT.println(dynamic_mixer.get_volume());
  511. } else {
  512. #ifdef VOLUME_MENU_CYCLE
  513. if (!min_vol_reached_) {
  514. if (SFX_volmin) {
  515. hybrid_font.PlayCommon(&SFX_volmin);
  516. } else {
  517. beeper.Beep(0.5, 1000);
  518. }
  519. STDOUT.print("Minimum Volume: ");
  520. min_vol_reached_ = true;
  521. } else {
  522. dynamic_mixer.set_volume(VOLUME);
  523. if (SFX_volmax) {
  524. hybrid_font.PlayCommon(&SFX_volmax);
  525. } else {
  526. beeper.Beep(0.5, 3000);
  527. }
  528. STDOUT.print("Maximum Volume: ");
  529. min_vol_reached_ = false;
  530. }
  531. #else
  532. if (SFX_volmin) {
  533. hybrid_font.PlayCommon(&SFX_volmin);
  534. } else {
  535. beeper.Beep(0.5, 1000);
  536. }
  537. STDOUT.print("Minimum Volume: ");
  538. #endif
  539.  
  540. }
  541. }
  542.  
  543. // Button Clicker to play press/release wav files when buttons are pressed.
  544. // Intended for Scavenger hilt where wheel makes tactile feel difficult.
  545. // Requires press.wav and release.wav files to work.
  546. void PlaySound(const char* sound) {
  547. RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
  548. if (player) {
  549. if (!player->PlayInCurrentDir(sound)) player->Play(sound);
  550. }
  551. }
  552. bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
  553. switch (EVENTID(button, event, modifiers)) {
  554. case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ANY_BUTTON | MODE_ON):
  555. case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ANY_BUTTON | MODE_OFF):
  556. case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ANY_BUTTON | MODE_ON):
  557. case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ANY_BUTTON | MODE_OFF):
  558. SaberBase::RequestMotion();
  559. PlaySound("press.wav");
  560. return false;
  561. case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON):
  562. case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_OFF):
  563. case EVENTID(BUTTON_AUX, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON):
  564. case EVENTID(BUTTON_AUX, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_OFF):
  565. PlaySound("release.wav");
  566. if (SaberBase::Lockup()) {
  567. SaberBase::DoEndLockup();
  568. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  569. return true;
  570. } else {
  571. return false;
  572. }
  573. case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ON):
  574. case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_ON):
  575. if (accel_.x < -0.15) {
  576. pointing_down_ = true;
  577. } else {
  578. pointing_down_ = false;
  579. }
  580. return true;
  581.  
  582. // Gesture Controls
  583. #ifdef SABERSENSE_SWING_ON
  584. case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_OFF):
  585. // Due to motion chip startup on boot creating false ignition we delay Swing On at boot for 3000ms
  586. if (millis() > 3000) {
  587. FastOn();
  588. }
  589. return true;
  590. #endif
  591.  
  592. #ifdef SABERSENSE_TWIST_ON
  593. case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF):
  594. // Delay twist events to prevent false trigger from over twisting
  595. if (millis() - last_twist_ > 2000 &&
  596. millis() - saber_off_time_ > 1000) {
  597. FastOn();
  598. last_twist_ = millis();
  599. }
  600. return true;
  601. #endif
  602.  
  603. #ifdef SABERSENSE_TWIST_OFF
  604. case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON):
  605. // Delay twist events to prevent false trigger from over twisting
  606. if (millis() - last_twist_ > 3000) {
  607. Off();
  608. last_twist_ = millis();
  609. saber_off_time_ = millis();
  610. }
  611. return true;
  612. #endif
  613.  
  614. #ifdef SABERSENSE_STAB_ON
  615. case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_OFF):
  616. if (millis() - saber_off_time_ > 1000) {
  617. FastOn();
  618. }
  619. return true;
  620. #endif
  621.  
  622. #ifdef SABERSENSE_THRUST_ON
  623. case EVENTID(BUTTON_NONE, EVENT_THRUST, MODE_OFF):
  624. if (millis() - saber_off_time_ > 1000) {
  625. FastOn();
  626. }
  627. return true;
  628. #endif
  629.  
  630. // Multiple Skips only available with 2 button installs.
  631. // Skips forward five fonts if pointing up, skips back five fonts if pointing down.
  632. case EVENTID(BUTTON_AUX, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF):
  633. // backwards if pointing down
  634. SetPreset(current_preset_.preset_num + (fusor.angle1() < -M_PI / 4 ? -5 : 5), true);
  635. #ifdef SAVE_PRESET
  636. SaveState(current_preset_.preset_num);
  637. #endif
  638. return true;
  639.  
  640. // Skips forward ten fonts if pointing up, skips back ten fonts if pointing down.
  641. case EVENTID(BUTTON_AUX, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF):
  642. // backwards if pointing down
  643. SetPreset(current_preset_.preset_num + (fusor.angle1() < -M_PI / 4 ? -10 : 10), true);
  644. #ifdef SAVE_PRESET
  645. SaveState(current_preset_.preset_num);
  646. #endif
  647. return true;
  648.  
  649. // Saber ON AND Volume Adjust.
  650. case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF):
  651. IgnoreClash(50); // Hopefully prevents false clashes due to 'clicky' button.
  652. // Low threshold so as not to conflict with 1-button lockup or volume menu.
  653. if (!mode_volume_) {
  654. On();
  655. } else {
  656. if (fusor.angle1() > 0) {
  657. VolumeUp();
  658. } else {
  659. VolumeDown();
  660. }
  661. }
  662. return true;
  663.  
  664. // Modified 2 button 'next/previous preset' AND volume down,
  665. // to align with multiple skips above.
  666. // Forward one font pointing up, back one font pointing down.
  667. case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF):
  668. // backwards if pointing down
  669. if (!mode_volume_) {
  670. SetPreset(current_preset_.preset_num + (fusor.angle1() < -M_PI / 4 ? -1 : 1), true);
  671. } else {
  672. VolumeDown();
  673. }
  674. #ifdef SAVE_PRESET
  675. SaveState(current_preset_.preset_num);
  676. #endif
  677. return true;
  678.  
  679. // 1 button 'next/previous preset' command.
  680. // Forward one font pointing up, back one font pointing down.
  681. #if NUM_BUTTONS == 1
  682. case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD, MODE_OFF):
  683. // backwards if pointing down
  684. if (!mode_volume_) {
  685. SetPreset(current_preset_.preset_num + (fusor.angle1() < -M_PI / 4 ? -1 : 1), true);
  686. } else {
  687. VolumeDown();
  688. }
  689. #ifdef SAVE_PRESET
  690. SaveState(current_preset_.preset_num);
  691. #endif
  692. return true;
  693. #endif
  694.  
  695. // 1 Button skips to first preset (up) or last preset (down)
  696. // or middle preset if horizontal:
  697. #if NUM_BUTTONS == 2
  698. case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD_LONG, MODE_OFF):
  699. #endif
  700. case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_LONG, MODE_OFF):
  701. #define DEGREES_TO_RADIANS (M_PI / 180)
  702. if (fusor.angle1() > 45 * DEGREES_TO_RADIANS) {
  703. // If pointing up
  704. SetPreset(0, true);
  705. } else if (fusor.angle1() < -45 * DEGREES_TO_RADIANS) {
  706. // If pointing down
  707. SetPreset(-1, true);
  708. } else {
  709. // If horizontal
  710. CurrentPreset tmp;
  711. tmp.SetPreset(-1);
  712. SetPreset(tmp.preset_num / 2, true);
  713. }
  714. #ifdef SAVE_PRESET
  715. SaveState(current_preset_.preset_num);
  716. #endif
  717. return true;
  718.  
  719. // 1 and 2 button: Previous Preset, retained legacy control.
  720. #if NUM_BUTTONS == 2
  721. case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_AUX):
  722. #endif
  723. if (!mode_volume_) {
  724. previous_preset();
  725. }
  726. return true;
  727.  
  728. // Skips to first preset (up) or last (battery or charge) preset (down)
  729. // or middle preset if horizontal:
  730. #if NUM_BUTTONS == 2
  731. // 2 button: First Preset
  732. case EVENTID(BUTTON_AUX, EVENT_HELD_LONG, MODE_OFF):
  733. #endif
  734. #define DEGREES_TO_RADIANS (M_PI / 180)
  735. if (fusor.angle1() > 45 * DEGREES_TO_RADIANS) {
  736. // If pointing up
  737. SetPreset(0, true);
  738. } else if (fusor.angle1() < -45 * DEGREES_TO_RADIANS) {
  739. // If pointing down
  740. SetPreset(-1, true);
  741. } else {
  742. // If horizontal
  743. CurrentPreset tmp;
  744. tmp.SetPreset(-1);
  745. SetPreset(tmp.preset_num / 2, true);
  746. }
  747. #ifdef SAVE_PRESET
  748. SaveState(current_preset_.preset_num);
  749. #endif
  750. return true;
  751.  
  752. // BladeID and manual blade array selector.
  753. case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF):
  754. #ifdef SABERSENSE_ARRAY_SELECTOR
  755. // Cycles through blade arrays regardless of BladeID status.
  756. if (fusor.angle1() > 0) {
  757. // Cycles forward...
  758. SabersenseArraySelector::cycleForward();
  759. } else {
  760. // Cycles backward.
  761. SabersenseArraySelector::cycleBackward();
  762. }
  763. FindBladeAgain();
  764. SabersenseArraySelector::checkAndPlaySound();
  765. return true;
  766. #else
  767. // Runs BladeID manually to actually check BladeID status.
  768. // Won't change arrays unless status tells it to (i.e. Data/Neg resistance changes).
  769. FindBladeAgain();
  770. SabersenseArraySelector::checkAndPlaySound();
  771. return true;
  772. #endif
  773.  
  774. // Activate Muted
  775. case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF):
  776. if (SetMute(true)) {
  777. unmute_on_deactivation_ = true;
  778. On();
  779. }
  780. return true;
  781.  
  782. // Turn Blade OFF
  783. #if NUM_BUTTONS > 1
  784. // 2 button
  785. case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, MODE_ON):
  786. #else
  787. // 1 button
  788. case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_LONG, MODE_ON):
  789. #endif
  790. if (!SaberBase::Lockup()) {
  791. #ifndef DISABLE_COLOR_CHANGE
  792. if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) {
  793. // Just exit color change mode.
  794. // Don't turn saber off.
  795. ToggleColorChangeMode();
  796. } else {
  797. Off();
  798. }
  799. }
  800. saber_off_time_ = millis();
  801. swing_blast_ = false;
  802. return true;
  803. #endif
  804.  
  805. // Character Quote and Force Effect, Blade ON.
  806. // Hilt pointed UP for Character Quote, plays sequentially,
  807. // Hilt pointed DOWN for Force Effect, plays randomly.
  808. case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON):
  809. #ifndef SABERSENSE_FLIP_AUDIO_PLAYERS
  810. // Define reverses UP/DOWN options for QUOTE/FORCE/TRACK audio player.
  811. // Quote player points upwards.
  812. if (SFX_quote) {
  813. if (fusor.angle1() > 0) {
  814. SFX_quote.SelectNext();
  815. SaberBase::DoEffect(EFFECT_QUOTE, 0);
  816. } else {
  817. SaberBase::DoForce(); // Force effect for hilt pointed DOWN.
  818. }
  819. } else {
  820. SaberBase::DoForce(); // Fallback: play force effect if no quotes are available.
  821. }
  822. return true;
  823. #else
  824. // Quote player points downwards.
  825. if (SFX_quote) {
  826. if (fusor.angle1() < 0) {
  827. SFX_quote.SelectNext();
  828. SaberBase::DoEffect(EFFECT_QUOTE, 0);
  829. } else {
  830. SaberBase::DoForce(); // Force effect for hilt pointed DOWN.
  831. }
  832. } else {
  833. SaberBase::DoForce(); // Fallback: play force effect if no quotes are available.
  834. }
  835. return true;
  836. #endif
  837.  
  838. // Character Quote and Music Track, Blade OFF.
  839. // Play sequential quote pointing up, play music track pointing down.
  840. case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF):
  841. #ifndef SABERSENSE_FLIP_AUDIO_PLAYERS
  842. // Define reverses UP/DOWN options for QUOTE/FORCE/TRACK audio players.
  843. // Quote player points upwards.
  844. if (SFX_quote) {
  845. if (fusor.angle1() > 0) {
  846. SFX_quote.SelectNext();
  847. SaberBase::DoEffect(EFFECT_QUOTE, 0);
  848. } else {
  849. StartOrStopTrack(); // Play track for hilt pointed DOWN.
  850. }
  851. } else {
  852. StartOrStopTrack(); // Fallback: play track if no quotes are available.
  853. }
  854. return true;
  855. #else
  856. // Quote player points downwards.
  857. if (SFX_quote) {
  858. if (fusor.angle1() < 0) {
  859. SFX_quote.SelectNext();
  860. SaberBase::DoEffect(EFFECT_QUOTE, 0);
  861. } else {
  862. StartOrStopTrack(); // Play track for hilt pointed DOWN.
  863. }
  864. } else {
  865. StartOrStopTrack(); // Fallback: play track if no quotes are available.
  866. }
  867. return true;
  868. #endif
  869.  
  870. // Colour Change.
  871. // 1 and 2 button modes.
  872. #ifndef DISABLE_COLOR_CHANGE
  873. case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_ON):
  874. ToggleColorChangeMode();
  875. return true;
  876. // 2 button mode only.
  877. #if NUM_BUTTONS == 2
  878. case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON | BUTTON_POWER):
  879. ToggleColorChangeMode();
  880. return true;
  881. #endif
  882. #endif
  883.  
  884. // Blaster Deflection
  885. #if NUM_BUTTONS == 1
  886. // 1 button
  887. case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON):
  888. #else
  889. // 2 button
  890. case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON):
  891. case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON):
  892. #endif
  893. swing_blast_ = false;
  894. SaberBase::DoBlast();
  895. return true;
  896.  
  897. // Multi-Blaster Deflection mode
  898. case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON | BUTTON_POWER):
  899. swing_blast_ = !swing_blast_;
  900. if (swing_blast_) {
  901. if (SFX_blstbgn) {
  902. hybrid_font.PlayCommon(&SFX_blstbgn);
  903. } else {
  904. hybrid_font.SB_Effect(EFFECT_BLAST, 0);
  905. }
  906. } else {
  907. if (SFX_blstend) {
  908. hybrid_font.PlayCommon(&SFX_blstend);
  909. } else {
  910. hybrid_font.SB_Effect(EFFECT_BLAST, 0);
  911. }
  912. }
  913. return true;
  914.  
  915. case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON):
  916. if (swing_blast_) {
  917. SaberBase::DoBlast();
  918. }
  919. return true;
  920.  
  921. // Lockup
  922. #if NUM_BUTTONS == 1
  923. // 1 button lockup
  924. case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER):
  925. #else
  926. #ifndef SABERSENSE_NO_LOCKUP_HOLD
  927. // 2 button lockup
  928. case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD, MODE_ON):
  929. #else
  930. case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_AUX):
  931. #endif
  932. #endif
  933. if (accel_.x < -0.15) {
  934. SaberBase::SetLockup(SaberBase::LOCKUP_DRAG);
  935. } else {
  936. SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  937. }
  938. swing_blast_ = false;
  939. SaberBase::DoBeginLockup();
  940. return true;
  941. break;
  942.  
  943. // Lightning Block, 1 and 2 button.
  944. case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD, MODE_ON):
  945. SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK);
  946. swing_blast_ = false;
  947. SaberBase::DoBeginLockup();
  948. return true;
  949. break;
  950.  
  951. // Melt
  952. case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_ON | BUTTON_POWER):
  953. SaberBase::SetLockup(SaberBase::LOCKUP_MELT);
  954. swing_blast_ = false;
  955. SaberBase::DoBeginLockup();
  956. return true;
  957. break;
  958.  
  959. case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_OFF):
  960. SaberBase::RequestMotion();
  961. return true;
  962.  
  963. // Enter Volume MENU
  964. #if NUM_BUTTONS == 1
  965. // 1 button
  966. case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_OFF | BUTTON_POWER):
  967. #else
  968. // 2 button
  969. case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_POWER):
  970.  
  971. #endif
  972. if (!mode_volume_) {
  973. mode_volume_ = true;
  974. if (SFX_vmbegin) {
  975. hybrid_font.PlayCommon(&SFX_vmbegin);
  976. } else {
  977. beeper.Beep(0.5, 3000);
  978. }
  979. STDOUT.println("Enter Volume Menu");
  980. } else {
  981. mode_volume_ = false;
  982. if (SFX_vmend) {
  983. hybrid_font.PlayCommon(&SFX_vmend);
  984. } else {
  985. beeper.Beep(0.5, 3000);
  986. }
  987. STDOUT.println("Exit Volume Menu");
  988. }
  989. return true;
  990.  
  991. // Battery level
  992. case EVENTID(BUTTON_POWER, EVENT_FOURTH_SAVED_CLICK_SHORT, MODE_OFF):
  993. #if NUM_BUTTONS == 2
  994. case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_LONG, MODE_OFF):
  995. #endif
  996. talkie.SayDigit((int)floorf(battery_monitor.battery()));
  997. talkie.Say(spPOINT);
  998. talkie.SayDigit(((int)floorf(battery_monitor.battery() * 10)) % 10);
  999. talkie.SayDigit(((int)floorf(battery_monitor.battery() * 100)) % 10);
  1000. talkie.Say(spVOLTS);
  1001. SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0);
  1002. return true;
  1003.  
  1004. #ifdef BLADE_DETECT_PIN
  1005. case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_ON):
  1006. case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_OFF):
  1007. // Might need to do something cleaner, but let's try this for now.
  1008. blade_detected_ = true;
  1009. FindBladeAgain();
  1010. SaberBase::DoBladeDetect(true);
  1011. return true;
  1012.  
  1013. case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_ON):
  1014. case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_OFF):
  1015. // Might need to do something cleaner, but let's try this for now.
  1016. blade_detected_ = false;
  1017. FindBladeAgain();
  1018. SaberBase::DoBladeDetect(false);
  1019. return true;
  1020. #endif
  1021.  
  1022. // Events that needs to be handled regardless of what other buttons are pressed.
  1023. case EVENTID(BUTTON_AUX2, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON):
  1024. if (SaberBase::Lockup()) {
  1025. SaberBase::DoEndLockup();
  1026. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1027. return true;
  1028. }
  1029. }
  1030. return false;
  1031. }
  1032.  
  1033. #ifdef SABERSENSE_OS7_LEGACY_SUPPORT
  1034. void SB_Effect(EffectType effect, float location) override { // Required for ProffieOS 7.x.
  1035. #else
  1036. void SB_Effect(EffectType effect, EffectLocation location) override { // Required for ProffieOS 8.x.
  1037. #endif
  1038. switch (effect) {
  1039. case EFFECT_QUOTE: hybrid_font.PlayCommon(&SFX_quote); return;
  1040. case EFFECT_POWERSAVE:
  1041. if (SFX_dim) {
  1042. hybrid_font.PlayCommon(&SFX_dim);
  1043. } else {
  1044. beeper.Beep(0.5, 3000);
  1045. }
  1046. return;
  1047. case EFFECT_BATTERY_LEVEL:
  1048. if (SFX_battery) {
  1049. hybrid_font.PlayCommon(&SFX_battery);
  1050. } else {
  1051. beeper.Beep(0.5, 3000);
  1052. }
  1053. return;
  1054. case EFFECT_FAST_ON:
  1055. if (SFX_faston) {
  1056. hybrid_font.PlayCommon(&SFX_faston);
  1057. }
  1058. return;
  1059.  
  1060. default: break; // avoids compiler warning
  1061. }
  1062. }
  1063.  
  1064. private:
  1065. bool pointing_down_ = false;
  1066. bool swing_blast_ = false;
  1067. bool mode_volume_ = false;
  1068. bool auto_lockup_on_ = false;
  1069. bool auto_melt_on_ = false;
  1070. bool max_vol_reached_ = false;
  1071. bool min_vol_reached_ = false;
  1072. uint32_t thrust_begin_millis_ = millis();
  1073. uint32_t push_begin_millis_ = millis();
  1074. uint32_t clash_impact_millis_ = millis();
  1075. uint32_t last_twist_ = millis();
  1076. uint32_t last_push_ = millis();
  1077. uint32_t saber_off_time_ = millis();
  1078. };
  1079.  
  1080. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement