From 10d29981322fea07d171d421b1c608dbdd26e770 Mon Sep 17 00:00:00 2001 From: NoSloppy <53964195+NoSloppy@users.noreply.github.com> Date: Sun, 24 Nov 2024 01:26:46 -0500 Subject: [PATCH] Bc prop overhaul (#682) * Delete props/saber_BC_buttons.h * Add files via upload * Update saber_BC_buttons.h * spelling * Update saber_BC_buttons.h * edit guards2 * change name from Toggle to EnterBladeLengthMode() * add Custom Spec modes Vol and Blade Length * Update saber_BC_buttons.h * stab location aware * Update instructions sections * conform to sound lib changes from 01c5872 * use pos_ in BCSelectBladeMode comment inline in prev conversation * add temp comment * test review comment wiping out * revised BCVolumeMode as per review * Intro comments edits as per review * guard BC_MAIN_BLADE == BC_SECOND_BLADE warning * BCSelectBladeMode ironed out * add SteppedModeBase to BCMenuSpec * BCMenuSpec as default unless def MENU_SPEC_TEMPLATE * review updates. see commit comment Change selected_blade_ to menu_current_blade_. Update ChangeBladeLengthMode to use pos_+1 for display and speaking (one-based index for user facing things). BC version too. current_volume_ function scope and useful only. Helper function `current_blade()` to handle a bunch of this->pos + 1) code. Remove redundant say() calls from prev() and next(). Add typedef for SteppedModeBase. Remove BC_ENABLE_OS_MENU define and base the use of prop-based BcMenuSpec menus on the non-existence of a MENU_SPEC_TEMPLATE in user config. add a Setup() function to init() soundLibrary. * actually remove redundant say() calls * use sound lib v2 for revert * prevent volume menu from over-queueing min/max volume prompts. * volume menu percentage fix * refreshed update to saber_BC_buttons.h * fix 3btn vs 2btn ON/blaster block * Resolving first section of review notes. * Update some button things and Melt use location Thrust direction needs some work. Not working great. * Location effects Also updated BladeSet blade naming for consistency * remove empty condition * remove GetSoundLengthNow function * missing semicolon * override DoAccel() to use bi-directional stab effect. * simplify stab direction detection * even more simplified forward_stab_ setting * restore safeguard delays to gesture ignitions * Update more gesture guards * guard volume menu if in scroll presets. * guard preset change if already in scroll presets. * reload style after blade length change * allow access to UpdateStyle() from modes * skip blade selection if only one blade * edit redundant SetMute calls * use absolute value of diff.x instead of + OR - * fix stab: since saber_time_off calc. * fix single blade ON muted comment. not dual blades. * update USER effects top comments. * fix weird indentation. * actually fix stab guard. * whitespace * another stab guard fix * BCChangeBladeLengthBlade1 select() use popMode() * Fix location lockup-drag for dual blades. * move thrusting_blade_declaration. * remove duplicate declaration. * add stab debug printout * NO_BLADE_NO_GEST_ONOFF update * set blade_detected_ in ScanBladeIdNow() * use onreturn in BCSelectBlade, and unguard BCVolumeMenu * better DelayTimer * clarify degrees / radians comment * fix redundant twist_timer_ check * DOUBLE_TWIST_TIMEOUT -> REGULAR_TWIST_TIMEOUT * add comment for DoChangePreset() * revert unneeded changes for blade_detected_ * revert weird indent * adjust DelayTimer again. * Update DelayTimer() * use fabs in EVENT_THRUST * add comment explaining DoChangePresetFast * fix #endif placement for EnterBladeLengthMode() * make EnterVolumeMenu() use either BC or OS menu based on MENU_SPEC_TEMPLATE defined. * clean up horizon detection for drag for dual blades. * NUM_BLADES, not NUM_BUTTONS * remove unneeded guard for single blade use in BCSelectBladeMode * fix indents and redundant return. * added helper functions GestureEnableBattleMode and NoBladeDisableGestures * fix void return in NoBladeDisableGestures() and missing ; * update USER effect twists. * simplify BCVolumeMode menu * remove faston stuff and unused SB_Effect cases. * fix indents * remove redundant if * fix indent * remove pointless TurnDualBladesOn/Off and call TurnOn/OffHelpers direct instead. * just return EffectLocation GetThrustBladeLocation(), no variable needed * remove unneeded global call to GetThrustBladeLocation * more indentation fixing * move return one line down for visibility * use fusor.angle1() instead of accel * fix Thrust On and GetThrustBladeLocation() * fix event twist stopTimer() call * whitespace * remove redundant printouts for THRUST ON * Fix saved twist for USER effects. * remove old comments * restore printout to DEBUG only * un-virtual EnterBladeLengthMode() * remove unneeded returns. * remove isTimerRunning() * say Edit Volume instead of Enter Volume Menu * change vol menu announce pt2 * fix EnterBladeLengthMode(); use condition * fix the fix for EnterBladeLengthMode() * Fix quote timer. Now overlap timer for quote and force. * rename delay timer variables * clarify beep delay comment --- props/saber_BC_buttons.h | 3259 ++++++++++++++++++++++++++++---------- 1 file changed, 2380 insertions(+), 879 deletions(-) diff --git a/props/saber_BC_buttons.h b/props/saber_BC_buttons.h index b0863fe0b..45252b923 100644 --- a/props/saber_BC_buttons.h +++ b/props/saber_BC_buttons.h @@ -2,82 +2,125 @@ saber_BC_buttons.h http://fredrik.hubbe.net/lightsaber/proffieos.html Copyright (c) 2016-2019 Fredrik Hubinette - Copyright (c) 2023 Brian Conner with contributions by: - Fredrik Hubinette, Fernando da Rosa, Matthew McGeary and Scott Weber. + Copyright (c) 2023-2024 Brian Conner with contributions by: + Fredrik Hubinette, Fernando da Rosa, Matthew McGeary, and Scott Weber. Distributed under the terms of the GNU General Public License v3. http://www.gnu.org/licenses/ -Includes 1 and 2 button controls. +Includes 1 or 2 button controls. (3rd button for power control of Dual Blades supported as well). Incorporates an intuitive control scheme so button actions are consistant whether blade is on or off. -Best if used with OS6 ProffieOS_Voicepack spoken menu prompts. +Best if used with ProffieOS_Voicepack spoken menu prompts. +This prop version requires a V2 ProffieOS Voicepack for menus to work right. + Download your choice of language and variation here: http://fredrik.hubbe.net/lightsaber/sound/ +Also, a large variety of FREE in-universe character Voicepacks available here: +https://crucible.hubbe.net/t/additional-voicepacks/4227 +If you'd care to make a donation for my time: +https://www.buymeacoffee.com/brianconner Features: - Live preset changing while blade is running (skips font.wav and preon). - Battle Mode, gesture ignitions, and multi-blast based on fett263's work. - Auto enter/exit multi-blaster block mode. - Spoken Battery level in volts OR percentage. Both always available. -- Dedicated Quote sounds - Always available. force.wavs can remain as force. +- Dedicated Quote sounds - Always available, blade ON or OFF. Force.wavs can remain as force. Add quote.wav files to font to use. Live toggle between sequential or random quote play. -- Play / Stop track control while blade is on. + Quotes can be triggered while blade is off. +- Play / Stop track control while blade is OFF OR ON. - Force Push is always available, not just in Battle Mode. - Melt is always available as no button, with pull-away or button to end. -- Drag is always clash with button pressed while pointing down. -- No blade inserted = no gestures option if Blade Detect is used. -- Optional On-the-fly volume controls with Quick MIN and MAX levels. +- Drag is always clash with button pressed while pointing DOWN. +- No gestures if No blade is inserted - available option if Blade Detect is used. +- Custom Volume menu with Quick MIN and MAX levels. - Bypass preon and/or postoff based on blade angle. - Spam Blast - Enter this mode to make the button super sensitive for multiple blaster blocks. Presses are prioritized over other features. No limits, no lag when "rapid firing". -- Swap feature with sound - Just an additional EFFECT that can be used to trigger - blade animations. See below. - No inadvertant effects during preon. - +- Rotary control of Volume and Scroll Presets. (Rotate hilt like turning a dial) +- * NEW for OS8 * + - Dual blade independent ignition and retraction control with a single Proffieboard. (Such as for a staff saber) + - Scroll Presets mode. + - Quotes play with blade ON or OFF, and will not overlap / interrupt one another. + - Mute sound plays before igniting muted.(optional mute.wav used) + - Blade Length editing menu. + - OS System Menu system available with a #define. +* Deprecated features: +- No Volume UP/DOWN with buttons. Use rotary control instead (see above), + but QuickMinVolume and QuickMaxVolume are still available. +- swap and monoforce features of yore are now just assignable User Effects. +- #define NO_VOLUME_MENU removed. --------------------------------------------------------------------------- Optional Blade style elements: On-Demand battery level - A layer built into the blade styles that reacts as the battery gets weaker, changing blade color from Green to Red, and the blade length shortens. -EFFECT_USER1 - Swap feature: Use as a standalone trigger for EffectSequence<>, - for example as a way to have multiple blade styles in one preset. - Usage:EffectSequence - Custom swap.wav files can be used as the sound effect, - otherwise ccchange.wav is used. -EFFECT_USER2 - For blade effects with sounds that might work better without hum, - this plays sounds monophonically. - (think seismic charge silence, iceblade etc...) - monosfx.wav files are used. It can be just the sound, or a - blade effect too by using EFFECT_USER2 in a TransitionEffectL. +User Effects: +- Can be built into blade style code to trigger anything that takes an EFFECT_XXXXX argument. +- If using styles from the Fett263 Style Library, these equate to "Special Abilities 1-8". +EFFECT_USER1 +EFFECT_USER2 +- 1 and 2 use POW button, and work when blade is ON. +EFFECT_USER3 +EFFECT_USER4 +- 3 and 4 use AUX button, and work when blade is ON. +EFFECT_USER5 +EFFECT_USER6 +- 5 and 6 use POW button, and work when blade is OFF. +EFFECT_USER7 +EFFECT_USER8 +- 7 and 8 use AUX button, and work when blade is OFF. +So if using a 1 button saber, USER effects 1,2,5,6 are available. + +EFFECT_POWERSAVE - PowerSave Dim Blade. Blade style should use a Mix or an AlphaL that applies + a transparent amount of Black to the base blade color. + Layer example: 50% brightness reduction + EffectSequence>,AlphaL>> --------------------------------------------------------------------------- Optional #defines: #define BC_ENABLE_AUTO_SWING_BLAST - Multi-blast initiated by simply swinging - within 1 second of last blast. - Exit by not swinging for 1 second. -#define FEMALE_TALKIE_VOICE - To use a female voice version of onboard Talkie. -#define NO_VOLUME_MENU - Option to omit On-the-fly Volume menu control with buttons. -#define ENABLE_FASTON - Use faston.wav for FastOn() ignitions, such as gesture ignitions or fast preset change. - The faston.wav sound will be replaced with fastout.wav. - If you have a good reason to keep faston.wav as is, please post at https://crucible.hubbe.net/ + within 1 second of last blast. + Exit by not swinging for 1 second. +#define FEMALE_TALKIE_VOICE - To use a female voice version of onboard Talkie. + +* NEW as of OS8: +#define MENU_SPEC_TEMPLATE - BC Volume Menu, Scroll Presets, ColorChange, and BC Blade Length adjust are used by default. + Use this define to override and add access the the OS System Menu for editing presets, colors etc... + Several template choices are available to set how the menus are used. See Documentation. +#define DYNAMIC_BLADE_LENGTH - This is required for onboard menu driven blade length adjustments. + The blade length maximum should be set in the user config file's BladeConfig section. + +// Adding the following define activates Dual Blade code in the prop. +#define BC_DUAL_BLADES - Use Dual Blades mode for a staff saber setup. + Defaults are BLADE 1 and BLADE 2 as MAIN and SECOND blades respectively. + BLADE # corresponds to the blades list in the BladeConfig section of the config file, + and therefore the position of the blade styles in a preset. + If your MAIN and SECOND blades are not in position 1 and 2, you can use the following + optional defines to manually set BLADE # to the appropriate blade from the BladeConfig list. +#define BC_MAIN_BLADE * Example: #define BC_MAIN_BLADE 2 +#define BC_SECOND_BLADE * Example: #define BC_SECOND_BLADE 4 + --------------------------------------------------------------------------- Gesture Controls: -- There are four gesture types: swing, stab, thrust and twist. +- There are three gesture types: swing, thrust and twist. For simplicity, using gesture ignition will automatically skip the preon effect. + * NOTE * If using Dual Blades, Swing and Twist ignitions will turn on all blades. Below are the options to add to the config to enable the various gestures. #define BC_SWING_ON -#define BC_STAB_ON #define BC_THRUST_ON #define BC_TWIST_ON #define BC_TWIST_OFF + #define NO_BLADE_NO_GEST_ONOFF -- If using blade detect, Gesture ignitions or retractions are disabled. - **NOTE** Only works when a BLADE_DETECT_PIN is defined. +- If using Blade Detect, this define disables gesture ignitions or retractions when no blade is inserted. + * Only works when a BLADE_DETECT_PIN is defined. #define BC_FORCE_PUSH -- This mode plays a force sound (or force push sound if the font contains it) with +- This gesture plays a force sound (or force push sound if the font contains push.wav) with a controlled pushing gesture, and is always available, not just in Battle Mode. #define BC_FORCE_PUSH_LENGTH @@ -98,208 +141,922 @@ Gesture Controls: when you pull away. - Automatic lockup and grazing clash (swinging through) detection works by measuring delay of the saber blade pulling back from the clash. - If you clash the blade and it does not pull back during the delay period, + If you clash the blade and do not pull away during the delay period, it is assumed to be a lockup and the lockup effect will show on the blade. If you clash the blade and pull away, only the bgn/end lockup effects will show. You can adjust the threshold of this detection by using: -#define BC_LOCKUP_DELAY (insert number here) + #define BC_LOCKUP_DELAY (insert number here) Default value is 200. If you don't rebound off the object (opponent's blade) but instead clash while swinging through, clash will automatically trigger. - To manually override the auto-lockup temporarily and get a regular clash, hold any button while clashing. -- Automatic clash/lockup uses the pre and post lockup effects +- Automatic clash/lockup uses the bgnlock and endlock effects so your blade style and font MUST have those capabilities to support battle mode. - Melt will automatically trigger with no buttons when you physically - stab something, and end when you pull away or push any button. + stab something, and end when you pull away or click any button. - Stab will trigger with no buttons and thrusting forward. +------------------------ GENERAL RULES ------------------------ + +Pointing the blade UP during ignition or retraction will bypass preon and postoff effects. +Gesture Ignitions bypass preon. + +Rotation gestures LEFT and RIGHT are single direction "twists". +TWIST gesture is a 2-direction rotation, like revving a motorcycle. Either direction first works. + +OS8 System Menus: +System menu will allow for editing of presets, colors and styles, similar to way the ProffieOS Workbench webpage and Edit mode work. +There's also an Edit Settings mode that has a volume level menu, blade length editing and more. +In this BC prop, the default mode is direct entry to BC Volume and BC Blade Length mode menus. +If you want to use the OS System Menu instead, you need to define a Menu Spec Template such as `#define MENU_SPEC_TEMPLATE DefaultMenuSpec` +While in any menu mode the following controls apply: +Save - Click POW +Cancel / Exit - Click AUX or Double Click POW + +Each section for controls have a descriptive version listed by feature and somwhat in the order +of using the saber, and a second summary list that is sorted by button clicks. + + ====================== 1 BUTTON CONTROLS ======================== | Sorted by ON or OFF state: (what it's like while using saber) | ================================================================= -************* WHILE SABER BLADE IS OFF *************** -Turn blade ON - Short click POW. (or gestures if defined, uses FastOn) - * NOTE * Gesture ignitions using FastOn bypass preon. -Turn ON without preon - Short click POW while pointing up. -Turn blade ON Muted - 4x click and hold POW. -Scroll Presets - Hold POW to toggle this mode on/off. Rotate hilt to cycle next and previous presets. - * NOTES * TWIST_ON not available in this mode. Resets to off after ignition. -Next Preset - Long click and release POW. -Prev Preset - Double click and hold POW, release after a second (click then long click). -Play/Stop Track - 4x click POW. -Volume Menu: - Enter/Exit - Hold POW + Clash. - Volume UP - Rotate Right - - or - - - Long click and release POW while in Volume Menu. (just like next preset) - Volume DOWN - Rotate Left - - or - - - Double click and hold POW, release after a second while in Volume Menu. - (click then long click, just like next preset) - Quick MAX Vol - Short click POW while in Volume Menu. - Quick MIN Vol - Double click POW while in Volume Menu. +* See below for modifications to these controls if BC_DUAL_BLADES is used. + +-------- When blade is OFF ------- +Turn Blade ON - Short Click POW, or use a Gesture Ignition (see Gesture Controls above). + * Gesture ignitions will bypass preon, and optional fastout.wav can be used instead of out.wav. +Turn ON without Preon - Short Click POW (while pointing UP), or use a Gesture Ignition. + * Uses fastout.wav if available. +Turn Blade ON Muted - Hold POW then Twist. + or + 4x Click and Hold POW. + * Muted ignitions will bypass preon. + Optional mute.wav will play before silent ignition and operation. + Saber unmutes when blade shuts off. +Scroll Presets - Hold POW. Beeps will be heard when entering mode, and font.wav will play. + Rotate hilt to cycle through presets. Right = Next, Left = Previous. + Click POW to ignite with chosen preset, or Click and Hold POW again to Exit. + * TWIST_ON not available in this mode. +Next Preset - Long Click then release POW (NOT pointing UP or DOWN). +Prev Preset - Long Click then release POW (while pointing DOWN). +Jump to First Preset - Long Click then release POW (while pointing UP). +Play/Stop Track - 4x Click POW. +BC Volume Menu: + Enter Menu - Hold POW and Clash. + Volume UP - Rotate Right. + Volume DOWN - Rotate Left. + Quick MAX Vol - Hold POW while in Volume Menu. + Quick MIN Vol - Double Click and Hold POW while in Volume Menu. + Save and Exit - Click POW. + Cancel and Exit - Double Click POW. +BC Blade Length Edit: + Enter Mode - Double Click and Hold POW. + Adjust - Rotate Right or Left + Save and Exit - Click POW. + Cancel and Exit - Double Click POW. +** OS System Menu * To use the OS menu system instead of the default BC Volume and BC Blade Length options, + use #define MENU_SPEC_TEMPLATE Spoken Battery Level - in volts - Triple click POW. - in percentage - Triple click and hold POW. -On-Demand Batt Level - Double click POW. - (requires EFFECT_BATTERY_LEVEL to be in blade style, - and uses battery.wav sound effect.) - -************* WHILE SABER BLADE IS ON **************** -Play/Stop Track - 4x click POW. -Next Preset Fast - Long click and release POW while pointing up. -Prev Preset Fast - Double click and hold POW, release after a second while pointing up. (click then long click) - * NOTE * Fast switching bypasses preon and font.wav. -Clash - No buttons, just hit the blade against something. - In Battle Mode, Hold POW and Clash to temporarily - override the auto-lockup and do regular Clash. -Stab - Just Thrust forward with a stabbing motion. - Works in Battle Mode. -Blaster Blocks - Click or Double click POW. -Spam Blaster Blocks - 3x click and hold while pointing up. This toggles SPAM BLAST mode ON/OFF, - and makes the button super sensitive for multiple blaster blocks. - * Note * This gets in the way of normal features, - so turn off when you're done spamming. Plays mzoom.wav. -Auto Swing Blast - if #define BC_ENABLE_AUTO_SWING_BLAST is active, - swinging within 1 second of doing button activated - Blaster Block will start this timed mode. - To trigger auto blaster blocks, swing saber - within 1 second of last Swing Blast block. - To exit, stop swinging for 1 second. -Lockup - Hold POW + Clash. - In Battle Mode, just Clash and stay there, - pull away or press POW to end lockup. -Drag - Hold POW + Clash while pointing down. -Melt - No button, just stab something. pull away or - press POW to end. -Lightning Block - Double click and hold POW. -Battle Mode - Triple click and hold POW to enter and exit. - Power OFF is disabled while in Battle Mode, - YOU MUST EXIT THE MODE WITH THIS COMBO FIRST. -Force Effect - Hold POW + Twist. (while NOT pointing up or down) -Monophonic Force - Hold POW + Twist. (while pointing up) -Color Change Mode - Hold POW + Twist. (while pointing down) - Rotate hilt to cycle through all available colors, or - Click POW to change if ColorChange<> used in blade style, - Click + hold POW to save color selection and exit. - Triple click POW to cancel and restore original color. - ColorChange explained: - If the style uses ColorChange<>, when you activate color change mode, - there will be up to 12 steps per rotation with a little sound at each step. - If it does not use ColorChange<>, the color wheel will be activated, - which has 32768 steps per rotation. - COLOR_CHANGE_DIRECT makes it so that IF the style uses ColorChange<>, - when you activate color change mode, it will immediately go to the - next color and exit color change mode. If the style does not use - ColorChange<>, it has no effect. -Quote Player - Triple click POW. -Toggle sequential or - random quote play - 4x click and hold POW. (while pointing down) -Force Push - Push hilt perpendicularly from a stop. -Swap (EffectSequence) - 4x click and hold POW. (while NOT pointing up) - * Requires EFFECT_USER1 in blade style. -PowerSave Dim Blade - 4x click and hold POW. (while pointing up) - To use Power Save requires AlphaL based EffectSequence in style. -Turn off blade - Hold POW and wait until blade is off, - or Twist if using #define BC_TWIST_OFF. -Turn OFF without postoff - Turn OFF while pointing up. + in percentage - 3x Click and Hold POW. + in volts - 3x Click and Hold POW (while pointing DOWN). + * Will show On-blade display if EFFECT_BATTERY_LEVEL is used in blade style. +On-Demand Batt Level - 3x Click and Hold POW, release after a second. (Double Click then Long Click) + * Requires EFFECT_BATTERY_LEVEL to be in blade style. + * Plays battery.wav sound effect if it exists in font or common folder, + otherwise a familiar tune of beeps :) + + +Quote Player - 3x Click POW. + * Does Force effect if no quote(s) exist. +Toggle Sequential or + Random quotes - 3x Click POW (while pointing DOWN). +User Effect 5 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +User Effect 6 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) + * Requires EFFECT_USER in blade style. + * Note the same controls when blade is ON are USER 1 and 2. + + +-------- When blade is ON ------- +Play/Stop Track - 4x Click POW. +Next Preset Fast - Hold POW and Twist (NOT pointing UP or DOWN). + * Fast switching presets bypasses preon and font.wav. +Previous Preset Fast - Hold POW and Twist (while pointing DOWN). +First Preset Fast - Hold POW and Twist (while pointing UP). + +Battle Mode - Hold POW + Swing to enter and exit Battle Mode. + * Power OFF is disabled while in Battle Mode. YOU MUST EXIT THE MODE FIRST. + +Clash - No button, just hit the blade against something. +Stab - Thrust forward with a stabbing motion. Works in Battle Mode. +Blaster Deflection - Click or Double click POW. +Spam Blaster Blocks - 4x Click and Hold POW (while pointing DOWN). Toggles SPAM BLAST mode ON/OFF. + * This makes the POW button super sensitive for multiple blaster blocks, + but gets in the way of controls for some other features, so deactivate when you're done spamming. + Plays mzoom.wav for activating/deactivating this mode. + Exits automatically when blade shuts OFF in case you leave it active. +Auto Swing Blast - Swinging within 1 second of doing a button activated Blaster Block will start this timed mode. + To trigger auto blaster blocks, swing saber within 1 second of last block. + To exit, stop swinging for 1 second. + * Requires #define BC_ENABLE_AUTO_SWING_BLAST. +Force Effect - Double Click and Hold POW, release after a second. (Double Click then Long Click) + +Lockup - Hold POW + Clash. Release button to end. + * In Battle Mode: + Just Clash and stay there to Lockup. + Holding POW while Clashing will do regular Clash without Locking up. + Pull away or press POW to end Lockup. +Drag - Hold POW + Clash (while pointing DOWN). Release button to end. +Melt - No button, just stab something. Rotate hilt to modify melting. Pull away or press POW to end. + +Lightning Block - Double Click and Hold POW. Release button to end. + +Color Change Mode - 3x Click and Hold POW. + Rotate hilt to cycle through all available colors. + Click POW to save color selection and Exit. + Double Click POW to Exit without saving. +Revert ColorChange - 3x Click and Hold POW, release after a second. (Double Click then Long Click) + Reverts ColorChanged blade color back to the uploaded config color. + * This is done outside ColorChange Mode + ColorChange explained: + If RotateColorsX is used in the blade style: + Rotate hilt to cycle through all available colors, + Hold POW to save color selection and exit. + If ColorChange<> is used in the blade style: + There are up to 12 colors per rotation with a ccchange.wav sound at each step. + If also using #define COLOR_CHANGE_DIRECT in the config, + simply entering Color Change Mode will select the next color in the list and exit Color Change Mode. + +Quote Player - 3x Click POW. + * Does Force effect if no quote(s) exist. +Toggle Sequential or + Random quotes - 3x Click POW (while pointing DOWN). +Force Push - Push hilt perpendicularly from a stop. + Plays push.wav if it exists, otherwise force.wav. +PowerSave Dim Blade - 4x Click and Hold POW (while pointing UP). + +User Effect 1 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +User Effect 2 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) + * Require EFFECT_USER in blade style. + * Note the same controls when blade is OFF are USER 5 and 6. + +Turn OFF blade - Hold POW until off -or - Twist if using #define BC_TWIST_OFF. +Turn OFF bypass postoff - Turn OFF (while pointing UP). + +--------------------------------------- + 1 button summary by clicks +--------------------------------------- +--------- When blade is OFF --------- +twist - turn both blades ON (requires Gesture defines) +stab +swing +1 click - turn blade ON + turn blade ON bypass preon (pointing up) +1 click long - first preset (pointing up) + next preset + previous preset (pointing down) +1 click held - enter/exit scroll presets + then twist - turn blade ON muted (back and forth twist) + then clash - enter BC volume menu + then rotate left - user effect 5 (keep holding POW until executed) + then rotate right - user effect 6 (keep holding POW until executed) +2 clicks held - blade length edit, or + OS system menu instead (requires #define MENU_SPEC_TEMPLATE) +3 clicks - quote + toggle sequential or random quotes (pointing down) +3 clicks long - on-demand battery level +3 clicks held - spoken battery level in percentage + spoken battery level in volts (pointing down) +4 clicks - play / stop track +4 clicks held - turn ON main blade muted +twist - turn blade ON (requires #define BC_TWIST_ON) +- BC volume menu: + rotate right - volume UP + rotate left - volume DOWN + 1 click held - quick MAX volume + 2 clicks held - quick MIN volume +- OS system menu: + rotate right/left - choose options / adjust + 1 click - select / save + 2 clicks - exit / cancel + +--------- When blade is ON --------- +1 click - blaster deflection +1 click long +1 click held - turn blade OFF + turn blade OFF bypass postoff (pointing up) + then twist - first preset fast (pointing up) + next preset fast + previous preset fast (pointing down) + then clash - lockup + drag (pointing down) + then rotate left - user effect 1 (keep holding POW until executed) + then rotate right - user effect 2 (keep holding POW until executed) + then swing - toggle battle mode +2 clicks - blaster deflection +2 clicks long - force +2 clicks held - lightning block (release to end) +3 clicks - quote + toggle sequential or random quotes (pointing down) +3 clicks long - revert colorchange to config +3 clicks held - color change mode +4 clicks - play / stop track +4 clicks long +4 clicks held - dim (pointing up) + toggle spam blasts (pointing down) + +hit object - clash +twist - turn blade OFF (requires #define BC_TWIST_OFF) +thrust (air) - stab +stab object - melt +push - force push + ====================== 2 BUTTON CONTROLS ======================== | Sorted by ON or OFF state: (what it's like while using saber) | ================================================================= -************* WHILE SABER BLADE IS OFF *************** -Turn blade ON - Short click POW. (or gestures if defined, uses FastOn) - * NOTE * Gesture ignitions using FastOn bypass preon. -Turn ON without preon - Short click POW while pointing up. -Turn blade ON Muted - 4x click and hold POW. -Scroll Presets - Hold POW to toggle this mode on/off. Rotate hilt to cycle next and previous presets. - * NOTES * TWIST_ON not available in this mode. Resets to off after ignition. -Next Preset - Long click and release POW. -Prev Preset - Double click and hold POW, release after a second (click then long click). -Play/Stop Track - Hold AUX + Double click POW. -Volume Menu: - Enter/Exit - Long click AUX. - Volume UP - Rotate Right - - or - - - Long click and release POW while in Volume Menu. (just like next preset) - Volume DOWN - Rotate Left - - or - - - Double click and hold POW, release after a second while in Volume Menu. - (click then long click, just like next preset) - Quick MAX Vol - Short click POW while in Volume Menu. - Quick MIN Vol - Double click POW while in Volume Menu. +* See below for modifications to these controls if BC_DUAL_BLADES is used. + +-------- When blade is OFF ------- +Turn Blade ON - Short Click POW, or use a Gesture Ignition (see Gesture Controls above). + * Gesture ignitions will bypass preon, and optional fastout.wav can be used instead of out.wav. +Turn ON without Preon - Short Click POW (while pointing UP), or use a Gesture Ignition. + * Uses fastout.wav if available. +Turn Blade ON Muted - Hold POW then Twist. + * Muted ignitions will bypass preon. + Optional mute.wav will play before silent ignition and operation. + Saber unmutes when blade shuts off. +Scroll Presets - Hold POW. Beeps will be heard when entering mode, and font.wav will play. + Rotate hilt to cycle through presets. Right = Next, Left = Previous. + Click POW to ignite with chosen preset, or Click and Hold POW again to Exit. + * TWIST_ON not available in this mode. +Next Preset - Click AUX (NOT pointing UP or DOWN). +Prev Preset - Click AUX (while pointing DOWN). +Jump to First Preset - Click AUX (while pointing UP). +Play/Stop Track - Long Click then release POW. +BC Volume Menu: + Enter/Exit - Hold POW + Click AUX. + Enter Menu - Hold POW and Clash. + Volume UP - Rotate Right. + Volume DOWN - Rotate Left. + Quick MAX Vol - Hold POW while in Volume Menu. + Quick MIN Vol - Double Click and Hold POW while in Volume Menu. + Save and Exit - Click POW. + Cancel and Exit - Double Click POW. +BC Blade Length Edit: + Enter Mode - Double Click and Hold POW. + Adjust - Rotate Right or Left + Save and Exit - Click POW. + Cancel and Exit - Double Click POW. +** OS System Menu * To use the OS menu system instead of the default BC Volume and BC Blade Length options, + use #define MENU_SPEC_TEMPLATE Spoken Battery Level - in volts - Triple click POW. - in percentage - Triple click and hold POW. -On-Demand Batt Level - Double click POW. - (requires EFFECT_BATTERY_LEVEL to be in blade style, - and uses battery.wav sound effect.) - -************* WHILE SABER BLADE IS ON **************** -Play/Stop Track - Hold AUX + Double click POW. -Next Preset Fast - Hold AUX + Long click and release POW while pointing up. -Prev Preset Fast - Hold AUX + Double click and hold POW for a second - while pointing up. (click then long click) - * NOTE * Fast switching bypasses preon and font.wav. -Clash - No buttons, just hit the blade against something. - In Battle Mode, Hold any button and Clash to - temporarily override the auto-lockup and do regular Clash. -Stab - Just Thrust forward with a stabbing motion. - Works in Battle Mode. -Blaster Blocks - Click or Double click POW. -Spam Blaster Blocks - 3x click and hold while pointing up. This toggles SPAM BLAST mode ON/OFF, - and makes the button super sensitive for multiple blaster blocks. - * Note * This gets in the way of normal features, - so turn off when you're done spamming. Plays mzoom.wav. -Auto Swing Blast - if #define BC_ENABLE_AUTO_SWING_BLAST is active, - swinging within 1 second of doing button activated - Blaster Block will start this timed mode. - To trigger auto blaster blocks, swing saber - within 1 second of last Swing Blast block. - To exit, stop swinging for 1 second. -Lockup - Hold AUX + Clash. - In Battle Mode, just Clash and stay there, - pull away or press any button to end lockup. -Drag - Hold AUX + Clash while pointing down. -Melt - No button, just stab something, - pull away or press any button to end. -Lightning Block - Double click and hold POW. -Battle Mode - Hold POW + Click AUX to enter and exit. - Power OFF is disabled while in Battle Mode, - YOU MUST EXIT THE MODE WITH THIS COMBO FIRST. -Force Effect - Hold POW + Twist. (while NOT pointing up or down) -Monophonic Force - Hold POW + Twist. (while pointing up) -Color Change Mode - Hold POW + Twist. (while pointing down) - Rotate hilt to cycle through all available colors, or - Click AUX to change if ColorChange<> used in blade style, - Click + hold POW to save color selection and exit. - Triple click POW to cancel and restore original color. - ColorChange explained: - If the style uses ColorChange<>, when you activate color change mode, - there will be up to 12 steps per rotation with a little sound at each step. - If it does not use ColorChange<>, the color wheel will be activated, - which has 32768 steps per rotation. - COLOR_CHANGE_DIRECT makes it so that IF the style uses ColorChange<>, - when you activate color change mode, it will immediately go to the - next color and exit color change mode. If the style does not use - ColorChange<>, it has no effect. -Quote Player - Triple click POW. -Toggle sequential or - random quote play - Hold AUX + Twist. (while pointing down) -Force Push - Push hilt perpendicularly from a stop. -Swap (EffectSequence) - Hold AUX + Twist. (while NOT pointing up) - * Requires EFFECT_USER1 in blade style. -PowerSave Dim Blade - Hold AUX + Twist. (while pointing up) - (To use Power Save requires AlphaL based EffectSequence in style) -Turn off blade - Hold POW and wait until blade is off, - or Twist if using #define BC_TWIST_OFF. -Turn OFF without postoff - Turn OFF while pointing up. - + in percentage - Hold AUX + Click POW. + in volts - Hold AUX + Click POW (while pointing DOWN). + * Will show On-blade display if EFFECT_BATTERY_LEVEL is used in blade style. +On-Demand Batt Level - Hold AUX + Long Click then release POW. + * Requires EFFECT_BATTERY_LEVEL to be in blade style. Uses battery.wav sound effect. + +Quote Player - 3x Click POW. + * Does Force effect if no quote(s) exist. +Toggle Sequential or + Random quotes - 3x Click POW (while pointing DOWN). + +User Effect 5 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +User Effect 6 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +User Effect 7 - Hold AUX then Rotate Left 60 degrees. (keep holding AUX until executed) +User Effect 8 - Hold AUX then Rotate Right 60 degrees. (keep holding AUX until executed) + * Requires EFFECT_USER in blade style. + * Note the same controls when blade is ON are USER 1,2,3,4. + +-------- When blade is ON ------- +Play/Stop Track - Long Click then release POW. +Next Preset Fast - Hold POW and Twist (NOT pointing UP or DOWN). + * Fast switching presets bypasses preon and font.wav. +Previous Preset Fast - Hold POW and Twist (while pointing DOWN). +First Preset Fast - Hold POW and Twist (while pointing UP). + +Battle Mode - Hold POW + Swing to enter and exit Battle Mode. + * Power OFF is disabled while in Battle Mode. YOU MUST EXIT THE MODE WITH THIS COMBO FIRST. + +Clash - No buttons, just hit the blade against something. + In Battle Mode, Hold any button and Clash to + temporarily override the auto-lockup and do regular Clash. +Stab - Thrust forward with a stabbing motion. Works in Battle Mode. +Blaster Deflection - Click or Double click POW or AUX. +Spam Blaster Blocks - 4x Click and Hold POW (while pointing DOWN). Toggles SPAM BLAST mode ON/OFF. + * This makes the POW and AUX buttons super sensitive for multiple blaster blocks, + but gets in the way of normal features, so deactivate when you're done spamming. + Plays mzoom.wav for activating/deactivating this mode. + Exits automatically when blade shuts OFF in case you leave it active. +Auto Swing Blast - Swinging within 1 second of doing a button activated Blaster Block will start this timed mode. + To trigger auto blaster blocks, swing saber within 1 second of last block. + To exit, stop swinging for 1 second. + * Requires #define BC_ENABLE_AUTO_SWING_BLAST. +Force Effect - Double Click and Hold POW, release after a second. (Double Click then Long Click) + * Works with monosfx.wav files, see EFFECT_USER2 in top comments. + +Lockup - Hold any button + Clash. Release button to end. + In Battle Mode: + Just Clash and stay there to Lockup. + Holding a button while Clashing will do regular Clash without Locking up. + Pull away or press a button to end Lockup. +Drag - Hold any button + Clash (while pointing DOWN). Release button to end. +Melt - No button, just stab something. Rotate hilt to modify melting. Pull away or press POW to end. + +Lightning Block - Hold POW + Click AUX. Release button to end. + +Color Change Mode - 3x Click and Hold POW. + Rotate hilt to cycle through all available colors. + Click POW to save color selection and Exit. + Double Click POW to Exit without saving. +Revert ColorChange - 3x Click and Hold POW, release after a second. (Double Click then Long Click) + Reverts ColorChanged blade color back to the uploaded config color. + * This is done outside ColorChange Mode + ColorChange explained: + If RotateColorsX is used in the blade style: + Rotate hilt to cycle through all available colors, + Hold POW to save color selection and exit. + If ColorChange<> is used in the blade style: + There are up to 12 colors per rotation with a ccchange.wav sound at each step. + If also using #define COLOR_CHANGE_DIRECT in the config, + simply entering Color Change Mode will select the next color in the list and exit Color Change Mode. + +Quote Player - 3x Click POW. + * Does Force effect if no quote(s) exist. +Toggle Sequential or + Random quotes - 3x Click POW (while pointing DOWN). +Force Push - Push hilt perpendicularly from a stop. + Plays push.wav if it exists, otherwise force.wav. +PowerSave Dim Blade - Hold AUX then Twist (while pointing UP). + +User Effect 1 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +User Effect 2 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +User Effect 3 - Hold AUX then Rotate Left 60 degrees. (keep holding AUX until executed) +User Effect 4 - Hold AUX then Rotate Right 60 degrees. (keep holding AUX until executed) + * Require EFFECT_USER in blade style. + * Note the same controls when blade is OFF are USER 5,6,7,8. + +Turn OFF blade - Hold POW until off -or - Twist if using #define BC_TWIST_OFF. +Turn OFF bypass postoff - Turn OFF (while pointing UP). + +--------------------------------------- + 2 button summary by clicks +--------------------------------------- +--------- When blade is OFF --------- +twist - turn both blades ON (requires Gesture defines) +stab +swing +1 click POW - turn blade ON + turn blade ON bypass preon (pointing up) +1 click POW long - play / stop track +1 click POW held - scroll presets + then twist - turn blade ON muted (back and forth twist) + then clash + then rotate left - user effect 5 (keep holding POW until executed) + then rotate right - user effect 6 (keep holding POW until executed) +1 click AUX - first preset (pointing up) + next preset + previous preset (pointing down) +1 click AUX held + then rotate left - user effect 7 (keep holding AUX until executed) + then rotate right - user effect 8 (keep holding AUX until executed) +2 clicks POW - turn blade ON muted +2 clicks held - blade length edit, or + OS system menu instead (requires #define MENU_SPEC_TEMPLATE) +3 clicks POW - quote + toggle sequential or random quotes (pointing down) +- BC Volume Menu + rotate right - volume UP + rotate left - volume DOWN + 1 click POW held - quick MAX volume + 1 click AUX held - quick MIN volume +- OS system menu: + rotate right/left - choose options / adjust + 1 click - select / save + 2 clicks - exit / cancel + +OFF COMBOS: +Hold AUX + then click POW - spoken battery level in percentage + spoken battery level in volts (pointing down) + then click POW long - on-demand battery level +Hold POW + then click AUX - enter BC volume menu + +--------- When blade is ON --------- +1 click POW - blaster deflection +1 click POW long - play / stop track +1 click POW held - turn blade OFF + turn blade OFF bypass postoff (pointing up) + then clash - lockup + then twist - first preset fast (pointing up) + next preset fast + previous preset fast (pointing down) + then rotate left - user effect 1 (keep holding POW until executed) + then rotate right - user effect 2 (keep holding POW until executed) + then swing - toggle battle mode +1 click AUX - blaster deflection +1 click AUX held + then clash - lockup + then rotate left - user effect 3 (keep holding AUX until executed) + then rotate right - user effect 4 (keep holding AUX until executed) +2 clicks POW - blaster deflection +2 clicks POW long - force +2 clicks AUX - blaster deflection +3 clicks POW - quote + toggle sequential or random quotes (pointing down) +3 clicks POW long - revert colorchange to config +3 clicks POW held - color change mode +4 clicks POW held - toggle spam blasts (pointing down) + +ON COMBOS: +Hold POW + then click AUX - lightning block (keep holding POW, release to end) + then twist - first preset fast (pointing up) + next preset fast + previous preset fast (pointing down) +Hold AUX + then twist - dim (pointing up) + +hit object - clash +twist - turn blade OFF (requires #define BC_TWIST_OFF) +thrust (air) - stab +stab object - melt +push - force push + + +|==================================================== DUAL BLADES CONTROLS ============================================== +| +| I attempted to keep the controls for this Dual Blade version familiar from the previous version of this prop. +| Much of it was rewritten from ground up, but I have tested extensively and I think the layout makes sense intuitively. +| In the spitit of comradery, I adopted many of the 2 button controls from the saber_fett263_buttons prop file. +| I never had really thought out much for the 2 button portion of this BC prop as I'm mostly a 1 button kind of guy. +| So, not being too concerned, and also knowing how much time and effort was put into crafting the fett263 prop, +| I'm pretty sure it's the right controls to provide for any 2 button users. +| Especially for Dual Blades controls! Makes so much sense. +| +| **** Dual Blades requires defining BC_DUAL_BLADES and optionally +| BC_MAIN_BLADE and BC_SECOND_BLADE. See "Optional defines" above. +| **** Pointing UP/DOWN specific controls, such as sequential quotes or spoken battery level are still based on +| the MAIN blade's orientation, except for turning blade ON/OFF as listed below. +| **** Optional BC_TWIST_ON and BC_TWIST_OFF Gesture defines are strongly recommended, +| practically required to get the right amount of controls. +| +| Changes to 1 button controls for Dual Blade use: +| - No Double Click Blaster Blocks. Use Spam Blast if needed. +| - Lightning Block is now Double Click (was Double Click and Hold POW) +| - There are other differences, but it's easiest just to use the full instructions below for your button setup. +| +| For an example pair of blade styles to use for main and second blades (distributed effects) see the following link: +| Dual_Blade_Example - https://pastebin.com/MFscr2Uf +| +|==================================================== 1 BUTTON DUAL BLADES =============================================== +| +|---------- ON/OFF control ---------- +| Turn Main Blade ON - Click POW or Thrust main blade forward. +| Turn Main Blade ON First Muted - Hold POW then Twist. +| Turn Main Blade OFF - Hold POW when saber is ON. +| +| Turn 2nd Blade ON - Double click POW or Thrust main blade forward. +| Turn 2nd Blade ON First Muted - 4x Click and Hold POW. +| Turn 2nd Blade OFF - Double click and Hold POW when saber is ON. +| +| Thrust ON - Thrust either blade in its pointed direction to turn it ON. +| +| Turn Both Blades ON - Double Click and Hold POW, release after a second. (Double Click then Long Click) +| This turns on both blades if none are ignited. +| or +| Use Gesture ignitions. Plays fastout.wav if available, bypassing font.wav and preon.wav. +| Turn Both Blades ON Muted - Hold POW then Swing. +| * Optional mute.wav will play before silent ignition and operation. Saber unmutes on retraction. +| Turn Both Blades OFF - Twist. Twist is a back-and-forth pair of motions, like revving a motorcycle. +| * Tries to turns off both blades. Therefore even if only one is ON, this will turn it off. +| Turn any Blade ON bypassing preon - Point either blade up while turning blade(s) ON. +| * Uses fastout.wav if available, bypassing font.wav and preon.wav. +| If no blade is pointing up, both blades will ignite normally with preon if one exists. +| Turn any Blade OFF bypassing postoff - Point either blade up while turning blade(s) OFF. +| +|-- Non-Ignition / Retraction related features: +|-------- When both blades are OFF ------- +| Scroll Presets - Click and Hold POW. +| Rotate hilt to cycle through presets. Right = Next, Left = Previous. +| Click POW to ignite with chosen preset, or Click and Hold POW again to Exit. +| * TWIST_ON not available in this mode. +| Next Preset - Long Click and release POW (NOT pointing MAIN Blade UP or DOWN). +| Prev Preset - Long Click and release POW (while pointing MAIN Blade DOWN). +| Jump to First Preset - Long Click and release POW (while pointing MAIN Blade UP). +| Play/Stop Track - 4x Click POW. +| BC Volume Menu: +| Enter Menu - Hold POW and Clash. +| Volume UP - Rotate Right. +| Volume DOWN - Rotate Left. +| Quick MAX Vol - Hold POW while in Volume Menu. +| Quick MIN Vol - Double Click and Hold POW while in Volume Menu. +| Save and Exit - Click POW. +| Cancel and Exit - Double Click POW. +| BC Blade Length Edit: +| Enter Mode - Double Click and Hold POW. +| Adjust - Rotate Right or Left +| Save and Exit - Click POW. +| Cancel and Exit - Double Click POW. +| ** OS System Menu * To use the OS menu system instead of the default BC Volume and BC Blade Length options, +| use #define MENU_SPEC_TEMPLATE +| Spoken Battery Level: +| in percentage - 3x Click and Hold POW. +| in volts - 3x Click and Hold POW (while pointing DOWN). +| * Will show On-blade display if EFFECT_BATTERY_LEVEL is used in blade style. +| On-Demand Battery Level - 3x Click and Hold POW, release after a second. (Double Click then Long Click) +| +| Quote Player - 3x Click POW. +| * Does Force effect if no quote(s) exist. +| Toggle Sequential or Random quotes - 3x Click and Hold POW (while pointing Main Blade DOWN). +| +| User Effect 5 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +| User Effect 6 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +| * Requires EFFECT_USER in blade style. +| * Note the same controls when blade is ON are USER 1 and 2. +| +|-------- When a blade is ON -------- +| Play/Stop Track - 4x Click POW. +| Next Preset Fast - Hold POW and Twist (NOT pointing UP or DOWN). +| * Fast switching presets bypasses preon and font.wav. +| Previous Preset Fast - Hold POW and Twist (while pointing DOWN). +| First Preset Fast - Hold POW and Twist (while pointing UP). +| Clash - No button, just hit the blade against something. +| In Battle Mode, Hold POW and Clash to temporarily override the auto-lockup and do regular Clash. +| Stab - Thrust either blade forward with a stabbing motion. Works in Battle Mode. +| Blaster Deflection - Click POW. +| Spam Blaster Blocks - 4x Click and Hold POW (while pointing DOWN). Toggles SPAM BLAST mode ON/OFF. +| * This makes the POW button super sensitive for multiple blaster blocks, +| but gets in the way of controls for some other features, so deactivate when you're done spamming. +| Plays mzoom.wav for activating/deactivating this mode. +| Exits if either blade shuts OFF in case you leave it active. +| Auto Swing Blast - Swinging within 1 second of doing a button activated Blaster Block will start this timed mode. +| To trigger auto blaster blocks, swing saber within 1 second of last block. +| To exit, stop swinging for 1 second. +| * Requires #define BC_ENABLE_AUTO_SWING_BLAST. +| Lockup - Hold POW then Clash. Release button to end. +| In Battle Mode: +| Just Clash and stay there to Lockup. +| Holding POW while Clashing will do regular Clash without Locking up. +| Pull away or press POW to end Lockup. +| Drag - Hold POW then Clash (while pointing Main Blade DOWN). Release button to end. +| Melt - No button, just stab something with Main Blade. Pull away or press POW to end. +| Lightning Block - Double Click POW. Click to end. +| Battle Mode - Hold POW then Swing to enter and exit Battle Mode. +| Resets to inactive when either blade shuts OFF. +| Force Effect - Double Click and Hold POW, release after a second (Click then Long Click). +| Color Change Mode - 3x Click and Hold POW to enter ColorChange mode. +| Rotate hilt to cycle through all available colors, or +| Click POW to save color selection and exit. +| Double Click POW to exit without saving. +| Revert ColorChange - 3x Click and Hold POW, release after a second. (Double Click then Long Click) +| Reverts ColorChanged blade color back to the uploaded config color. +| * This is done outside ColorChange Mode +| ColorChange explained: +| If RotateColorsX is used in the blade style: +| Rotate hilt to cycle through all available colors, +| Hold POW to save color selection and exit. +| If ColorChange<> is used in the blade style: +| There are up to 12 colors per rotation with a ccchange.wav sound at each step. +| If also using #define COLOR_CHANGE_DIRECT in the config, +| simply entering Color Change Mode will select the next color in the list and exit Color Change Mode. +| +| Quote Player - 3x Click POW. +| * Does Force effect if no quote(s) exist. +| Toggle Sequential or Random quotes - 3x Click and Hold POW (while pointing Main Blade DOWN). +| Force Push - Push hilt perpendicularly from a stop. +| PowerSave Dim Blade - 4x Click and Hold POW (while pointing Main Blade UP) +| +| User Effect 1 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +| User Effect 2 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +| * Require EFFECT_USER in blade style. +| * Note the same controls when blade is OFF are USER 5 and 6. +| +| --------------------------------------- +| 1 button dual blade summary by clicks +| --------------------------------------- +| --------- When both blades are OFF --------- +| +| gestures: (require gesture defines) +| twist or swing - turn both blades ON +| stab / thrust - turn stabbed / thrusted blade ON +| +| buttons: +| 1 click - turn main blade ON +| turn main blade ON first bypass preon (either blade pointing up) +| 1 click long - first preset (main blade pointing up) +| next preset +| previous preset (main blade pointing down) +| 1 click held - enter/exit scroll presets +| then twist - turn main blade ON first muted (back and forth twist) +| then clash - enter BC volume menu +| then rotate left - user effect 5 (keep holding POW until executed) +| then rotate right - user effect 6 (keep holding POW until executed) +| then swing - turn both blades ON muted +| 2 clicks - turn second blade ON +| 2 clicks long - turn both blades ON +| 2 clicks held - blade length edit, or +| OS system menu instead (requires #define MENU_SPEC_TEMPLATE) +| 3 clicks - quote +| toggle sequential or random quotes (pointing down) +| 3 clicks long - on-demand battery level +| 3 clicks held - spoken battery level in percentage +| spoken battery level in volts (pointing down) +| 4 clicks - play / stop track +| 4 clicks held - turn second blade ON first muted +| - BC Volume menu: +| rotate right - volume UP +| rotate left - volume DOWN +| 1 click POW held - quick MAX volume +| 1 click AUX held - quick MIN volume +| - OS system menu: +| rotate right/left - choose options / adjust +| 1 click - select / save +| 2 clicks - exit / cancel +| +| --------- When a blade is ON --------- +| twist - turn both blades OFF (requires Gesture defines) +| 1 click - turn main blade ON (if second blade is ON), otherwise blaster deflection +| 1 click held - turn main blade OFF +| turn main blade OFF bypass postoff (pointing either blade UP) +| then twist - first preset fast (main blade pointing up) +| next preset fast +| previous preset fast (main blade pointing down) +| then clash - lockup +| then rotate left - user effect 1 (keep holding POW until executed) +| then rotate right - user effect 2 (keep holding POW until executed) +| then swing - toggle battle mode +| 2 clicks - lightning block (click to end) +| 2 clicks long - force +| 2 clicks held - turn second blade OFF +| turn second blade OFF bypass postoff (pointing either blade UP) +| 3 clicks - quote +| toggle sequential or random quotes (main blade pointing down) +| 3 clicks long - revert colorchange to config +| 3 clicks held - color change mode +| 4 clicks - play / stop track +| 4 clicks long +| 4 clicks held - dim (pointing up) +| toggle spam blasts (pointing down) +| hit object - clash +| twist - turn all blades OFF (requires #define BC_TWIST_OFF) +| thrust (air) - stab +| stab object - melt +| push - force push +| +| +|==================================================== 2 BUTTON DUAL BLADES============================================ +| +|---------- ON/OFF control ---------- +| Turn Main Blade ON - Click POW. +| Turn Main Blade ON First Muted - Double Click POW. +| Turn Main Blade OFF - Hold POW when saber is ON. +| +| Turn 2nd Blade ON - Click AUX. +| Turn 2nd Blade ON First Muted - Double Click AUX. +| Turn 2nd Blade OFF - Click and Hold AUX when saber is ON. +| +| Thrust ON - Thrust either blade in its pointed direction to turn it ON. +| +| Turn Both Blades ON - Double Click and Hold AUX, release after a second. (Double Click then Long Click) +| This turns on both blades if none are ignited. +| or +| Use Gesture ignitions. Plays fastout.wav if available, bypassing font.wav and preon.wav. +| Turn Both Blades ON Muted - Hold POW then Twist. +| * Optional mute.wav will play before silent ignition and operation. Saber unmutes on retraction. +| Turn Both Blades OFF - Twist. Twist is a back-and-forth pair of motions, like revving a motorcycle. +| * Tries to turns off both blades. Therefore even if only one is ON, this will turn it off. +| Turn any Blade ON bypassing preon - Point either blade up while turning blade(s) ON. +| * Uses fastout.wav if available, bypassing font.wav and preon.wav. +| If no blade is pointing up, both blades will ignite normally with preon if one exists. +| Turn any Blade OFF bypassing postoff - Point either blade up while turning blade(s) OFF. +| +|-- Non-Ignition / Retraction related features: +|-------- When both blades are OFF ------- +| Scroll Presets - Click and Hold POW to toggle this mode ON/OFF. Rotate hilt to cycle next and previous presets. +| * TWIST_ON not available in this mode. Use any ON/OFF control above to ignite with chosen preset. +| Next Preset - Click and Hold AUX, release after a second (NOT pointing UP or DOWN). +| Prev Preset - Click and Hold AUX, release after a second (while pointing DOWN). +| Jump to First Preset - Click and Hold AUX, release after a second (while pointing UP). +| BC Volume Menu: +| Enter Menu - Hold POW then Click AUX. +| Volume UP - Rotate Right. +| Volume DOWN - Rotate Left. +| Quick MAX Vol - Hold POW while in Volume Menu. +| Quick MIN Vol - Double Click and Hold POW while in Volume Menu. +| Save and Exit - Click POW. +| Cancel and Exit - Double Click POW. +| BC Blade Length Edit: +| Enter Mode - Double Click and Hold POW. +| Adjust - Rotate Right or Left +| Save and Exit - Click POW. +| Cancel and Exit - Double Click POW. +| ** OS System Menu * To use the OS menu system instead of the default BC Volume and BC Blade Length options, +| use #define MENU_SPEC_TEMPLATE +| Spoken Battery Level: +| in volts - Hold AUX then Click POW. +| in percentage - Hold AUX then Click POW (while pointing Main Blade DOWN). +| * Will show On-blade display if EFFECT_BATTERY_LEVEL is used in blade style. +| On-Demand Battery Level - Hold AUX, then Long Click POW, release POW after a second. +| * Requires EFFECT_BATTERY_LEVEL to be in blade style. Uses battery.wav sound effect. +| +| Quote Player - 3x Click POW. +| * Does Force effect if no quote(s) exist. +| Toggle Sequential or Random quotes - 3x Click and Hold POW (while pointing Main Blade DOWN). +| +| User Effect 5 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +| User Effect 6 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +| User Effect 7 - Hold AUX then Rotate Left 60 degrees. (keep holding AUX until executed) +| User Effect 8 - Hold AUX then Rotate Right 60 degrees. (keep holding AUX until executed) +| * Requires EFFECT_USER in blade style. +| * Note the same controls when blade is ON are USER 1,2,3,4. +| +|-------- When a blade is ON ------- +| Play/Stop Track - Long Click POW. +| Next Preset Fast - Hold POW and Twist (NOT pointing UP or DOWN). +| * Fast switching presets bypasses preon and font.wav. +| Previous Preset Fast - Hold POW and Twist (while pointing DOWN). +| First Preset Fast - Hold POW and Twist (while pointing UP). +| Clash - No button, just hit the blade against something. +| In Battle Mode, Hold POW and Clash to temporarily override the auto-lockup and do regular Clash. +| Stab - Thrust either blade forward with a stabbing motion. Works in Battle Mode. +| Blaster Deflection - Click or Double click POW or AUX. +| Spam Blaster Blocks - 4x Click and Hold POW (while pointing DOWN). Toggles SPAM BLAST mode ON/OFF. +| * This makes the POW button super sensitive for multiple blaster blocks, +| but gets in the way of controls for some other features, so deactivate when you're done spamming. +| Plays mzoom.wav for activating/deactivating this mode. +| Exits if either blade shuts OFF in case you leave it active. +| Auto Swing Blast - Swinging within 1 second of doing a button activated Blaster Block will start this timed mode. +| To trigger auto blaster blocks, swing saber within 1 second of last block. +| To exit, stop swinging for 1 second. +| * Requires #define BC_ENABLE_AUTO_SWING_BLAST. +| Lockup - Hold AUX then Clash. Release button to end. +| In Battle Mode: +| Just Clash and stay there to Lockup. +| Holding a button while Clashing will do regular Clash without Locking up. +| Pull away or press a button to end Lockup. +| Drag - Hold AUX then Clash (while pointing Main Blade DOWN). Release button to end. +| Melt - No button, just stab something with Main Blade. Pull away or press a button to end. +| Lightning Block - Hold POW, Click and release AUX (keep holding power to block, release POW to end. +| Battle Mode - Hold POW then Swing to enter and exit Battle Mode. +| Resets to inactive when either blade shuts OFF. +| Force Effect - Double Click and Hold POW, release after a second. (Click then Long Click) +| Color Change Mode - 3x Click and Hold POW. +| Rotate hilt to cycle through all available colors. +| Click POW to save color selection and Exit. +| Double Click POW to Exit without saving. +| Revert ColorChange - 3x Click and Hold POW, release after a second. (Double Click then Long Click) +| Reverts ColorChanged blade color back to the uploaded config color. +| * This is done outside ColorChange Mode +| ColorChange explained: +| If RotateColorsX is used in the blade style: +| Rotate hilt to cycle through all available colors, +| Hold POW to save color selection and exit. +| If ColorChange<> is used in the blade style: +| There are up to 12 colors per rotation with a ccchange.wav sound at each step. +| If also using #define COLOR_CHANGE_DIRECT in the config, +| simply entering Color Change Mode will select the next color in the list and exit Color Change Mode. +| +| Quote Player - 3x Click POW. +| * Does Force effect if no quote(s) exist. +| Toggle Sequential or Random quotes - 3x Click and Hold POW (while pointing Main Blade DOWN). +| +| Force Push - Push hilt perpendicularly from a stop. +| PowerSave Dim Blade - Hold AUX then Twist (while pointing Main Blade UP). +| +| User Effect 1 - Hold POW then Rotate Left 60 degrees. (keep holding POW until executed) +| User Effect 2 - Hold POW then Rotate Right 60 degrees. (keep holding POW until executed) +| User Effect 3 - Hold AUX then Rotate Left 60 degrees. (keep holding AUX until executed) +| User Effect 4 - Hold AUX then Rotate Right 60 degrees. (keep holding AUX until executed) +| * Require EFFECT_USER in blade style. +| * Note the same controls when blade is OFF are USER 5,6,7,8. +| +| --------------------------------------- +| 2 button dual blade summary by clicks +| --------------------------------------- +| --------- When both blades are OFF --------- +| +| gestures: (require gesture defines) +| twist or swing - turn both blades ON +| stab / thrust - turn stabbed / thrusted blade ON +| +| buttons: +| 1 click POW - turn main blade ON +| turn main blade ON first bypass preon (either blade pointing up) +| 1 click POW long - play / stop track +| 1 click POW held - scroll presets +| then twist - turn both blades blade ON muted (back and forth twist) +| then rotate left - user effect 5 (keep holding POW until executed) +| then rotate right - user effect 6 (keep holding POW until executed) +| 1 click AUX - turn second blade ON +| turn second blade ON first bypass preon (either blade pointing up) +| 1 click AUX long - first preset (pointing up) +| next preset +| previous preset (pointing down) +| 1 click AUX held +| then rotate left - user effect 7 (keep holding AUX until executed) +| then rotate right - user effect 8 (keep holding AUX until executed) +| 2 clicks POW - turn main blade ON first muted +| 2 clicks held - blade length edit, or +| OS system menu instead (requires #define MENU_SPEC_TEMPLATE) +| 2 clicks AUX - turn second blade ON first muted +| 2 clicks AUX long - turn both blades ON +| - turn both blades ON bypass preon (either blade pointing up) +| 3 clicks POW - quote +| toggle sequential or random quotes (pointing down) +| - BC Volume menu: +| rotate right - volume UP +| rotate left - volume DOWN +| 1 click POW held - quick MAX volume +| 1 click AUX held - quick MIN volume +| - OS system menu: +| rotate right/left - choose options / adjust +| 1 click - select / save +| 2 clicks - exit / cancel +| +| OFF COMBOS: +| Hold AUX +| then click POW - spoken battery level in percentage +| spoken battery level in volts (pointing down) +| then click POW long - on-demand battery level +| Hold POW +| then click AUX - enter BC volume menu +| +| --------- When a blade is ON --------- +| 1 click POW - turn main blade ON (if second blade is ON), otherwise blaster deflection +| 1 click POW long - play / stop track +| 1 click POW held - turn main blade OFF +| turn main blade OFF bypass postoff (pointing either blade UP) +| then twist - first preset fast (main blade pointing up) +| next preset fast +| previous preset fast (main blade pointing down) +| then clash - lockup +| then rotate left - user effect 1 (keep holding POW until executed) +| then rotate right - user effect 2 (keep holding POW until executed) +| then swing - toggle battle mode +| 1 click AUX - turn second blade ON (if main blade is ON), otherwise blaster deflection +| 1 click AUX held - turn second blade OFF +| turn second blade OFF bypass postoff (pointing either blade UP) +| then clash - lockup +| drag (main blade pointing down) +| then rotate left - user effect 3 (keep holding AUX until executed) +| then rotate right - user effect 4 (keep holding AUX until executed) +| 2 clicks POW - blaster deflection +| 2 clicks POW long - force +| 2 clicks AUX - blaster deflection +| 2 clicks AUX long - turn all blades OFF (any remaining blade) +| 3 clicks POW - quote +| toggle sequential or random quotes (main blade pointing down) +| 3 clicks POW long - revert colorchange to config +| 3 clicks POW held - color change mode +| 4 clicks POW held - toggle spam blasts (pointing down) +| +| ON COMBOS: +| Hold POW +| then click AUX - lightning block (keep holding POW, release to end) +| then twist - first preset fast (pointing up) +| next preset fast +| previous preset fast (pointing down) +| Hold AUX +| then twist - dim (pointing up) +| +| hit object - clash +| twist - turn all blades OFF (requires #define BC_TWIST_OFF) +| thrust (air) - stab +| stab object - melt +| push - force push +| +| +|==================================================== 3 BUTTON DUAL BLADES============================================ +| * Note * - The main difference for 3 button control is the independent control for a second blade. All else is the same as 2 button above. +| +|---------- ON/OFF control ---------- +| Turn 2nd Blade ON - Click AUX2 (button 3). +| Turn 2nd Blade ON First Muted - Double Click AUX2. +| Turn 2nd Blade OFF - Click and Hold AUX2. +| Blaster Blocks - Click or Double Click AUX2 +| (Blaster Blocks are single or Double Clicks on all buttons) +| --------------------------------------- +| 3 button dual blade summary by clicks +| --------------------------------------- +| --------- When blade is OFF --------- +| +| 1 click AUX2 - turn second blade ON +| turn second blade ON first bypass preon (either blade pointing up) +| --------- When blade is ON --------- +| +| 1 click AUX2 held - turn second blade OFF +| turn second blade OFF bypass postoff (pointing either blade UP) +| +| If you have any questions feel free to post them on The Crucible forum +| https://crucible.hubbe.net. Tag me @NoSloppy. */ #ifndef PROPS_SABER_BC_BUTTONS_H @@ -308,6 +1065,8 @@ Turn OFF without postoff - Turn OFF while pointing up. #include "prop_base.h" #include "../sound/hybrid_font.h" #include "../sound/sound_library.h" +#include "../modes/select_cancel_mode.h" +#include "../modes/settings_menues.h" #undef PROP_TYPE #define PROP_TYPE SaberBCButtons @@ -348,30 +1107,37 @@ Turn OFF without postoff - Turn OFF while pointing up. #define BUTTON_HELD_LONG_TIMEOUT 2000 #endif -#ifdef BC_SWING_ON -#define SWING_GESTURE +#ifndef NORMAL_TWIST_TIMEOUT +#define NORMAL_TWIST_TIMEOUT 400 #endif -#ifdef BC_STAB_ON -#define STAB_GESTURE +#ifndef BC_MAIN_BLADE +#define BC_MAIN_BLADE 1 #endif -#ifdef BC_TWIST_ON -#define TWIST_GESTURE +#ifndef BC_SECOND_BLADE +#define BC_SECOND_BLADE 2 #endif -#ifdef BC_THRUST_ON -#define THRUST_GESTURE +#ifdef BC_DUAL_BLADES +#if BC_MAIN_BLADE == BC_SECOND_BLADE +#error BC_MAIN_BLADE and BC_SECOND_BLADE cannot be the same +#endif #endif -#if defined(NO_BLADE_NO_GEST_ONOFF) && !defined(BLADE_DETECT_PIN) -#error Using NO_BLADE_NO_GEST_ONOFF requires a BLADE_DETECT_PIN to be defined +#if defined(NO_BLADE_NO_GEST_ONOFF) && !defined(BLADE_DETECT_PIN) && !defined(BLADE_ID_SCAN_MILLIS) +#error Using NO_BLADE_NO_GEST_ONOFF requires either BLADE_DETECT_PIN or BLADE_ID_SCAN_MILLIS to be defined. #endif #if defined(BC_NO_BM) && defined(BC_GESTURE_AUTO_BATTLE_MODE) #error You cannot define both BC_NO_BM and BC_GESTURE_AUTO_BATTLE_MODE #endif +#ifdef BC_DUAL_BLADES + static constexpr BladeSet BC_MAIN_BLADE_SET = BladeSet::fromBlade(BC_MAIN_BLADE); + static constexpr BladeSet BC_SECOND_BLADE_SET = BladeSet::fromBlade(BC_SECOND_BLADE); +#endif + EFFECT(dim); // for EFFECT_POWERSAVE EFFECT(battery); // for EFFECT_BATTERY_LEVEL EFFECT(bmbegin); // for Begin Battle Mode @@ -382,40 +1148,247 @@ EFFECT(volup); // for increse volume EFFECT(voldown); // for decrease volume EFFECT(volmin); // for minimum volume reached EFFECT(volmax); // for maximum volume reached -#ifdef ENABLE_FASTON -#warning The faston.wav sound will be replaced with fastout.wav. If you have a good reason to keep faston.wav as is, please post at https://crucible.hubbe.net/ -EFFECT(faston); // for EFFECT_FAST_ON. Being replaced by fastout.wav, which is already defined in the main OS. -#endif -EFFECT(blstbgn); // for Begin Multi-Blast -EFFECT(blstend); // for End Multi-Blast EFFECT(push); // for Force Push gesture -EFFECT(monosfx); // for Monophonically played sounds (iceblade, seismic charge etc...) -EFFECT(swap); // for standalone triggering EffectSequence<> +EFFECT(tr); // for EFFECT_TRANSITION_SOUND, use with User Effects. EFFECT(mute); // Notification before muted ignition to avoid confusion. +EFFECT(mzoom); // for Spam Blast enter/exit + +template +struct BCVolumeMode : public SPEC::SteppedMode { + const int max_volume_ = VOLUME; + const int min_volume_ = VOLUME * 0.10; + float initial_volume_ = 0.0; + int initial_percentage_ = 0; + int percentage_ = 0; + + int steps_per_revolution() override { + return 12; // adjust for sensitivity + } + + void mode_activate(bool onreturn) override { + PVLOG_NORMAL << "** Enter Volume Menu\n"; + initial_volume_ = dynamic_mixer.get_volume(); + initial_percentage_ = round((initial_volume_ / max_volume_) * 10) * 10; + SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); + mode::getSL()->SayEditVolume(); + announce_volume(); + SPEC::SteppedMode::mode_activate(onreturn); + } + + void announce_volume() { + if (percentage_ <= 10) { + mode::getSL()->SayMinimumVolume(); + } else if (percentage_ >=100) { + mode::getSL()->SayMaximumVolume(); + } else { + mode::getSL()->SayWhole(percentage_); + mode::getSL()->SayPercent(); + } + } + + void mode_deactivate() override { + announce_volume(); + mode::getSL()->SayVolumeMenuEnd(); + SPEC::SteppedMode::mode_deactivate(); + } + + void next() override { + int current_volume_ = dynamic_mixer.get_volume(); + if (current_volume_ < max_volume_) { + current_volume_ += max_volume_ * 0.10; + if (current_volume_ >= max_volume_) { + current_volume_ = max_volume_; + QuickMaxVolume(); + } else { + mode::getSL()->SayVolumeUp(); + } + dynamic_mixer.set_volume(current_volume_); + } + } + + void prev() override { + int current_volume_ = dynamic_mixer.get_volume(); + if (current_volume_ > min_volume_) { + current_volume_ -= max_volume_ * 0.10; + if (current_volume_ <= min_volume_) { + current_volume_ = min_volume_; + QuickMinVolume(); + } else { + mode::getSL()->SayVolumeDown(); + } + dynamic_mixer.set_volume(current_volume_); + } + } + + void QuickMaxVolume() { + dynamic_mixer.set_volume(max_volume_); + PVLOG_NORMAL << "** Maximum Volume\n"; + mode::getSL()->SayMaximumVolume(); + } + + void QuickMinVolume() { + dynamic_mixer.set_volume(min_volume_); + PVLOG_NORMAL << "** Minimum Volume\n"; + mode::getSL()->SayMinimumVolume(); + } + + void update() override { // Overridden to substitute the tick sound + float volume = dynamic_mixer.get_volume(); + percentage_ = round((volume / max_volume_) * 10) * 10; + SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); + } + + void select() override { + PVLOG_NORMAL << "** Saved - Exit Volume Menu\n"; + mode::getSL()->SaySave(); + SPEC::SteppedMode::select(); + } + + void exit() override { + PVLOG_NORMAL << "** Cancelled - Exit Volume Menu\n"; + percentage_ = initial_percentage_; + dynamic_mixer.set_volume(initial_volume_); + mode::getSL()->SayCancel(); + SPEC::SteppedMode::exit(); + } + + bool mode_Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override { + switch (EVENTID(button, event, 0)) { + // Custom button controls for BCVolumeMode + case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, 0): + QuickMaxVolume(); + return true; + + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD_MEDIUM, 0): + case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD_MEDIUM, 0): + QuickMinVolume(); + return true; + } + // Use the select and exit controls from SelectCancelMode + return SPEC::SelectCancelMode::mode_Event2(button, event, modifiers); + } +}; + +#ifdef DYNAMIC_BLADE_LENGTH + +template +struct BCSelectBladeMode : public SPEC::MenuBase { + int current_blade() { return this->pos_ + 1; } + ShowColorSingleBladeTemplate,Pulsing> highlighted_blade_; + bool single_blade_adjusted_ = false; + uint16_t size() override { return NUM_BLADES; } + + void mode_activate(bool onreturn) override { + mode::getSL()->SaySelectBlade(); + SPEC::SteppedMode::mode_activate(onreturn); + highlighted_blade_.Start(current_blade()); + PVLOG_NORMAL << "** Highlighting blade: " << current_blade() << "\n"; + } + + void mode_deactivate() { + highlighted_blade_.Stop(current_blade()); + prop_UpdateStyle(); + } + + void next() override { + highlighted_blade_.Stop(current_blade()); + SPEC::MenuBase::next(); + highlighted_blade_.Start(current_blade()); + PVLOG_NORMAL << "** Highlighting blade: " << current_blade() << "\n"; + } + + void prev() override { + highlighted_blade_.Stop(current_blade()); + SPEC::MenuBase::prev(); + highlighted_blade_.Start(current_blade()); + PVLOG_NORMAL << "** Highlighting blade: " << current_blade() << "\n"; + } + + void say() override { + mode::getSL()->SayBlade(); + mode::getSL()->SayWhole(current_blade()); + } + + void select() override { + // Set the current blade to send and push to ChangeBladeLengthMode + mode::menu_current_blade = current_blade(); + highlighted_blade_.Stop(current_blade()); + pushMode(); + } +}; + +template +// struct BCChangeBladeLengthBlade1 : public SPEC::ChangeBladeLengthMode { +struct BCChangeBladeLengthBlade1 : public mode::ChangeBladeLengthBlade1 { + // virtual int blade() { return mode::menu_current_blade; } + + int steps_per_revolution() override { + return 30; // adjust for sensitivity + } + void select() override { + prop_SetBladeLength(this->blade(), this->getLength()); + prop_SaveState(); + mode::getSL()->SaySave(); + popMode(); + } + void update() override { + hybrid_font.PlayPolyphonic(&SFX_volup); + this->say_time_ = Cyclint(millis()) + (uint32_t)(SaberBase::sound_length * 1000) + 300; + if (!this->say_time_) this->say_time_ += 1; + this->fadeout(SaberBase::sound_length); + } +}; +#endif // DYNAMIC_BLADE_LENGTH + +template +struct BCMenuSpec { + typedef BCVolumeMode BCVolumeMenu; +#ifdef DYNAMIC_BLADE_LENGTH + typedef BCSelectBladeMode BCSelectBladeMenu; + typedef mode::ShowLengthStyle ShowLengthStyle; + typedef BCChangeBladeLengthBlade1 ChangeBladeLengthMode; +#endif // DYNAMIC_BLADE_LENGTH + typedef mode::SelectCancelMode SelectCancelMode; + typedef mode::SteppedMode SteppedMode; + typedef mode::SteppedModeBase SteppedModeBase; + typedef mode::MenuBase MenuBase; + typedef SoundLibraryV2 SoundLibrary; +}; + +#ifndef MENU_SPEC_TEMPLATE + void Setup() { + MKSPEC::SoundLibrary::init(); + } +#endif class DelayTimer { public: - DelayTimer() : triggered_(false), trigger_time_(0), duration_(0) {} + DelayTimer() : triggered_(false), trigger_time_(0), duration_(0) {} - void trigger(uint32_t duration) { - triggered_ = true; - trigger_time_ = millis(); - duration_ = duration; - } + void trigger(uint32_t duration) { + triggered_ = true; + trigger_time_ = millis(); + duration_ = duration; + } - bool timerCheck() { - if (!triggered_) return false; - if (millis() - trigger_time_ > duration_) { - triggered_ = false; // Reset the timer flag - return true; // Timer has elapsed - } - return false; // Timer is still running + void stopTimer() { + PVLOG_DEBUG << "** STOPPING timer.\n"; + triggered_ = false; + } + + bool isTimerExpired() { + if (!triggered_) return false; + if (millis() - trigger_time_ > duration_) { + stopTimer(); + return true; // Timer has elapsed } + return false; // Timer is still running + } private: - bool triggered_; - uint32_t trigger_time_; - uint32_t duration_; + bool triggered_; + uint32_t trigger_time_; + uint32_t duration_; }; // The Saber class implements the basic states and actions @@ -425,6 +1398,34 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { SaberBCButtons() : PropBase() {} const char* name() override { return "SaberBCButtons"; } +#if defined(DYNAMIC_BLADE_LENGTH) && !defined(MENU_SPEC_TEMPLATE) + void EnterBladeLengthMode() { + if (scroll_presets_ || !current_style()) return; + if (current_mode == this) { + sound_library_.SayEditBladeLength(); + +#if NUM_BLADES > 1 + pushMode::BCSelectBladeMenu>(); +#else + pushMode::ChangeBladeLengthMode>(); +#endif + + } + } +#endif // DYNAMIC_BLADE_LENGTH + + void EnterVolumeMenu() { + if (!current_style() || scroll_presets_) return; + if (current_mode == this) { +#ifdef MENU_SPEC_TEMPLATE + sound_library_.SayEditVolume(); + pushMode::ChangeVolumeMode>(); +#else + pushMode::BCVolumeMenu>(); +#endif + } + } + void Loop() override { PropBase::Loop(); DetectMenuTurn(); @@ -452,7 +1453,7 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { SaberBase::SetLockup(SaberBase::LOCKUP_NONE); auto_melt_on_ = false; } - //EVENT_PUSH +//EVENT_PUSH if (fabs(mss.x) < 3.0 && mss.y * mss.y + mss.z * mss.z > 70 && fusor.swing_speed() < 30 && @@ -464,8 +1465,8 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { } else { push_begin_millis_ = millis(); } - } else { - // EVENT_SWING - Swing On gesture control to allow fine tuning of speed needed to ignite + } else { // SaberBase is not ON +// EVENT_SWING - Swing On gesture control to allow fine tuning of speed needed to ignite if (millis() - saber_off_time_ < MOTION_TIMEOUT) { SaberBase::RequestMotion(); if (swinging_ && fusor.swing_speed() < 90) { @@ -477,26 +1478,51 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { } } } - // EVENT_THRUST - if (mss.y * mss.y + mss.z * mss.z < 16.0 && - mss.x > 14 && - fusor.swing_speed() < 150) { - if (millis() - thrust_begin_millis_ > 15) { - Event(BUTTON_NONE, EVENT_THRUST); - thrust_begin_millis_ = millis(); - } - } else { +// EVENT_THRUST + if (mss.y * mss.y + mss.z * mss.z < 20.0 && // slightly more forgiving than 16.0? + (fabs(mss.x) > 14) && // Check for both positive and negative x-motion + fusor.swing_speed() < 150) { + if (millis() - thrust_begin_millis_ > 15) { + Event(BUTTON_NONE, EVENT_THRUST); thrust_begin_millis_ = millis(); } - - // Mute timer check - play optional mute.wav first. - if (mute_timer_.timerCheck()) { - DoMute(); + } else { + thrust_begin_millis_ = millis(); + } +// Timers + // Play optional mute.wav first. + if (mute_all_delay_timer_.isTimerExpired()) { + if (SetMute(true)) { + unmute_on_deactivation_ = true; + muted_ = true; + TurnOnHelper(); + } + } +#ifdef BC_DUAL_BLADES + if (mute_mainBlade_delay_timer_.isTimerExpired()) { + if (SetMute(true)) { + unmute_on_deactivation_ = true; + muted_ = true; + TurnBladeOn(BC_MAIN_BLADE_SET); + PVLOG_NORMAL << "** Main Blade Turned ON Muted\n"; + } + } + if (mute_secondBlade_delay_timer_.isTimerExpired()) { + if (SetMute(true)) { + unmute_on_deactivation_ = true; + muted_ = true; + TurnBladeOn(BC_SECOND_BLADE_SET); + PVLOG_NORMAL << "** Second Blade Turned ON Muted\n"; + } } - // Scroll Presets timer check - avoid beep/wav overlap - if (scroll_presets_timer_.timerCheck() && scroll_presets_) { +#endif + // Delaying playing font.wav so that beep can be heard first. + if (scroll_presets_beep_delay_timer_.isTimerExpired() && scroll_presets_) { SaberBase::DoEffect(EFFECT_NEWFONT, 0); } + if (twist_delay_timer_.isTimerExpired()) { + DoSavedTwist(); + } } // Loop() #ifdef SPEAK_BLADE_ID @@ -504,149 +1530,72 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { if (&SFX_mnum) { sound_library_.SayNumber(id, SAY_WHOLE); } else { - PVLOG_NORMAL << "No mnum.wav number prompts found.\n"; + PVLOG_NORMAL << "** No mnum.wav number prompts found.\n"; beeper.Beep(0.25, 2000.0); } } #endif -// Revert colorchange witout saving (reset to Variation == 0) - void ResetColorChangeMode() { - if (!current_style()) return; - PVLOG_NORMAL << "Reset Color Variation\n"; - SetVariation(0); - PVLOG_NORMAL << "Color change mode done, variation = " << SaberBase::GetCurrentVariation() << "\n"; - SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_NONE); - } - -// Volume Menu - void VolumeMenu() { - if (!mode_volume_) { - current_menu_angle_ = fusor.angle2(); - mode_volume_ = true; - if (SFX_vmbegin) { - sound_library_.SayEnterVolumeMenu(); - } else { - beeper.Beep(0.1, 1000); - beeper.Beep(0.1, 2000); - beeper.Beep(0.1, 3000); - } - PVLOG_NORMAL << "Enter Volume Menu\n"; - SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); - } else { - mode_volume_ = false; - if (SFX_vmend) { - sound_library_.SayVolumeMenuEnd(); - } else { - beeper.Beep(0.1, 3000); - beeper.Beep(0.1, 2000); - beeper.Beep(0.1, 1000); - } - PVLOG_NORMAL << "Exit Volume Menu\n"; - } - } - - void VolumeUp() { - SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); - current_menu_angle_ = fusor.angle2(); - if (dynamic_mixer.get_volume() < VOLUME) { - dynamic_mixer.set_volume(std::min(VOLUME + VOLUME * 0.1, - dynamic_mixer.get_volume() + VOLUME * 0.10)); - if (SFX_volup) { - hybrid_font.PlayPolyphonic(&SFX_volup); - } else { - beeper.Beep(0.10, 2000); - beeper.Beep(0.20, 2500); - } - PVLOG_NORMAL << "Volume Up - Current Volume: "; - PVLOG_NORMAL << dynamic_mixer.get_volume() << "\n"; - } else { - QuickMaxVolume(); + void DetectMenuTurn() { + float a = fusor.angle2() - current_twist_angle_; + if (isPointingUp()) return; + // Keep the rotational angle within range of + // -180 to 180 degrees in terms of radians. + if (a > M_PI) a-= M_PI * 2; + if (a < -M_PI) a+= M_PI * 2; + + if (a < -M_PI / 3) { + CheckSavedTwist(EVENT_TWIST_LEFT); + } else if (a > M_PI / 3) { + CheckSavedTwist(EVENT_TWIST_RIGHT); } } - void VolumeDown() { - SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); - current_menu_angle_ = fusor.angle2(); - if (dynamic_mixer.get_volume() > (0.10 * VOLUME)) { - dynamic_mixer.set_volume(std::max(VOLUME * 0.1, - dynamic_mixer.get_volume() - VOLUME * 0.10)); - if (SFX_voldown) { - hybrid_font.PlayPolyphonic(&SFX_voldown); - } else { - beeper.Beep(0.10, 2000); - beeper.Beep(0.20, 1500); - } - PVLOG_NORMAL << "Volume Down - Current Volume: "; - PVLOG_NORMAL << dynamic_mixer.get_volume() << "\n"; - } else { - QuickMinVolume(); + void CheckSavedTwist(uint32_t event) { + if (!saved_twist_) { + // Save the current twist and start the timer if no twist is saved + saved_twist_ = event; + twist_delay_timer_.trigger(NORMAL_TWIST_TIMEOUT); + PVLOG_DEBUG << "**** Saving twist event: " << (event == EVENT_TWIST_LEFT ? "TWIST LEFT" : "TWIST RIGHT") << ". Starting timer.\n"; } } - void QuickMaxVolume() { - SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); - dynamic_mixer.set_volume(VOLUME); - if (millis() - volume_range_delay_ > 2000) { - if (SFX_volmax) { - hybrid_font.PlayPolyphonic(&SFX_volmax); - } else { - beeper.Beep(0.5, 3000); - } - PVLOG_NORMAL << "Maximum Volume\n"; - volume_range_delay_ = millis(); - } + void DoSavedTwist() { + // Trigger the saved twist after timeout + PVLOG_DEBUG << (saved_twist_ == EVENT_TWIST_LEFT ? "**** Doing SAVED TWIST LEFT\n" : "Doing SAVED TWIST RIGHT\n"); + Event(BUTTON_NONE, (EVENT)saved_twist_); + current_twist_angle_ = fusor.angle2(); + // Clear the twist state and reset strokes to prevent Normal Twist after USER twist + DoGesture(TWIST_CLOSE); + saved_twist_ = 0; } - void QuickMinVolume() { - SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); - dynamic_mixer.set_volume(std::max(VOLUME * 0.1, - dynamic_mixer.get_volume() - VOLUME * 0.90)); - if (millis() - volume_range_delay_ > 2000) { - if (SFX_volmin) { - hybrid_font.PlayPolyphonic(&SFX_volmin); - } else { - beeper.Beep(0.5, 1000); - } - PVLOG_NORMAL << "Minimum Volume\n"; - volume_range_delay_ = millis(); - } + void BeepEnterFeature() { + beeper.Beep(0.05, 2000); + beeper.Silence(0.05); + beeper.Beep(0.05, 2000); + beeper.Silence(0.05); + beeper.Beep(0.10, 3000); } - void DetectMenuTurn() { - float a = fusor.angle2() - current_menu_angle_; - if (is_pointing_up()) return; - if (a > M_PI) a-=M_PI*2; - if (a < -M_PI) a+=M_PI*2; - if (mode_volume_) { - if (a > M_PI / 6) VolumeUp(); - if (a < -M_PI / 6) VolumeDown(); - } else if (scroll_presets_) { - - if (a > M_PI / 6) { - beeper.Beep(0.05, 4000); - current_menu_angle_ = fusor.angle2(); - next_preset(); - } - - if (a < -M_PI / 6) { - beeper.Beep(0.05, 3000); - current_menu_angle_ = fusor.angle2(); - previous_preset(); - } - } + void BeepExitFeature() { + beeper.Beep(0.05, 3000); + beeper.Silence(0.05); + beeper.Beep(0.05, 3000); + beeper.Silence(0.05); + beeper.Beep(0.10, 2000); } - bool is_pointing_up() { + bool isPointingUp() { if (fusor.angle1() > M_PI / 3) return true; -#ifdef DUAL_BLADES +#ifdef BC_DUAL_BLADES if (fusor.angle1() < -M_PI / 3) return true; #endif return false; } void TurnOnHelper() { - if (is_pointing_up()) { + if (isPointingUp() || muted_) { FastOn(); } else { On(); @@ -654,561 +1603,1093 @@ class SaberBCButtons : public PROP_INHERIT_PREFIX PropBase { } void TurnOffHelper() { - if (is_pointing_up()) { + if (SaberBase::Lockup() || battle_mode_) return; + if (isPointingUp()) { Off(OFF_FAST); } else { Off(); } saber_off_time_ = millis(); battle_mode_ = false; + spam_blast_ = false; + muted_ = false; } - void DoMute() { - if (SetMute(true)) { - unmute_on_deactivation_ = true; - TurnOnHelper(); + void MuteAll() { + if (hybrid_font.PlayPolyphonic(&SFX_mute)) { + mute_all_delay_timer_.trigger(SaberBase::sound_length * 1000); + } else { + mute_all_delay_timer_.trigger(0); } } - RefPtr wav_player; + // Previous, next, or first preset, depending on blade angle + void DoChangePreset() { + if (scroll_presets_) return; + if (fusor.angle1() > M_PI / 3) { + // Main Blade pointing UP + first_preset(); + PVLOG_NORMAL << "** Jumped to first preset\n"; + } else if (fusor.angle1() < -M_PI / 3) { + // Main Blade pointing DOWN + previous_preset(); + PVLOG_NORMAL << "** Previous preset\n"; + } else { + // Main Blade NOT pointing UP or DOWN + next_preset(); + PVLOG_NORMAL << "** Next preset\n"; + } + } - bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override { - switch (EVENTID(button, event, modifiers)) { - case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ON): - case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_ON): - return true; + // When ON, use FastOn() for previous, next, or first preset, depending on blade angle + void DoChangePresetFast() { + if (fusor.angle1() > M_PI / 3) { + // Main Blade pointing UP + first_preset_fast(); + PVLOG_NORMAL << "** Jumped to first preset fast\n"; + } else if (fusor.angle1() < -M_PI / 3) { + // Main Blade pointing DOWN + previous_preset_fast(); + PVLOG_NORMAL << "** Previous preset fast\n"; + } else { + next_preset_fast(); + PVLOG_NORMAL << "** Next preset fast\n"; + } + // Trying to maintain mute through Fast preset change + if (muted_) { + unmute_on_deactivation_ = true; + SetMute(true); + } + } -// Gesture Ignition Controls -#ifdef BC_SWING_ON - case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_OFF): - if (mode_volume_) return false; -#ifdef NO_BLADE_NO_GEST_ONOFF - if (!blade_detected_) return false; -#endif - FastOn(); -#ifdef BC_GESTURE_AUTO_BATTLE_MODE - PVLOG_NORMAL << "Entering Battle Mode\n"; - battle_mode_ = true; -#endif - return true; -#endif // BC_SWING_ON + void DoSpokenBatteryLevel() { + if (scroll_presets_) return; + // Avoid weird battery readings when using USB + if (battery_monitor.battery() < 0.5) { + sound_library_.SayTheBatteryLevelIs(); + sound_library_.SayDisabled(); + return; + } + sound_library_.SayTheBatteryLevelIs(); + // pointing DOWN + if (fusor.angle1() < -M_PI / 4) { + sound_library_.SayNumber(battery_monitor.battery(), SAY_DECIMAL); + sound_library_.SayVolts(); + PVLOG_NORMAL << "Battery Voltage: " << battery_monitor.battery() << "\n"; + speaking_ = true; + SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0); + } else { + sound_library_.SayNumber(battery_monitor.battery_percent(), SAY_WHOLE); + sound_library_.SayPercent(); + PVLOG_NORMAL << "Battery Percentage: " < 2000 && - millis() - saber_off_time_ > 1000) { - FastOn(); -#ifdef BC_GESTURE_AUTO_BATTLE_MODE - PVLOG_NORMAL << "Entering Battle Mode\n"; - battle_mode_ = true; -#endif - last_twist_ = millis(); - } - return true; -#endif // BC_TWIST_ON + void OnDemandBatteryLevel() { + if (scroll_presets_) return; + PVLOG_NORMAL << "Battery Voltage: " << battery_monitor.battery() << "\n"; + PVLOG_NORMAL << "Battery Percentage: " < 3000) { - last_twist_ = millis(); - TurnOffHelper(); - } - return true; -#endif // BC_TWIST_OFF + void DoTrackStartOrStop() { + if (scroll_presets_ || spam_blast_) return; + PVLOG_NORMAL << "** Track playback Started or Stopped\n"; + StartOrStopTrack(); + } -#ifdef BC_STAB_ON - case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_OFF): - if (mode_volume_) return false; -#ifdef NO_BLADE_NO_GEST_ONOFF - if (!blade_detected_) return false; -#endif - // Delay Stab On at boot - if (millis() - saber_off_time_ > 1000) { - FastOn(); -#ifdef BC_GESTURE_AUTO_BATTLE_MODE - PVLOG_NORMAL << "Entering Battle Mode\n"; - battle_mode_ = true; -#endif - } - return true; -#endif // BC_STAB_ON + void DoLockup() { + if (!SaberBase::Lockup() && SaberBase::IsOn()) { -#ifdef BC_THRUST_ON - case EVENTID(BUTTON_NONE, EVENT_THRUST, MODE_OFF): - if (mode_volume_) return false; -#ifdef NO_BLADE_NO_GEST_ONOFF - if (!blade_detected_) return false; -#endif - // Delay Thrust On at boot - if (millis() - saber_off_time_ > 1000) { - FastOn(); -#ifdef BC_GESTURE_AUTO_BATTLE_MODE - PVLOG_NORMAL << "Entering Battle Mode\n"; - battle_mode_ = true; -#endif +#ifdef BC_DUAL_BLADES + if (isMainBladeOn() && !isSecondBladeOn()) { + MainBladeLockupOrDrag(); + } else if (!isMainBladeOn() && isSecondBladeOn()) { + SecondBladeLockupOrDrag(); + } else { // Both blades must be ON + DualBladesLockupOrDrag(); } - return true; -#endif // BC_THRUST_ON - -#ifdef BC_FORCE_PUSH - case EVENTID(BUTTON_NONE, EVENT_PUSH, MODE_ON): - // Delay Force Push from previous Push - if (millis() - last_push_ > 2000) { - if (SFX_push) { - hybrid_font.PlayCommon(&SFX_push); +#else + // Single blade scenario (non-dual blades) + if (fusor.angle1() < -M_PI / 4) { + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG); + } else { + if (!battle_mode_) { + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL); } else { - hybrid_font.DoEffect(EFFECT_FORCE, 0); + // Overrides Auto-lockup if holding Button during clash, NOT pointing DOWN + return; } - last_push_ = millis(); } - return true; -#endif // BC_FORCE_PUSH +#endif -// Turn Saber ON - case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): - // No power on without exiting Vol Menu first - if (mode_volume_) { - QuickMaxVolume(); - } else { - TurnOnHelper(); - } - return true; - -// Turn Saber ON Muted -case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD, MODE_OFF): - if (!mode_volume_) { - if (SFX_mute) { - hybrid_font.PlayCommon(&SFX_mute); - mute_timer_.trigger(SaberBase::sound_length * 1000); - } else { - DoMute(); - } + SaberBase::DoBeginLockup(); } - return true; + } -// Toggle Scroll Presets - case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, MODE_OFF): - scroll_presets_ = !scroll_presets_; - if (scroll_presets_) { - PVLOG_NORMAL << "** Enter Scroll Presets\n"; - // beep on enter, then play font.wav - beeper.Beep(0.05, 2000); - beeper.Silence(0.05); - beeper.Beep(0.05, 2000); - beeper.Silence(0.05); - beeper.Beep(0.10, 3000); - scroll_presets_timer_.trigger(350); - } else { - PVLOG_NORMAL << "** Exit Scroll Presets\n"; - // beep on exit - beeper.Beep(0.05, 3000); - beeper.Silence(0.05); - beeper.Beep(0.05, 3000); - beeper.Silence(0.05); - beeper.Beep(0.10, 2000); - // No need to play font.wav again when exiting - } - return true; + void DoLightningBlock() { + if (spam_blast_ || on_pending_) return; + SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK); + SaberBase::DoBeginLockup(); + } -// Next Preset AND Volume Up -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_ON): + void DoBlasterBlock() { + if (spam_blast_ || on_pending_) return; +#ifdef BC_DUAL_BLADES + SaberBase::DoEffect(EFFECT_BLAST, EffectLocation(0, GetBladeAboveHorizon())); #else - // 2 button - case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_ON | BUTTON_AUX): + SaberBase::DoBlast(); #endif - // Bypass NewFont and preon if a blade is pointing up. - if (is_pointing_up()) { - // Don't change preset if in colorchange mode - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) return false; - next_preset_fast(); - } - return true; - case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF): - if (!mode_volume_) { - next_preset(); - } else { - VolumeUp(); - } - return true; + last_blast_millis_ = millis(); + } -// Previous Preset AND Volume Down -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_LONG, MODE_ON): -#else - // 2 button - case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_LONG, MODE_ON | BUTTON_AUX): -#endif - // Bypass NewFont and preon if pointing up. - if (is_pointing_up()) { - //Don't change preset if in colorchange mode - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) return false; - previous_preset_fast(); + void DoQuote() { + if (scroll_presets_ || spam_blast_) return; + if (overlap_timer_initialized_ && !overlap_delay_timer_.isTimerExpired()) return; // prevent overlapping. + overlap_timer_initialized_ = true; + if (SFX_quote) { + sequential_quote_ ? SFX_quote.SelectNext() : SFX_quote.Select(-1); + SaberBase::DoEffect(EFFECT_QUOTE, 0); + } else { + SaberBase::DoForce(); + } + } + + void DoSpamBlast() { + if (!spam_blast_) return; + SaberBase::DoBlast(); + last_blast_millis_ = millis(); + } + + void ToggleSequentialQuote() { + if (scroll_presets_ || spam_blast_) return; + sequential_quote_ = !sequential_quote_; + PVLOG_NORMAL << (sequential_quote_ ? "** Quotes play sequentially\n" : "** Quotes play randomly\n"); + if (SFX_mnum) { + sequential_quote_ ? sound_library_v2.SaySequential() : sound_library_.SayRandom(); + } else { + beeper.Beep(0.5, sequential_quote_ ? 3000 : 1000); + } + } + + void ToggleSpamBlast() { + spam_blast_ = !spam_blast_; + PVLOG_NORMAL << (spam_blast_ ? "** Entering" : "** Exiting") << " Spam Blast Mode\n"; + if (!hybrid_font.PlayPolyphonic(&SFX_mzoom)) { + spam_blast_ ? BeepEnterFeature() : BeepExitFeature(); + } + } + + void ToggleBattleMode() { +#ifndef BC_NO_BM + battle_mode_ = !battle_mode_; + PVLOG_NORMAL << (battle_mode_ ? "** Entering" : "** Exiting") << " Battle Mode\n"; + if (battle_mode_) { + if (!hybrid_font.PlayPolyphonic(&SFX_bmbegin)) { + hybrid_font.DoEffect(EFFECT_FORCE, 0); } - return true; - case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_LONG, MODE_OFF): - if (!mode_volume_) { - previous_preset(); - } else { - VolumeDown(); + } else { + if (!hybrid_font.PlayPolyphonic(&SFX_bmend)) { + beeper.Beep(0.5, 3000); } - return true; + } +#endif + } -// Start or Stop Track -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_FOURTH_SAVED_CLICK_SHORT, MODE_OFF): - case EVENTID(BUTTON_POWER, EVENT_FOURTH_SAVED_CLICK_SHORT, MODE_ON): -#else - // 2 or 3 button - case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON | BUTTON_AUX): - case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF | BUTTON_AUX): + void GestureEnableBattleMode() { +#ifdef BC_GESTURE_AUTO_BATTLE_MODE + PVLOG_NORMAL << "** Entering Battle Mode\n"; + battle_mode_ = true; +#endif + } + + void NoBladeDisableGestures() { +#ifdef NO_BLADE_NO_GEST_ONOFF + if (!blade_present()) return; #endif - if (!mode_volume_) { - StartOrStopTrack(); + } + +#ifdef BC_DUAL_BLADES + + static const BladeSet controlled_blades_; + + bool isMainBladeOn() { return SaberBase::OnBlades()[BC_MAIN_BLADE]; } + bool isSecondBladeOn() { return SaberBase::OnBlades()[BC_SECOND_BLADE]; } + + // Get blade location to apply effect to + BladeSet GetBladeAboveHorizon() { + return fusor.angle1() > 0 ? BC_MAIN_BLADE_SET : BC_SECOND_BLADE_SET; + } + + void TurnBladeOn(BladeSet target_blade) { + // Add in all non-controlled blades, effectively excluding the "other" blade. + target_blade = target_blade | ~controlled_blades_; + if (SaberBase::OnBlades().off()) { + PVLOG_NORMAL << "** No blades are currently ON, turning on the " + << (target_blade[BC_MAIN_BLADE] ? "MAIN" : "SECOND") + << " blade and all others, excluding the " + << (target_blade[BC_MAIN_BLADE] ? "SECOND" : "MAIN") + << " blade\n"; + if (isPointingUp() || muted_) { + FastOn(EffectLocation(0, target_blade)); + } else { + On(EffectLocation(0, target_blade)); } - return true; + } else { + PVLOG_NORMAL << "** Turning on the " + << (target_blade[BC_MAIN_BLADE] ? "MAIN" : "SECOND") + << " blade\n"; + SaberBase::TurnOn(EffectLocation(0, target_blade)); + } + } -// Enter / Exit Volume MENU -#ifndef NO_VOLUME_MENU + void TurnBladeOff(BladeSet target_blade) { + SaberBase::OffType off_type = isPointingUp() ? SaberBase::OFF_FAST : SaberBase::OFF_NORMAL; + PVLOG_DEBUG << "***** Off type: " << (off_type == SaberBase::OFF_FAST ? "OFF_FAST" : "OFF_NORMAL") << "\n"; + + // Check if this is the only blade ON (of MAIN or SECOND blades) + if ((SaberBase::OnBlades() & ~target_blade & controlled_blades_).off()) { + // Other blade is not on, so just do normal Off() with appropriate off_type + PVLOG_NORMAL << "** Turning OFF all blades\n"; + Off(off_type); + muted_ = false; + } else { + // Only Turn OFF this blade, leave the other one ON. + PVLOG_NORMAL << "** Turning OFF only the " + << (target_blade[BC_MAIN_BLADE] ? "MAIN" : "SECOND") + << " blade\n"; + SaberBase::TurnOff(off_type, EffectLocation(0, target_blade)); + } + battle_mode_ = false; + spam_blast_ = false; + saber_off_time_ = millis(); + } + + void TurnMainBladeOnMuted() { + if (hybrid_font.PlayPolyphonic(&SFX_mute)) { + mute_mainBlade_delay_timer_.trigger(SaberBase::sound_length * 1000); + } else { + mute_mainBlade_delay_timer_.trigger(0); + } + } + + void TurnSecondBladeOnMuted() { + if (hybrid_font.PlayPolyphonic(&SFX_mute)) { + mute_secondBlade_delay_timer_.trigger(SaberBase::sound_length * 1000); + } else { + mute_mainBlade_delay_timer_.trigger(0); + } + } + + // Determine the active blade based on x-axis motion - for thrust effects + EffectLocation GetThrustBladeLocation() { + mss = fusor.mss(); + if (mss.x < 14) { + thrusting_blade_ = BC_MAIN_BLADE_SET; + } else if (mss.x > -14) { + thrusting_blade_ = BC_SECOND_BLADE_SET; + } + return EffectLocation(0, thrusting_blade_); + } + + void DoAccel(const Vec3& accel, bool clear) override { + fusor.DoAccel(accel, clear); + accel_loop_counter_.Update(); + Vec3 diff = fusor.clash_mss(); + float v; + if (clear) { + accel_ = accel; + diff = Vec3(0, 0, 0); + v = 0.0; + } else { + #ifndef PROFFIEOS_DONT_USE_GYRO_FOR_CLASH + v = (diff.len() + fusor.gyro_clash_value()) / 2.0; + #else + v = diff.len(); + #endif + } + // If we're spinning the saber or if loud sounds are playing, + // require a stronger acceleration to activate the clash. + if (v > (CLASH_THRESHOLD_G * (1 + + fusor.gyro().len() / 500.0 +#if defined(ENABLE_AUDIO) && defined(AUDIO_CLASH_SUPPRESSION_LEVEL) + + dynamic_mixer.audio_volume() * (AUDIO_CLASH_SUPPRESSION_LEVEL * 1E-10) * dynamic_mixer.get_volume() +#endif + ))) { + if ( (accel_ - fusor.down()).len2() > (accel - fusor.down()).len2() ) { + diff = -diff; + } + + bool stab = (fabs(diff.x) > 2.0 * sqrtf(diff.y * diff.y + diff.z * diff.z)) && + fusor.swing_speed() < 150; + + if (!clash_pending1_) { + forward_stab_ = (stab && (diff.x < 0)); + if (stab) PVLOG_DEBUG << "**** DoAccel >>>>>>>> - forward_stab_ = " << (forward_stab_ = (diff.x < 0)) << "\n"; + clash_pending1_ = true; + pending_clash_is_stab1_ = stab; + pending_clash_strength1_ = v; + } else { + pending_clash_strength1_ = std::max(v, (float)pending_clash_strength1_); + } + } + accel_ = accel; + } + + // Handle lockup or drag for the main blade when only the main blade is on + void MainBladeLockupOrDrag() { + if (fusor.angle1() < -M_PI / 4) { + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG, BC_MAIN_BLADE_SET); + } else if (!battle_mode_) { + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL, BC_MAIN_BLADE_SET); + } + } + + // Handle lockup or drag for the second blade when only the second blade is on + void SecondBladeLockupOrDrag() { + if (fusor.angle1() > M_PI / 4) { + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG, BC_SECOND_BLADE_SET); + } else if (!battle_mode_) { + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL, BC_SECOND_BLADE_SET); + } + } + + // Handle lockup or drag for dual blades when both blades are on + void DualBladesLockupOrDrag() { + if (fusor.angle1() < -M_PI / 4) { + // Main blade is pointing down, and within drag range + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG, BC_MAIN_BLADE_SET); + } else if (fusor.angle1() > M_PI / 4) { + // Main blade is pointing up, and within drag range for the second blade + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG, BC_SECOND_BLADE_SET); + } else { + // Otherwise, apply normal lockup based on which blade is above the horizon + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL, GetBladeAboveHorizon()); + } + } + +#endif // BC_DUAL_BLADES + + RefPtr wav_player; + + bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override { + + if (event == EVENT_TWIST) { + PVLOG_DEBUG << "**** Detected EVENT_TWIST in Event2, stopping timer and resetting saved twist.\n"; + saved_twist_ = 0; + twist_delay_timer_.stopTimer(); + } + + switch (EVENTID(button, event, modifiers)) { + // storage of unused cases + case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_ON): + return true; + +// Volume Up +// Scroll Presets Next + case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_OFF): + if (scroll_presets_) { + beeper.Beep(0.05, 4000); + next_preset(); + } + return true; + +// Volume Down +// Scroll Presets Previous + case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_OFF): + if (scroll_presets_) { + beeper.Beep(0.05, 3000); + previous_preset(); + } + return true; + +/* +Case structure below +1btn + single blade + dual blades + any blades + +2 or 3btn + single blade + dual blades + any blades + +any # of buttons + single blade + dual blades + any blades +*/ + +// ----------------------------------------------- 1 btn section #if NUM_BUTTONS == 1 +#ifndef BC_DUAL_BLADES +// -------------------- 1 btn single blade + +// Turn Blade ON Muted + case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD_MEDIUM, MODE_OFF): + MuteAll(); + return true; + +// Blaster Deflection + case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + DoBlasterBlock(); + return true; + +// Lightning Block + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD_MEDIUM, MODE_ON): + DoLightningBlock(); + return true; + +#else // else BC_DUAL_BLADES is defined + +// -------------------- 1 btn dual blades + +// Turn Main Blade ON First Muted + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF | BUTTON_POWER): + TurnMainBladeOnMuted(); + return true; + +// Turn Second Blade ON First Muted + case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD_MEDIUM, MODE_OFF): + TurnSecondBladeOnMuted(); + return true; + +// Turn Second Blade ON + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): + TurnBladeOn(BC_SECOND_BLADE_SET); + return true; + +// Turn Second Blade OFF + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD_MEDIUM, MODE_ON): + if (isSecondBladeOn()) { + TurnBladeOff(BC_SECOND_BLADE_SET); + } + return true; + +// Turn Dual Blades ON + case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_LONG, MODE_OFF): + TurnOnHelper(); + return true; + +// Turn Dual Blades ON Muted + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_OFF | BUTTON_POWER): + MuteAll(); + return true; + +// Lightning Block (Double Click POW to start, Click POW to Stop) + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + if (!isSecondBladeOn() && !on_pending_) { + TurnBladeOn(BC_SECOND_BLADE_SET); + return true; + } else { + DoLightningBlock(); + } + return true; + +#endif // BC_DUAL_BLADES + +// -------------------- 1 btn any blades + +// Enter / Exit Volume MENU case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_OFF | BUTTON_POWER): -#else - // 2 button + EnterVolumeMenu(); + return true; + +// Spoken Battery Level in percentage +// Spoken Battery Level in volts - pointing DOWN + case EVENTID(BUTTON_POWER, EVENT_THIRD_HELD_MEDIUM, MODE_OFF): + DoSpokenBatteryLevel(); + return true; + +// On-Demand Battery Level + case EVENTID(BUTTON_POWER, EVENT_THIRD_CLICK_LONG, MODE_OFF): + OnDemandBatteryLevel(); + return true; + +// Change Preset +// Pointing UP = First +// Pointing DOWN = Previous +// NOT Pointing UP or DOWN = Next + case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF): + DoChangePreset(); + return true; + +// Start or Stop Track + case EVENTID(BUTTON_POWER, EVENT_FOURTH_SAVED_CLICK_SHORT, MODE_OFF): + case EVENTID(BUTTON_POWER, EVENT_FOURTH_SAVED_CLICK_SHORT, MODE_ON): + DoTrackStartOrStop(); + return true; + +// Spam Blaster Blocks + case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ON): + DoSpamBlast(); + return true; + +// Lockup / Drag + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER): + DoLockup(); + return true; + +// Power Save blade dimming - pointing UP +// Spam Blast toggle - pointing DOWN + case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD_MEDIUM, MODE_ON): + // pointing DOWN + if (fusor.angle1() < -M_PI / 3) { + ToggleSpamBlast(); + return true; + } + // pointing UP + if (isPointingUp()) { + SaberBase::DoEffect(EFFECT_POWERSAVE, 0); + return true; + } + return true; + +#endif // NUM_BUTTONS == 1 + +// ----------------------------------------------- 2 or 3 btn section +#if NUM_BUTTONS == 2 || NUM_BUTTONS == 3 +#ifndef BC_DUAL_BLADES + +// -------------------- 2 btn single blade + +// Change Preset +// Pointing UP = First +// Pointing DOWN = Previous +// NOT Pointing UP or DOWN = Next + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): + DoChangePreset(); + return true; + +// Turn Blade ON Muted + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): + MuteAll(); + return true; + +// Blaster Deflection + case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX2, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX2, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + DoBlasterBlock(); + return true; + +#else // else BC_DUAL_BLADES is defined + +// -------------------- 2 or 3 btn dual blades + +// Change Preset +// Pointing UP = First +// Pointing DOWN = Previous +// NOT Pointing UP or DOWN = Next case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_LONG, MODE_OFF): -#endif - VolumeMenu(); + DoChangePreset(); return true; + +// Turn Second Blade ON +#if NUM_BUTTONS == 3 + case EVENTID(BUTTON_AUX2, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): +#else + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): #endif + TurnBladeOn(BC_SECOND_BLADE_SET); + return true; -// Spoken Battery Level in volts - case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF): - if (!mode_volume_) { - // Avoid weird battery readings when using USB - if (battery_monitor.battery() < 0.5) { - sound_library_.SayTheBatteryLevelIs(); - sound_library_.SayDisabled(); +// Turn Second Blade ON additionally +// Blaster Deflection + case EVENTID(BUTTON_AUX2, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + if (!isSecondBladeOn() && !on_pending_) { + TurnBladeOn(BC_SECOND_BLADE_SET); + return true; + } // fall through + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): +#if NUM_BUTTONS == 2 + if (!isSecondBladeOn() && !on_pending_) { + TurnBladeOn(BC_SECOND_BLADE_SET); return true; + } // fall through +#endif + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX2, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + DoBlasterBlock(); + return true; + +// Turn Main Blade ON First Muted + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): + TurnMainBladeOnMuted(); + return true; + +// Turn Second Blade ON First Muted +#if NUM_BUTTONS == 3 + case EVENTID(BUTTON_AUX2, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): +#else + case EVENTID(BUTTON_AUX, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): +#endif + TurnSecondBladeOnMuted(); + return true; + +// Turn Dual Blades ON +#if NUM_BUTTONS == 3 + // Use AUX (middle ideally) for dual blade ON/OFF when 3btns + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): +#else + case EVENTID(BUTTON_AUX, EVENT_SECOND_CLICK_LONG, MODE_OFF): +#endif + TurnOnHelper(); + return true; + +// Turn Dual Blades ON Muted + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF | BUTTON_POWER): + MuteAll(); + return true; + +// Turn Second Blade OFF +#if NUM_BUTTONS == 3 + case EVENTID(BUTTON_AUX2, EVENT_FIRST_HELD_MEDIUM, MODE_ON): +#else + case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD_MEDIUM, MODE_ON): +#endif + if (isSecondBladeOn()) { + TurnBladeOff(BC_SECOND_BLADE_SET); } - sound_library_.SayTheBatteryLevelIs(); - sound_library_.SayNumber(battery_monitor.battery(), SAY_DECIMAL); - sound_library_.SayVolts(); - PVLOG_NORMAL << "Battery Voltage: " << battery_monitor.battery() << "\n"; - speaking_ = true; - SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0); - } - return true; + return true; + +// Turn Dual Blades OFF (will also turn OFF any single blade that's ON) +#if NUM_BUTTONS == 3 + case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD_MEDIUM, MODE_ON): +#else + case EVENTID(BUTTON_AUX, EVENT_SECOND_CLICK_LONG, MODE_ON): +#endif + TurnOffHelper(); + return true; + +#endif // BC_DUAL_BLADES + +// -------------------- 2 or 3 btn any blades + +// Enter / Exit Volume MENU + case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_POWER): + EnterVolumeMenu(); + return true; // Spoken Battery Level in percentage - case EVENTID(BUTTON_POWER, EVENT_THIRD_HELD, MODE_OFF): - if (!mode_volume_) { - if (battery_monitor.battery() < 0.5) { - sound_library_.SayTheBatteryLevelIs(); - sound_library_.SayDisabled(); - return true; +// Spoken Battery Level in volts - pointing DOWN + case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_AUX): + DoSpokenBatteryLevel(); + return true; + +// On-Demand Battery Level hold AUX, long click POW...? + case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF | BUTTON_AUX): + OnDemandBatteryLevel(); + return true; + +// Start or Stop Track + case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF): + case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_ON): + DoTrackStartOrStop(); + return true; + +// Spam Blaster Blocks + case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ON): + DoSpamBlast(); + return true; + +// Lockup / Drag + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER): + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_AUX): + DoLockup(); + return true; + +// Lightning Block + case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON | BUTTON_POWER): + DoLightningBlock(); + return true; + +// Power Save blade dimming - pointing UP + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_AUX): + if (isPointingUp()) { + SaberBase::DoEffect(EFFECT_POWERSAVE, 0); } - sound_library_.SayTheBatteryLevelIs(); - sound_library_.SayNumber(battery_monitor.battery_percent(), SAY_WHOLE); - sound_library_.SayPercent(); - PVLOG_NORMAL << "Battery Percentage: " < 500) { + FastOn(); + GestureEnableBattleMode(); } - } - return true; + return true; +#endif // BC_SWING_ON -// Lightning Block - case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD_MEDIUM, MODE_ON): - //Don't lightning block if in colorchange mode - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) return false; - if (SaberBase::IsOn()) { // prevents triggering during preon - SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK); - SaberBase::DoBeginLockup(); - } - return true; +#ifdef BC_TWIST_ON + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF): + if (scroll_presets_) return false; + NoBladeDisableGestures(); + // Delay twist events to prevent false trigger from over twisting + if (millis() - last_twist_millis_ > 300 && + millis() - saber_off_time_ > 500) { + FastOn(); + GestureEnableBattleMode(); + last_twist_millis_ = millis(); + } + return true; +#endif // BC_TWIST_ON -// Spam Blast toggle - pointing up -// Battle Mode toggle - NOT pointing up -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_THIRD_HELD, MODE_ON): +#ifdef BC_TWIST_OFF + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON): + NoBladeDisableGestures(); + // Delay twist events to prevent false trigger from over twisting + if (millis() - last_twist_millis_ > 500) { + last_twist_millis_ = millis(); + TurnOffHelper(); + } + return true; +#endif // BC_TWIST_OFF + +#ifdef BC_THRUST_ON + case EVENTID(BUTTON_NONE, EVENT_THRUST, MODE_OFF): + NoBladeDisableGestures(); + // Delay Thrust On at boot + if (millis() - saber_off_time_ > 1000) { + GestureEnableBattleMode(); +#ifdef BC_DUAL_BLADES + GetThrustBladeLocation(); + thrusting_blade_ = thrusting_blade_ | ~controlled_blades_; + PVLOG_NORMAL << "** " << (thrusting_blade_[BC_MAIN_BLADE] ? "MAIN" : "SECOND") << " Blade THRUST ON\n"; + FastOn(EffectLocation(0, thrusting_blade_)); #else - // 2 button - case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON | BUTTON_POWER): + FastOn(); #endif - if (SaberBase::IsOn()) { // prevents triggering during preon - if (fusor.angle1() > M_PI / 3) { - sound_library_.SayZoomingIn(); - spam_blast_ = !spam_blast_; - return true; } -#ifndef BC_NO_BM - if (!battle_mode_) { - PVLOG_NORMAL << "Entering Battle Mode\n"; - battle_mode_ = true; - if (SFX_bmbegin) { - hybrid_font.PlayCommon(&SFX_bmbegin); - } else { + return true; +#endif // BC_THRUST_ON + +#ifdef BC_FORCE_PUSH + case EVENTID(BUTTON_NONE, EVENT_PUSH, MODE_ON): + // Delay Force Push from previous Push + if (millis() - last_push_millis_ > 2000) { + if (!hybrid_font.PlayPolyphonic(&SFX_push)) { hybrid_font.DoEffect(EFFECT_FORCE, 0); } + last_push_millis_ = millis(); + } + return true; +#endif // BC_FORCE_PUSH + +// Enter OS System Menu +// Enter BC Blade Length Mode + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD_MEDIUM, MODE_OFF): +#ifdef MENU_SPEC_TEMPLATE + PVLOG_NORMAL << "** Entering ProffieOS Menu System\n"; + EnterMenu(); +#elif defined(DYNAMIC_BLADE_LENGTH) + EnterBladeLengthMode(); +#endif + return true; + +// Toggle Scroll Presets + case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, MODE_OFF): + scroll_presets_ = !scroll_presets_; + if (scroll_presets_) { + PVLOG_NORMAL << "** Enter Scroll Presets\n"; + BeepEnterFeature(); + scroll_presets_beep_delay_timer_.trigger(350); } else { - PVLOG_NORMAL << "Exiting Battle Mode\n"; - battle_mode_ = false; - if (SFX_bmend) { - hybrid_font.PlayCommon(&SFX_bmend); - } else { - beeper.Beep(0.5, 3000); - } + PVLOG_NORMAL << "** Exit Scroll Presets\n"; + BeepExitFeature(); + // No need to play font.wav again when exiting + } + return true; + +// Auto Swing Blast + // Do swings within 1sec of blaster block to do swing blocks. + // No swing for 1sec to auto exit. +#ifdef BC_ENABLE_AUTO_SWING_BLAST + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON): + if (millis() - last_blast_millis_ < 1000) { + SaberBase::DoBlast(); + last_blast_millis_ = millis(); + PVLOG_NORMAL << "** Auto Swing Blast mode\n"; } + break; #endif - } - return true; + +// Force + case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_LONG, MODE_ON): + if ((spam_blast_ || on_pending_) || (overlap_timer_initialized_ && !overlap_delay_timer_.isTimerExpired())) return false; + SaberBase::DoForce(); + overlap_timer_initialized_ = true; + return true; + +// Toggle Battle Mode + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON | BUTTON_POWER): + ToggleBattleMode(); + return true; // Auto Lockup Mode - case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON): - if (!battle_mode_ || swinging_) return false; - clash_impact_millis_ = millis(); - SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL); - auto_lockup_on_ = true; - SaberBase::DoBeginLockup(); - return true; - -// Color Change - pointing down -// MonoForce - pointing up -// Force - NOT pointing up or down - case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_POWER): - if (SaberBase::IsOn()) { // prevents triggering during preon - // pointing down + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON): + if (!battle_mode_ || swinging_) return false; + clash_impact_millis_ = millis(); + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL); + auto_lockup_on_ = true; + SaberBase::DoBeginLockup(); + return true; + +// First Preset Fast - pointing UP +// Next Preset Fast - NOT pointing UP or down +// Previous Preset Fast - pointing DOWN + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_POWER): + DoChangePresetFast(); + return true; + +// Enter ColorChange Mode #ifndef DISABLE_COLOR_CHANGE - if (fusor.angle1() < - M_PI / 4) { + case EVENTID(BUTTON_POWER, EVENT_THIRD_HELD_MEDIUM, MODE_ON): + if (spam_blast_) return false; ToggleColorChangeMode(); - return true; - } + return true; #endif - // pointing up - if (fusor.angle1() > M_PI / 3) { - SaberBase::DoEffect(EFFECT_USER2, 0); - } else { - // NOT pointing up OR down - SaberBase::DoForce(); - } - } - return true; - -// Quote -// Revert Color Change without saving (reset to Variation == 0) - case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_ON): - if (SaberBase::IsOn()) { // prevents triggering during preon - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { - ResetColorChangeMode(); - return true; + +// Revert ColorChange to uploaded config color (reset Variation) + case EVENTID(BUTTON_POWER, EVENT_THIRD_CLICK_LONG, MODE_ON): + if (spam_blast_) return false; + PVLOG_NORMAL << "** Reverted Color Variation to uploaded config color. Variation = " << SaberBase::GetCurrentVariation() << "\n"; + SaberBase::SetVariation(0); + if (SFX_mnum) { + sound_library_v2.SayResetToDefaultColor(); } else { - if (SFX_quote) { - if (sequential_quote_) { - SFX_quote.SelectNext(); - } else { - SFX_quote.Select(-1); - } - SaberBase::DoEffect(EFFECT_QUOTE, 0); - // hybrid_font.PlayCommon(&SFX_quote); - } else { - SaberBase::DoForce(); - } + beeper.Beep(0.20, 2000.0); + beeper.Beep(0.20, 1414.2); + beeper.Beep(0.20, 1000.0); } - } - return true; + return true; -// Power Save blade dimming - pointing up -// Swap effect - NOT pointing up or down -// Toggle sequential quote play - pointing down -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD_MEDIUM, MODE_ON): -#else - case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_AUX): -#endif - if (SaberBase::IsOn()) { // prevents triggering during preon - // pointing up - if (fusor.angle1() > M_PI / 3) { - SaberBase::DoEffect(EFFECT_POWERSAVE, 0); - return true; - } else if (fusor.angle1() < - M_PI / 4) { - // pointing down - sequential_quote_ = !sequential_quote_; - sound_library_.SayRandom(); - if (sequential_quote_) { - sound_library_.SayDisabled(); - } else { - sound_library_.SayEnabled(); - } - return true; +// Quote - NOT pointing DOWN +// Toggle Sequential/Random quotes - pointing DOWN + case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF): + if (fusor.angle1() < -M_PI / 4) { + // pointing DOWN + ToggleSequentialQuote(); } else { - // NOT pointing up or down - SaberBase::DoEffect(EFFECT_USER1, 0); + DoQuote(); } - } - return true; + return true; -// Turn Blade OFF - case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, MODE_ON): - if (!SaberBase::Lockup()) { -#ifndef DISABLE_COLOR_CHANGE - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { - // Just exit color change mode. - // Don't turn saber off. - ToggleColorChangeMode(); - return true; - } -#endif - if (!battle_mode_) { - TurnOffHelper(); - } - } - return true; +// User Effects a.k.a. "Special Abilities" ©Fett263 + case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_ON | BUTTON_POWER): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER1 **\n"; + SaberBase::DoEffect(EFFECT_USER1, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_ON | BUTTON_POWER): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER2 **\n"; + SaberBase::DoEffect(EFFECT_USER2, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_ON | BUTTON_AUX): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER3 **\n"; + SaberBase::DoEffect(EFFECT_USER3, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_ON | BUTTON_AUX): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER4 **\n"; + SaberBase::DoEffect(EFFECT_USER4, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_OFF | BUTTON_POWER): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER5 **\n"; + SaberBase::DoEffect(EFFECT_USER5, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_OFF | BUTTON_POWER): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER6 **\n"; + SaberBase::DoEffect(EFFECT_USER6, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_OFF | BUTTON_AUX): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER7 **\n"; + SaberBase::DoEffect(EFFECT_USER7, 0); + return true; + + case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_OFF | BUTTON_AUX): + if (scroll_presets_) return false; + PVLOG_NORMAL << "** EFFECT_USER8 **\n"; + SaberBase::DoEffect(EFFECT_USER8, 0); + return true; // Blade Detect #ifdef BLADE_DETECT_PIN - case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_ON): - case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_OFF): - // Might need to do something cleaner, but let's try this for now. - blade_detected_ = true; - FindBladeAgain(); - SaberBase::DoBladeDetect(true); - return true; - - case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_ON): - case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_OFF): - // Might need to do something cleaner, but let's try this for now. - blade_detected_ = false; - FindBladeAgain(); - SaberBase::DoBladeDetect(false); - return true; + case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_ON): + case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_OFF): + blade_detected_ = true; + FindBladeAgain(); + SaberBase::DoBladeDetect(true); + return true; + + case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_ON): + case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_OFF): + blade_detected_ = false; + FindBladeAgain(); + SaberBase::DoBladeDetect(false); + return true; #endif // Events that need to be handled regardless of what other buttons are pressed. - case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_OFF): - case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_OFF): - case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_OFF): - SaberBase::RequestMotion(); - return true; - - case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): - case EVENTID(BUTTON_AUX, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): - case EVENTID(BUTTON_AUX2, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): - if (SaberBase::Lockup()) { - SaberBase::DoEndLockup(); - SaberBase::SetLockup(SaberBase::LOCKUP_NONE); + case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_OFF): + case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_OFF): + case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_OFF): + saber_off_time_ = millis(); + SaberBase::RequestMotion(); return true; - } - } + + case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): + case EVENTID(BUTTON_AUX2, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON): + if (SaberBase::Lockup()) { + SaberBase::DoEndLockup(); + SaberBase::SetLockup(SaberBase::LOCKUP_NONE); + return true; + } + } // switch return false; - } + } // Event2 void SB_Effect(EffectType effect, EffectLocation location) override { switch (effect) { - case EFFECT_QUOTE: hybrid_font.PlayCommon(&SFX_quote); return; - // Dim case EFFECT_POWERSAVE: - if (SFX_dim) { - hybrid_font.PlayCommon(&SFX_dim); - } else { + if (!hybrid_font.PlayPolyphonic(&SFX_dim)) { beeper.Beep(0.1, 1300); beeper.Beep(0.1, 900); beeper.Beep(0.1, 600); @@ -1221,9 +2702,7 @@ case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD, MODE_OFF): speaking_ = false; return; } - if (SFX_battery) { - hybrid_font.PlayCommon(&SFX_battery); - } else { + if (!hybrid_font.PlayPolyphonic(&SFX_battery)) { beeper.Beep(1.0, 475); beeper.Beep(0.5, 693); beeper.Beep(0.16, 625); @@ -1234,64 +2713,86 @@ case EVENTID(BUTTON_POWER, EVENT_FOURTH_HELD, MODE_OFF): PVLOG_VERBOSE << "The Force will be with you...always.\n"; } return; - // Gesture on, bybass preon - case EFFECT_FAST_ON: -#ifdef ENABLE_FASTON - if (SFX_faston) hybrid_font.PlayCommon(&SFX_faston); -#endif + case EFFECT_IGNITION: scroll_presets_ = false; + saber_on_time_ = millis(); return; - case EFFECT_FAST_OFF: - if (SaberBase::IsOn()) { - Off(OFF_FAST); - saber_off_time_ = millis(); - } + case EFFECT_TRANSITION_SOUND: + if (SFX_tr) hybrid_font.PlayPolyphonic(&SFX_tr); return; - // Swap - case EFFECT_USER1: - if (SFX_swap) { - hybrid_font.PlayCommon(&SFX_swap); - } else { - hybrid_font.PlayCommon(&SFX_ccchange); - } + + } + } // SB_Effect + + void SB_Effect2(EffectType effect, EffectLocation location) override { + switch (effect) { + case EFFECT_QUOTE: + case EFFECT_FORCE: + overlap_delay_timer_.trigger(SaberBase::sound_length * 1000); return; - // Monoforce - case EFFECT_USER2: - if (SFX_monosfx) { - hybrid_font.PlayMonophonic(&SFX_monosfx , &SFX_hum); + } + } // SB_Effect2 + + void Clash2(bool stab, float strength) override { + SaberBase::SetClashStrength(strength); + if (Event(BUTTON_NONE, stab ? EVENT_STAB : EVENT_CLASH)) { + IgnoreClash(400); + } else { + IgnoreClash(100); + // Saber must be on and not in lockup or colorchange mode for stab/clash/melt. + if (SaberBase::IsOn() && !SaberBase::Lockup() + && SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE) { + if (stab) { + SaberBase::DoStab(); } else { - SaberBase::DoEffect(EFFECT_FORCE, 0); +#ifdef BC_DUAL_BLADES + SaberBase::DoEffect(EFFECT_CLASH, EffectLocation(0, GetBladeAboveHorizon())); +#else + SaberBase::DoClash(); +#endif } - return; + } } } private: - DelayTimer mute_timer_; - DelayTimer scroll_presets_timer_; - - float current_menu_angle_ = 0.0; - bool mode_volume_ = false; + DelayTimer mute_all_delay_timer_; + DelayTimer mute_mainBlade_delay_timer_; + DelayTimer mute_secondBlade_delay_timer_; + DelayTimer scroll_presets_beep_delay_timer_; + DelayTimer overlap_delay_timer_; + DelayTimer twist_delay_timer_; + + float current_twist_angle_ = 0.0; + uint32_t saved_twist_ = 0; + bool battle_mode_ = false; bool auto_lockup_on_ = false; bool auto_melt_on_ = false; - bool battle_mode_ = false; - bool max_vol_reached_ = false; - bool min_vol_reached_ = false; bool sequential_quote_ = false; bool spam_blast_ = false; - // Avoid overlap of battery.wav when doing Spoken Battery Level - bool speaking_ = false; + bool speaking_ = false; // Don't play battery.wav when doing Spoken Battery Level bool scroll_presets_ = false; + bool muted_ = false; + bool forward_stab_ = false; + bool overlap_timer_initialized_ = false; uint32_t thrust_begin_millis_ = millis(); uint32_t push_begin_millis_ = millis(); uint32_t clash_impact_millis_ = millis(); - uint32_t last_twist_ = millis(); - uint32_t last_push_ = millis(); - uint32_t last_blast_ = millis(); + uint32_t last_thrust_millis_ = millis(); + uint32_t last_twist_millis_ = millis(); + uint32_t last_push_millis_ = millis(); + uint32_t last_blast_millis_ = millis(); uint32_t saber_off_time_ = millis(); - uint32_t volume_range_delay_ = millis(); + uint32_t saber_on_time_ = millis(); + Vec3 mss; + EffectLocation location; + BladeSet thrusting_blade_; }; +#ifdef BC_DUAL_BLADES + const BladeSet SaberBCButtons::controlled_blades_ = BC_MAIN_BLADE_SET | BC_SECOND_BLADE_SET; +#endif + #endif