Advertisement
Rob22976

saber_sabertrio_buttons.h

Feb 22nd, 2023
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 90.87 KB | None | 0 0
  1. /*
  2. ProffieOS: Control software for lightsabers and other props.
  3. http://fredrik.hubbe.net/lightsaber/teensy_saber.html
  4. Copyright (c) 2016-2019 Fredrik Hubinette
  5.  
  6. Copyright (c) 2020-2021 Fernando da Rosa
  7.  
  8. Voice Prompts and sounds required for certain features and should be included in /common folder or /font folder on SD card.
  9.  
  10. Track Player requires track files to be located in /font/tracks for font specific tracks or /common/tracks for universal (all presets) or a combination of the two.
  11. */
  12.  
  13. // Sabertrio Proffie Preconfiguration
  14. // DEFAULT 2 SWITCH CONTROLS (ACTIVATION and AUXILIARY)
  15. //
  16. // Ignite Blade                 - Press & release ACTIVATION SWITCH while blade is OFF.
  17. //                              - Twist while blade is OFF.
  18. //                                NOTE: If no blade or blade plug is detected, the 'no blade' sound effect will play.
  19. // Mute Mode                    - Press & hold AUXILIARY SWITCH + press & release ACTIVATION SWITCH while blade is OFF.
  20. // Retract Blade                - Press & hold the ACTIVATION SWITCH for while blade is ON.
  21. //                              - Twist while blade is ON.
  22. // Blaster Block Effect         - Press & release AUXILIARY SWITCH while blade is ON.
  23. // Flash on Clash               - Strike blade while blade is ON.
  24. // Blade Lockup Effect          - Press & hold AUXILIARY SWITCH and strike blade while blade is ON.
  25. // Blade Tip Drag Effect        - Press & hold AUXILIARY SWITCH and strike blade downwards while blade is ON.
  26. // Wall Melt Effect             - Press & hold AUXILIARY SWITCH and strike blade forward while blade is ON.
  27. // Force Effect                 - Press & hold AUXILIARY SWITCH while blade is ON.
  28. // Force Lightning Parry Effect - Double press & hold ACTIVATION SWITCH when blade is on.
  29. // Kyber Dial                   - Press & hold AUXILIARY SWITCH + press & release ACTIVATION SWITCH while blade is ON.
  30. //                              - Press & hold ACTIVATION SWITCH or AUXILIARY SWITCH while in Blade Length Adjust Mode.
  31. //                                THEN: Twist rotate saber to change blade color.
  32. //                                NOTE: Press & release AUXILIARY SWITCH to select color and exit Kyber Dial.
  33. //                                NOTE: Press & release ACTIVATION SWITCH to revert to default color and exit Kyber Dial.
  34. // Blade Length Adjustment      - Press & Hold ACTIVATION SWITCH or AUXILIARY SWITCH while in Kyber Dial.
  35. //                                THEN: Twist rotate to change blade length.
  36. //                                NOTE: Press & release AUXILIARY SWITCH to save blade length and exit Blade Length Adjustment.
  37. //                                NOTE: Press & release ACTIVATION SWITCH to revert to last saved blade length and exit Blade Length Adjustment.
  38. // Sound Bank Selection         - Press & hold AUXILIARY SWITCH while blade is OFF.
  39. //                                NOTE: Press & release AUXILIARY SWITCH while in Sound Bank Selection to cycle to next sound font.
  40. //                                NOTE: Press & release ACTIVATION SWITCH while in Sound Bank Selection to cycle to previous sound font.
  41. //                                NOTE: Press & Hold ACTIVATION or AUXIALRY SWITCH while in Sound Bank Selection to select sound font.
  42. // Volume Menu                  - Press & Hold AUXILIARY SWITCH & ACTIVATION SWITCH while blade is OFF.
  43. //                                NOTE: Press & release AUXILIARY SWITCH while in Volume Menu to increase volume.
  44. //                                NOTE: Press & release ACTIVATION SWITCH while in Volume Menu to decrease volume.
  45. //                                NOTE: Press & hold ACTIVATION SWITCH or AUXILIARY SWITCH while in Volume Menu to exit.
  46. // Battery Level Meter          - Press & release AUXILIARY SWITCH while blade is OFF.
  47. // Gesture Toggle               - Press & hold ACTIVATION SWITCH + Twist Rotate the saber while blade is OFF.
  48. // Track Player                 - Press & hold ACTIVATION SWITCH + press & release AUXILIARY SWITCH.
  49.  
  50. #ifndef PROPS_SABER_SABERTRIO_BUTTONS_H
  51. #define PROPS_SABER_SABERTRIO_BUTTONS_H
  52.  
  53. #ifndef MOTION_TIMEOUT
  54. #define MOTION_TIMEOUT 60 * 15 * 1000
  55. #endif
  56.  
  57. #ifndef FETT263_SWING_ON_SPEED
  58. #define FETT263_SWING_ON_SPEED 250
  59. #endif
  60.  
  61. #ifndef FETT263_LOCKUP_DELAY
  62. #define FETT263_LOCKUP_DELAY 200
  63. #endif
  64.  
  65. #ifndef FETT263_BM_CLASH_DETECT
  66. #define FETT263_BM_CLASH_DETECT 0
  67. #endif
  68.  
  69. #ifndef FETT263_FORCE_PUSH_LENGTH
  70. #define FETT263_FORCE_PUSH_LENGTH 5
  71. #endif
  72.  
  73. #if NUM_BUTTONS < 1
  74. #error /props/s3button_config.h requires 1 or 2 Buttons for operation
  75. #endif
  76.  
  77. #if defined(FETT263_CLASH_STRENGTH_SOUND) && !defined(FETT263_MAX_CLASH)
  78. #define FETT263_MAX_CLASH 16
  79. #endif
  80.  
  81. #ifdef FETT263_TWIST_ON
  82. #define TWIST_GESTURE
  83. #endif
  84.  
  85. #if defined(FETT263_THRUST_ON_NO_BM) && !defined(THRUST_GESTURE)
  86. #error FETT263_THRUST_ON_NO_BM requires either FETT263_THRUST_ON or FETT263_THRUST_ON_PREON
  87. #endif
  88.  
  89. #ifdef FETT263_FORCE_PUSH_ALWAYS_ON
  90. #define FORCE_PUSH_CONDITION true
  91. #define FETT263_FORCE_PUSH
  92. #else
  93. #define FORCE_PUSH_CONDITION battle_mode_
  94. #endif
  95.  
  96. #include "prop_base.h"
  97. #include "../sound/hybrid_font.h"
  98. #include "../sound/effect.h"
  99. #include "../common/current_preset.h"
  100.  
  101. #ifdef FETT263_EDIT_MODE_MENU
  102. #include "../common/file_reader.h"
  103. #include "../common/malloc_helper.h"
  104. #include "../common/color.h"
  105. #include "../styles/edit_mode.h"
  106. #endif
  107.  
  108. #undef PROP_TYPE
  109. #define PROP_TYPE SaberSabertrioButtons
  110.  
  111. EFFECT(dim);        // for EFFECT_POWERSAVE
  112. EFFECT(battery);    // for EFFECT_BATTERY_LEVEL
  113. EFFECT(bmbegin);    // for Begin Battle Mode
  114. EFFECT(bmend);      // for End Battle Mode
  115. EFFECT(vmbegin);    // for Begin Volume Menu
  116. EFFECT(vmend);      // for End Volume Menu
  117. EFFECT(volup);      // for increse volume
  118. EFFECT(voldown);    // for decrease volume
  119. EFFECT(volmin);     // for minimum volume reached
  120. EFFECT(volmax);     // for maximum volume reached
  121. EFFECT(mute);       // for Mute Mode
  122. EFFECT(faston);     // for EFFECT_FAST_ON
  123. EFFECT(blstbgn);    // for Begin Multi-Blast
  124. EFFECT(blstend);    // for End Multi-Blast
  125. EFFECT(push);       // for Force Push gesture in Battle Mode
  126. EFFECT(quote);      // quote on force effect
  127. EFFECT(no_blade);   // for NO BLADE img
  128. EFFECT(kyber);      // for KyberDial img
  129. EFFECT(volumemenu); // for Volume Menu img
  130. EFFECT(length);     // for Blade Length Menu img
  131. EFFECT(cancel);     // for Cancel
  132.  
  133. EFFECT(fontmenu);
  134. EFFECT(plus);
  135. EFFECT(minus);
  136. EFFECT(default);
  137. #ifdef FETT263_EDIT_MODE_MENU
  138. EFFECT(medit);      // Edit Mode
  139. #endif
  140.  
  141. #include "../sound/sound_library.h"
  142.  
  143. class GestureControlFile : public ConfigFile {
  144. public:
  145.   void iterateVariables(VariableOP *op) override {
  146.     CONFIG_VARIABLE2(gestureon, 1);
  147.  
  148.     CONFIG_VARIABLE2(swingonspeed, FETT263_SWING_ON_SPEED);
  149.     #ifdef TWIST_GESTURE
  150.     CONFIG_VARIABLE2(twiston, 1);
  151.     #else
  152.     CONFIG_VARIABLE2(twiston, 0);
  153.     #endif
  154.  
  155.     #ifdef FETT263_TWIST_OFF
  156.     CONFIG_VARIABLE2(twistoff, 1);
  157.     #else
  158.     CONFIG_VARIABLE2(twistoff, 0);
  159.     #endif
  160.  
  161.     #ifdef FORCE_PUSH_CONDITION
  162.     CONFIG_VARIABLE2(forcepush, 1);
  163.     #else
  164.     CONFIG_VARIABLE2(forcepush, 0);
  165.     #endif
  166.  
  167.     CONFIG_VARIABLE2(forcepushlen, FETT263_FORCE_PUSH_LENGTH);
  168.     CONFIG_VARIABLE2(lockupdelay, FETT263_LOCKUP_DELAY);
  169.     CONFIG_VARIABLE2(clashdetect, FETT263_BM_CLASH_DETECT);
  170.     #ifdef FETT263_MAX_CLASH
  171.     CONFIG_VARIABLE2(maxclash, FETT263_MAX_CLASH);
  172.     #else
  173.     CONFIG_VARIABLE2(maxclash, 16);
  174.     #endif
  175.   }
  176.   bool gestureon;           // gesture controls on/off for use with "Gesture Sleep"
  177.   bool swingon;             // Swing On ignition
  178.   int swingonspeed;         // Swing On speed (200 ~ 500 range)
  179.   bool twiston;             // Twist On ignition
  180.   bool thruston;            // Thrust On ignition
  181.   bool stabon;              // Stab On ignition
  182.   bool forcepush;           // Force Push
  183.   uint32_t forcepushlen;    // Force Push Length
  184.   uint32_t lockupdelay;     // Lockup Delay (for Battle Mode)
  185.   bool twistoff;            // Twist Off retraction
  186.   // disable PWR button for retraction, for use with "Power Lock" mode
  187.   // to prevent button turning saber off
  188.   bool powerlock;
  189.   int clashdetect;          // maximum Clash Strength to detect Clash during Battle Mode (0 ~ 10 range)
  190.   int maxclash;             // maximum Clash Strength for Clash Sound and Detection works with CLASH_THRESHOLD_G to create range of Clash Strength (8 ~ 16 range)
  191. };
  192.  
  193. #ifdef FETT263_EDIT_MODE_MENU
  194. // Edit Length
  195. int length_edit_length = 0;
  196.  
  197. // Edit Length Preview for Edit Mode
  198. // LengthEdit uses blade color and creates single white pixel at last pixel, black above last pixel
  199. template<class BASE, class LIGHTUP, class BLACK = BLACK>
  200. class LengthEdit {
  201. public:
  202.   void run(BladeBase* blade) {
  203.     base_.run(blade);
  204.     lightup_.run(blade);
  205.     black_.run(blade);
  206.   }
  207.  
  208.   OverDriveColor getColor(int led) {
  209.     if (led == length_edit_length) return lightup_.getColor(led);
  210.     if (led > length_edit_length) return black_.getColor(led);
  211.     return base_.getColor(led);
  212.   }
  213.  
  214. private:
  215.   BASE base_;
  216.   LIGHTUP lightup_;
  217.   BLACK black_;
  218. };
  219.  
  220. // Edit Style Settings
  221.  
  222. class IntEdit {
  223. public:
  224.   void run(BladeBase* blade) {  }
  225.   int getInteger(int led) { return int_edit_; }
  226.   static void SetIntValue(int value) { int_edit_ = value; }
  227. private:
  228.   static int int_edit_;
  229. };
  230.  
  231. int IntEdit::int_edit_ = 0;
  232. #endif
  233.  
  234. // Color List
  235. struct ColorListEntry { Color16 color; ColorNumber color_number; };
  236.  
  237. static constexpr ColorListEntry color_list_[] = {
  238.   { Red::color(), COLOR_RED },
  239.   { OrangeRed::color(), COLOR_ORANGERED },
  240.   { DarkOrange::color(), COLOR_DARKORANGE },
  241.   { Orange::color(), COLOR_ORANGE },
  242.   { { 46260, 33410, 0 }, COLOR_GOLD },
  243.   { Yellow::color(), COLOR_YELLOW },
  244.   { GreenYellow::color(), COLOR_GREENYELLOW },
  245.   { Green::color(), COLOR_GREEN },
  246.   { Aquamarine::color(), COLOR_AQUAMARINE },
  247.   { Cyan::color(), COLOR_CYAN },
  248.   { DeepSkyBlue::color(), COLOR_DEEPSKYBLUE },
  249.   { DodgerBlue::color(), COLOR_DODGERBLUE },
  250.   { Blue::color(), COLOR_BLUE },
  251.   { { 7710, 15420, 51400 }, COLOR_ICEBLUE },
  252.   { { 11102, 92, 53864 }, COLOR_INDIGO },
  253.   { { 24000, 0, 50536 }, COLOR_PURPLE },
  254.   { { 30324, 0, 49768 }, COLOR_DEEPPURPLE },
  255.   { Magenta::color(), COLOR_MAGENTA },
  256.   { DeepPink::color(), COLOR_DEEPPINK },
  257.   { { 25700, 25700, 38550 }, COLOR_SILVER },
  258.   { { 21845, 21845, 51400 }, COLOR_GLACIER },
  259.   { { 46260, 46260, 65535 }, COLOR_ICEWHITE },
  260.   { LightCyan::color(), COLOR_LIGHTCYAN },
  261.   { Moccasin::color(), COLOR_MOCCASIN },
  262.   { LemonChiffon::color(), COLOR_LEMONCHIFFON },
  263.   { NavajoWhite::color(), COLOR_NAVAJOWHITE },
  264.   { White::color(), COLOR_WHITE }
  265. };
  266.  
  267. // Edit Color Submenu
  268. const int rgb_arg_menu_[] {
  269.   BASE_COLOR_ARG,
  270.   ALT_COLOR_ARG,
  271.   BLAST_COLOR_ARG,
  272.   CLASH_COLOR_ARG,
  273.   LOCKUP_COLOR_ARG,
  274.   DRAG_COLOR_ARG,
  275.   LB_COLOR_ARG,
  276.   STAB_COLOR_ARG,
  277.   PREON_COLOR_ARG,
  278.   IGNITION_COLOR_ARG,
  279.   RETRACTION_COLOR_ARG,
  280.   POSTOFF_COLOR_ARG,
  281.   SWING_COLOR_ARG,
  282.   EMITTER_COLOR_ARG,
  283.   OFF_COLOR_ARG
  284. };
  285.  
  286. // Edit Style Setting Submenu
  287. const int int_arg_menu_[] {
  288.   STYLE_OPTION_ARG,
  289.   IGNITION_OPTION_ARG,
  290.   IGNITION_TIME_ARG,
  291.   IGNITION_DELAY_ARG,
  292.   IGNITION_POWER_UP_ARG,
  293.   RETRACTION_OPTION_ARG,
  294.   RETRACTION_TIME_ARG,
  295.   RETRACTION_DELAY_ARG,
  296.   RETRACTION_COOL_DOWN_ARG,
  297.   LOCKUP_POSITION_ARG,
  298.   DRAG_SIZE_ARG,
  299.   MELT_SIZE_ARG,
  300.   SWING_OPTION_ARG,
  301.   EMITTER_SIZE_ARG,
  302.   OFF_OPTION_ARG,
  303.   // preon always needs to be last options to allow check for SFX_preon in menu
  304.   PREON_OPTION_ARG,
  305.   PREON_SIZE_ARG
  306. };
  307.  
  308. // The Saber class implements the basic states and actions
  309. // for the saber.
  310. class SaberSabertrioButtons : public PROP_INHERIT_PREFIX PropBase {
  311. public:
  312.   SaberSabertrioButtons() : PropBase() {}
  313.   const char* name() override { return "SaberSabertrioButtons"; }
  314.  
  315.   GestureControlFile saved_gesture_control;
  316.  
  317.   void RestoreGestureState() {
  318.     saved_gesture_control.ReadINIFromDir(NULL, "gesture");
  319.   }
  320.  
  321.   void SaveGestureState() {
  322.     STDOUT.println("Saving Gesture State");
  323.     saved_gesture_control.Write("gesture.tmp");
  324.     saved_gesture_control.Write("gesture.ini");
  325.   }
  326.  
  327.   //This generates ini files to save the saber state
  328.   void GenerateIniFiles() {
  329.     SaveState(current_preset_.preset_num);
  330.     SaveGestureState();
  331.   }
  332.  
  333.   Color16 GetColorArg(int blade, int arg) {
  334.     char argspace[32];
  335.     if (style_parser.GetArgument(current_preset_.GetStyle(blade), arg + 2, argspace))
  336.     {
  337.       char* tmp;
  338.       int r = strtol(argspace, &tmp, 0);
  339.       int g = strtol(tmp+1, &tmp, 0);
  340.       int b = strtol(tmp+1, NULL, 0);
  341.       return Color16(r,g,b);
  342.     }
  343.     return Color16();
  344.   }
  345.  
  346.   // Color / Style Editing
  347.   HSL hsl_;
  348.   ShowColorAllBladesTemplate<ShowColorStyle> show_color_all_;
  349.   #ifdef FETT263_EDIT_MODE_MENU
  350.   ShowColorSingleBladeTemplate<ShowColorStyle> show_color_;
  351.   ShowColorSingleBladeTemplate<Mix<Bump<Int<16384>,Int<14000>>,RgbArg<1,Rgb<255,0,0>>,ShowColorStyle>> bump_color_;
  352.   ShowColorSingleBladeTemplate<Mix<SmoothStep<Int<26000>,Int<8000>>,RgbArg<1,Rgb<255,0,0>>,ShowColorStyle>> tip_color_;
  353.   ShowColorSingleBladeTemplate<Mix<SmoothStep<Int<7000>,Int<-6000>>,RgbArg<1,Rgb<255,0,0>>,ShowColorStyle>> hilt_color_;
  354.   ShowColorSingleBladeTemplate<Mix<SmoothStep<Int<7000>,Int<-6000>>,Black,ShowColorStyle>> pre_color_;
  355.   ShowColorSingleBladeTemplate<LengthEdit<ShowColorStyle,Pulsing<White,Rgb<100,100,100>,800>>,LengthEdit<ShowColorStyle,Pulsing<White,Rgb<100,100,100>,800>>> show_length_;
  356.   ShowColorSingleBladeTemplate<Mix<Bump<IntEdit,Int<10000>>,Black,ShowColorStyle>> show_lockup_position_;
  357.   ShowColorSingleBladeTemplate<Mix<SmoothStep<IntEdit,Int<6000>>,Black,ShowColorStyle>> show_drag_size_;
  358.   ShowColorSingleBladeTemplate<Mix<SmoothStep<IntEdit,Int<-6000>>,Black,ShowColorStyle>> show_emitter_size_;
  359.   #if NUM_BLADES > 1
  360.   ShowColorSingleBladeTemplate<Pulsing<ShowColorStyle,Black,800>,Pulsing<ShowColorStyle,Black,800>> show_preview_;
  361.   #endif
  362.  
  363.   // Show Full Blade Preview for Color Editing
  364.   void ShowFull() {
  365.     show_color_.Start(blade_num_);
  366.     ShowColorStyle::SetColor(GetColorArg(blade_num_, effect_num_));
  367.     saved_color_ = GetColorArg(blade_num_, effect_num_);
  368.     hsl_ = saved_color_.toHSL();
  369.     hsl_angle_ = fusor.angle2();
  370.   }
  371.  
  372.   // Show Partial Blade Preview (Clash, Lockup, Drag, Melt, Preon, Emitter) for Color Editing
  373.   void ShowPart() {
  374.     ShowColorStyle::SetColor(GetColorArg(blade_num_, effect_num_));
  375.     saved_color_ = GetColorArg(blade_num_, effect_num_);
  376.     hsl_ = saved_color_.toHSL();
  377.     hsl_angle_ = fusor.angle2();
  378.   }
  379.  
  380.   #if NUM_BLADES > 1
  381.   void BladePreview(int blade) {
  382.     if (blade_preview_ > 0)
  383.     {
  384.       show_preview_.Stop(blade_preview_);
  385.     }
  386.     blade_preview_ = blade;
  387.     show_preview_.Start(blade);
  388.     ShowColorStyle::SetColor(GetColorArg(blade_num_, 1));
  389.   }
  390.   #endif
  391.  
  392.   void LengthPreview(int blade) {
  393.     show_length_.Start(blade);
  394.     length_edit_length = blade_length_ - 1;
  395.     ShowColorStyle::SetColor(GetColorArg(blade_num_, 1));
  396.   }
  397.  
  398.   // Copy Color Arguments from one blade to another
  399.   void CopyColors() {
  400.     effect_num_ = 16;
  401.     while (true) {
  402.       effect_num_ -= 1;
  403.       if (style_parser.UsesArgument(current_preset_.GetStyle(blade_num_), effect_num_ + 2)) break;
  404.     }
  405.     saved_color_ = GetColorArg(blade_num_, effect_num_);
  406.     hsl_ = saved_color_.toHSL();
  407.     NewColor(blade_num_, effect_num_);
  408.     current_preset_.Save();
  409.     effect_num_ = 0;
  410.   }
  411.  
  412.   // Set In/Out (Ignition/Retraction) Arguments
  413.   void SetInOut() {
  414.     char style_arg[10];
  415.     itoa(calc_, style_arg, 10);
  416.     current_preset_.SetStyle(blade_num_,style_parser.SetArgument(current_preset_.GetStyle(blade_num_), set_num_ + 2, style_arg));
  417.     current_preset_.Save();
  418.     switch (menu_type_) {
  419.       case MENU_STYLE_OPTION:
  420.       case MENU_IGNITION_TIME:
  421.       case MENU_IGNITION_OPTION:
  422.       case MENU_IGNITION_POWER_UP_OPTION:
  423.       case MENU_IGNITION_DELAY:
  424.       SetPresetFast(current_preset_.preset_num);
  425.       break;
  426.       case MENU_PREON_OPTION:
  427.       case MENU_PREON_SIZE:
  428.       UpdatePreon();
  429.       break;
  430.       case MENU_RETRACTION_TIME:
  431.       case MENU_RETRACTION_OPTION:
  432.       case MENU_RETRACTION_COOL_DOWN_OPTION:
  433.       case MENU_RETRACTION_DELAY:
  434.       UpdateStyle();
  435.       off_event_ = true;
  436.       restart_millis_ = millis();
  437.       break;
  438.       default:
  439.       break;
  440.     }
  441.   }
  442.  
  443.   void RevertInOut() {
  444.     char style_arg[10];
  445.     itoa(arg_revert_, style_arg, 10);
  446.     current_preset_.SetStyle(blade_num_,style_parser.SetArgument(current_preset_.GetStyle(blade_num_), set_num_ + 2, style_arg));
  447.     switch (menu_type_) {
  448.       case MENU_RETRACTION_OPTION:
  449.       case MENU_RETRACTION_TIME:
  450.       case MENU_RETRACTION_COOL_DOWN_OPTION:
  451.       case MENU_RETRACTION_DELAY:
  452.       char ig[10];
  453.       itoa(ignite_time_, ig, 10);
  454.       current_preset_.SetStyle(blade_num_,style_parser.SetArgument(current_preset_.GetStyle(blade_num_), RETRACTION_TIME_ARG, ig));
  455.       break;
  456.       default:
  457.       break;
  458.     }
  459.     current_preset_.Save();
  460.     SetPresetFast(current_preset_.preset_num);
  461.   }
  462.  
  463.   void SayStyleNumber(int style_num) {
  464.     sound_library_.SayStyle();
  465.     sound_library_.SayNumber(style_num, SAY_WHOLE);
  466.     if (style_parser.UsesArgument(current_preset_.GetStyle(blade_num_), STYLE_OPTION_ARG + 2)) {
  467.       char argspace[32];
  468.       style_parser.GetArgument(current_preset_.GetStyle(blade_num_), STYLE_OPTION_ARG + 2, argspace);
  469.       int opt = strtol(argspace, NULL, 0);
  470.       sound_library_.SayOption();
  471.       sound_library_.SayNumber(opt, SAY_WHOLE);
  472.     }
  473.   }
  474.  
  475.   // Stop location/size previews in Style Settings Mode
  476.   void StopSettingPreview() {
  477.     switch (set_num_) {
  478.       case LOCKUP_POSITION_ARG:
  479.       show_lockup_position_.Stop(blade_num_);
  480.       break;
  481.  
  482.       case DRAG_SIZE_ARG:
  483.       case MELT_SIZE_ARG:
  484.       show_drag_size_.Stop(blade_num_);
  485.       break;
  486.  
  487.       case EMITTER_SIZE_ARG:
  488.       case PREON_SIZE_ARG:
  489.       show_emitter_size_.Stop(blade_num_);
  490.       break;
  491.  
  492.       default:
  493.       break;
  494.     }
  495.     UpdateStyle();
  496.     menu_type_ = MENU_STYLE_SETTING_SUB;
  497.   }
  498.  
  499.   // Save IntArg values 16 ~ 25 from Edit Mode Menu selection
  500.   void SaveStyleSetting() {
  501.     char style_arg[10];
  502.     itoa(calc_, style_arg, 10);
  503.     current_preset_.SetStyle(blade_num_,style_parser.SetArgument(current_preset_.GetStyle(blade_num_), set_num_ + 2, style_arg));
  504.     current_preset_.Save();
  505.     StopSettingPreview();
  506.     MenuSave();
  507.   }
  508.  
  509.   void CancelStyleSetting() {
  510.     StopSettingPreview();
  511.     MenuCancel();
  512.   }
  513.   #endif
  514.  
  515.   // Edit Color
  516.   #define H_CHANGE (M_PI / 98304)
  517.   #define L_ANGLE (M_PI / 30)
  518.   #define H_ANGLE (M_PI / 16384)
  519.   #define EDIT_MODE_ZOOM (M_PI * 2 / 2000)
  520.  
  521.   void EditColor() {
  522.     if (edit_color_ && color_mode_ != COLOR_LIST)
  523.     {
  524.       float a = fusor.angle2() - hsl_angle_;
  525.       if (a > M_PI) a-=M_PI*2;
  526.       if (a < -M_PI) a+=M_PI*2;
  527.       float angle = 100;
  528.       switch (color_mode_) {
  529.         case EDIT_COLOR: angle = H_ANGLE; break;
  530.         case ZOOM_COLOR:
  531.         case CC_ZOOM_COLOR:
  532.         angle = EDIT_MODE_ZOOM;
  533.         break;
  534.         case EDIT_WHITE:
  535.         case EDIT_BLACK: angle = L_ANGLE; break;
  536.         default: break;
  537.       }
  538.       int steps = 0;
  539.       if (a > angle * 2/3)
  540.       {
  541.         hsl_angle_ += angle;
  542.         if (hsl_angle_ > M_PI) hsl_angle_ -= M_PI*2;
  543.         steps++;
  544.       }
  545.       if (a < -angle * 2/3)
  546.       {
  547.         hsl_angle_ -= angle;
  548.         if (hsl_angle_ < M_PI) hsl_angle_ += M_PI*2;
  549.         steps--;
  550.       }
  551.       switch (color_mode_) {
  552.         default: break;
  553.         case EDIT_COLOR:
  554.         case ZOOM_COLOR:
  555.         case CC_ZOOM_COLOR:
  556.         hsl_.H = fract(hsl_.H - H_CHANGE * steps);
  557.         break;
  558.  
  559.         case EDIT_WHITE:
  560.         if (steps > 0)
  561.         {
  562.           if (hsl_.L < 1.0)
  563.           {
  564.             hsl_.L = clamp(hsl_.L + 0.01, 0.5, 1.0);
  565.             if (hsl_.L == 1.0)
  566.             {
  567.               sound_library_.SayMaximum();
  568.               hsl_angle_ = fusor.angle2();
  569.             }
  570.           }
  571.         }
  572.         if (steps < 0)
  573.         {
  574.           if (hsl_.L > 0.5)
  575.           {
  576.             hsl_.L = clamp(hsl_.L - 0.01, 0.5, 1.0);
  577.             if (hsl_.L == 0.5)
  578.             {
  579.               sound_library_.SayMinimum();
  580.               hsl_angle_ = fusor.angle2();
  581.             }
  582.           }
  583.         }
  584.         break;
  585.  
  586.         case EDIT_BLACK:
  587.         if (steps > 0)
  588.         {
  589.           if (hsl_.L < 0.5)
  590.           {
  591.             hsl_.L = clamp(hsl_.L + 0.01, 0.01, 0.5);
  592.             if (hsl_.L == 0.5)
  593.             {
  594.               sound_library_.SayMaximum();
  595.               hsl_angle_ = fusor.angle2();
  596.             }
  597.           }
  598.         }
  599.         if (steps < 0)
  600.         {
  601.           if (hsl_.L > 0.01)
  602.           {
  603.             hsl_.L = clamp(hsl_.L - 0.01, 0.01, 0.5);
  604.             if (hsl_.L == 0.01)
  605.             {
  606.               sound_library_.SayMinimum();
  607.               hsl_angle_ = fusor.angle2();
  608.             }
  609.           }
  610.         }
  611.         break;
  612.       }
  613.       ShowColorStyle::SetColor(Color16(hsl_));
  614.     }
  615.   }
  616.  
  617.   // Saves New Color from Edit Mode Preview Styles to Preset
  618.   void NewColor(int blade, int effect) {
  619.     STDOUT << "NewColor(" << blade << "," << effect << ")\n";
  620.     char new_color[32];
  621.     Color16 color_source;
  622.     switch (color_mode_) {
  623.       case COLOR_LIST:
  624.       case CC_COLOR_LIST:
  625.       color_source = Color16(color_list_[dial_].color);
  626.       break;
  627.  
  628.       default:
  629.       color_source = Color16(hsl_);
  630.       break;
  631.     }
  632.     itoa(Color16(color_source).r, new_color, 10);
  633.     strcat(new_color, ",");
  634.     itoa(Color16(color_source).g, new_color + strlen(new_color), 10);
  635.     strcat(new_color, ",");
  636.     itoa(Color16(color_source).b, new_color + strlen(new_color), 10);
  637.     #if NUM_BLADES > 1
  638.     if (color_mode_ == CC_COLOR_LIST  || color_mode_ == CC_ZOOM_COLOR)
  639.     {
  640.       for (int i = 1; i <= NUM_BLADES; i++) {
  641.         current_preset_.SetStyle(i,style_parser.SetArgument(current_preset_.GetStyle(i), effect + 2, new_color));
  642.       }
  643.     }
  644.     else
  645.     {
  646.       current_preset_.SetStyle(blade,style_parser.SetArgument(current_preset_.GetStyle(blade), effect + 2, new_color));
  647.     }
  648.     #else
  649.     current_preset_.SetStyle(blade,style_parser.SetArgument(current_preset_.GetStyle(blade), effect + 2, new_color));
  650.     #endif
  651.     color_mode_ = NONE;
  652.   }
  653.  
  654.   // Toggles ColorChange Mode if current style uses RgbArg to CC_COLOR_LIST
  655.   void ToggleCCMode() {
  656.     bool uses_rgb_arg = false;
  657.     for (int i = 1; i <= NUM_BLADES; i++)
  658.     uses_rgb_arg |= style_parser.UsesArgument(current_preset_.GetStyle(i), 3);
  659.     if (!uses_rgb_arg)
  660.     {
  661.       #ifndef DISABLE_COLOR_CHANGE
  662.       ToggleColorChangeMode();
  663.       #endif
  664.     }
  665.     else
  666.     {
  667.       bool handles_color_change;
  668.       #define USES_COLOR_CHANGE(N) \
  669.       handles_color_change |= current_config->blade##N->current_style() && current_config->blade##N->current_style()->IsHandled(HANDLED_FEATURE_CHANGE_TICKED);
  670.       ONCEPERBLADE(USES_COLOR_CHANGE)
  671.       if (!handles_color_change)
  672.       {
  673.         color_mode_ = CC_COLOR_LIST;
  674.         show_color_all_.Start();
  675.         for (int i = 1; i <= NUM_BLADES; i++) {
  676.           if (style_parser.UsesArgument(current_preset_.GetStyle(i), BASE_COLOR_ARG + 2))
  677.           {
  678.             ShowColorStyle::SetColor(GetColorArg(i, BASE_COLOR_ARG));
  679.             break;
  680.           }
  681.         }
  682.         current_menu_angle_ = fusor.angle2();
  683.         dial_ = -1;
  684.         hybrid_font.PlayCommon(&SFX_ccbegin);
  685.       }
  686.       else
  687.       {
  688.         #ifndef DISABLE_COLOR_CHANGE
  689.         ToggleColorChangeMode();
  690.         #endif
  691.       }
  692.     }
  693.   }
  694.  
  695.   void Setup() override {
  696.     RestoreGestureState();
  697.   }
  698.  
  699.   #ifdef FETT263_SAVE_CHOREOGRAPHY
  700.   bool chdir(const char* dir) override {
  701.     bool ret = PropBase::chdir(dir);
  702.     RestoreChoreo();
  703.     clash_count_ = 0;
  704.     return ret;
  705.   }
  706.   #endif
  707.  
  708.   // Check Event "Delays" for Edit Mode for Ignition/Retraction/Preon Settings Previews and Choreography Save
  709.   void CheckEvent() {
  710.     if (next_event_ && !wav_player->isPlaying())
  711.     {
  712.         #ifdef FETT263_EDIT_MODE_MENU
  713.         switch (menu_type_) {
  714.           case MENU_IGNITION_TIME:
  715.           case MENU_RETRACTION_TIME:
  716.           next_event_ = false;
  717.           SetInOut();
  718.           break;
  719.  
  720.           default:
  721.           next_event_ = false;
  722.           break;
  723.         }
  724.         #endif
  725.     }
  726.  
  727.     #ifdef FETT263_EDIT_MODE_MENU
  728.     if (off_event_ && millis() - restart_millis_ > 200)
  729.     {
  730.       Off();
  731.       off_event_ = false;
  732.       restart_ = true;
  733.       restart_millis_ = millis();
  734.     }
  735.     if (restart_ && (int)(millis() - restart_millis_) > ignite_time_ + 1000)
  736.     {
  737.       restart_ = false;
  738.       FastOn();
  739.     }
  740.     #endif
  741.   }
  742.  
  743.   enum ClashType {
  744.     CLASH_NONE,
  745.     CLASH_CHECK,
  746.     CLASH_BATTLE_MODE,
  747.     CLASH_LOCKUP,
  748.     CLASH_LOCKUP_END,
  749.     #ifdef FETT263_CLASH_STRENGTH_SOUND
  750.     CLASH_NORMAL,
  751.     CLASH_STAB,
  752.     #endif
  753.   };
  754.  
  755.   #ifdef FETT263_CLASH_STRENGTH_SOUND
  756.   void HandleClash() {
  757.     if (clash_type_ == CLASH_BATTLE_MODE)
  758.     {
  759.       if (SaberBase::GetClashStrength() < saved_gesture_control.clashdetect)
  760.       {
  761.         clash_type_ = CLASH_NORMAL;
  762.       }
  763.       else
  764.       {
  765.         clash_type_ = CLASH_LOCKUP;
  766.         auto_lockup_on_ = true;
  767.       }
  768.     }
  769.  
  770.     if (saved_gesture_control.maxclash < 8) saved_gesture_control.maxclash = 8;
  771.     float clash_value = (SaberBase::GetClashStrength() - GetCurrentClashThreshold()) / saved_gesture_control.maxclash;
  772.     switch (clash_type_) {
  773.       default:
  774.       break;
  775.       case CLASH_NORMAL:
  776.       if (SFX_clash)
  777.       {
  778.         SFX_clash.SelectFloat(clash_value);
  779.       }
  780.       else
  781.       {
  782.         SFX_clsh.SelectFloat(clash_value);
  783.       }
  784.       SaberBase::DoClash();
  785.       break;
  786.       case CLASH_STAB:
  787.       if (SFX_stab)
  788.       {
  789.         SFX_stab.SelectFloat(clash_value);
  790.       }
  791.       else
  792.       {
  793.         if (SFX_clash)
  794.         {
  795.           SFX_clash.SelectFloat(clash_value);
  796.         }
  797.         else
  798.         {
  799.           SFX_clsh.SelectFloat(clash_value);
  800.         }
  801.       }
  802.       SaberBase::DoStab();
  803.       break;
  804.  
  805.       case CLASH_LOCKUP:
  806.       if (battle_mode_) clash_value = (SaberBase::GetClashStrength() - saved_gesture_control.clashdetect) / saved_gesture_control.maxclash;
  807.       if (SFX_bgnlock)
  808.       {
  809.         SFX_bgnlock.SelectFloat(clash_value);
  810.       }
  811.       SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  812.       SaberBase::DoBeginLockup();
  813.       break;
  814.       case CLASH_LOCKUP_END:
  815.       if (SFX_endlock)
  816.       {
  817.         float swing_value = fusor.swing_speed() / 600;
  818.         SFX_endlock.SelectFloat(swing_value);
  819.       }
  820.       SaberBase::DoEndLockup();
  821.       SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  822.       break;
  823.     }
  824.     clash_type_ = CLASH_NONE;
  825.   }
  826.   #endif
  827.  
  828.   void Loop() override {
  829.     PropBase::Loop();
  830.     DetectTwist();
  831.     Vec3 mss = fusor.mss();
  832.     if (clash_type_ != CLASH_NONE && millis() - clash_impact_millis_ > 1)
  833.     {
  834.       // CHECK PUSH
  835.       if (clash_type_ == CLASH_CHECK)
  836.       {
  837.         Event(BUTTON_NONE, EVENT_PUSH);
  838.         clash_type_ = CLASH_NONE;
  839.       }
  840.       if (clash_type_ != CLASH_LOCKUP_END)
  841.       {
  842.         #ifdef FETT263_CLASH_STRENGTH_SOUND
  843.         HandleClash();
  844.         #else
  845.         if (clash_type_ == CLASH_BATTLE_MODE && SaberBase::GetClashStrength() > saved_gesture_control.clashdetect) {
  846.           SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  847.           SaberBase::DoBeginLockup();
  848.           auto_lockup_on_ = true;
  849.         }
  850.         else
  851.         {
  852.           SaberBase::DoClash();
  853.         }
  854.         clash_type_ = CLASH_NONE;
  855.         #endif
  856.       }
  857.     }
  858.     sound_library_.Poll(wav_player);
  859.     #if defined(FETT263_EDIT_MODE_MENU) || defined(FETT263_SAVE_CHOREOGRAPHY)
  860.     CheckEvent();
  861.     #endif
  862.     EditColor();
  863.     if (SaberBase::IsOn())
  864.     {
  865.       DetectSwing();
  866.  
  867.       if (auto_lockup_on_ &&
  868.         !swinging_ &&
  869.         fusor.swing_speed() > 120 &&
  870.         millis() - clash_impact_millis_ > saved_gesture_control.lockupdelay &&
  871.         SaberBase::Lockup())
  872.         {
  873.           #ifdef FETT263_CLASH_STRENGTH_SOUND
  874.           clash_type_ = CLASH_LOCKUP_END;
  875.           HandleClash();
  876.           #else
  877.           SaberBase::DoEndLockup();
  878.           SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  879.           #endif
  880.           auto_lockup_on_ = false;
  881.         }
  882.  
  883.         if (auto_melt_on_ &&
  884.           !swinging_ &&
  885.           fusor.swing_speed() > 60 &&
  886.           millis() - clash_impact_millis_ > saved_gesture_control.lockupdelay &&
  887.           SaberBase::Lockup())
  888.           {
  889.             SaberBase::DoEndLockup();
  890.             SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  891.             auto_melt_on_ = false;
  892.           }
  893.  
  894.           // EVENT_PUSH
  895.           if (fabs(mss.x) < 3.0 &&
  896.           mss.y * mss.y + mss.z * mss.z > 100 &&
  897.           fusor.swing_speed() < 20 &&
  898.           fabs(fusor.gyro().x) < 5)
  899.           {
  900.             if (millis() - push_begin_millis_ > saved_gesture_control.forcepushlen) {
  901.               // Checking for Clash at end of movement
  902.               clash_type_ = CLASH_CHECK;
  903.               push_begin_millis_ = millis();
  904.               clash_impact_millis_ = millis();
  905.             }
  906.           }
  907.           else
  908.           {
  909.             push_begin_millis_ = millis();
  910.           }
  911.         }
  912.         else
  913.         {
  914.           // EVENT_SWING - Swing On gesture control to allow fine tuning of speed needed to ignite
  915.           if (menu_ || millis() - saber_off_time_millis_ < MOTION_TIMEOUT)
  916.           {
  917.             SaberBase::RequestMotion();
  918.             if (swinging_ && fusor.swing_speed() < 90)
  919.             {
  920.               swinging_ = false;
  921.             }
  922.             if (!swinging_ && fusor.swing_speed() > saved_gesture_control.swingonspeed)
  923.             {
  924.               swinging_ = true;
  925.               Event(BUTTON_NONE, EVENT_SWING);
  926.             }
  927.           }
  928.           // EVENT_THRUST
  929.           if (mss.y * mss.y + mss.z * mss.z < 16.0 &&
  930.             mss.x > 14  &&
  931.             fusor.swing_speed() < 150)
  932.             {
  933.               if (millis() - thrust_begin_millis_ > 15)
  934.               {
  935.                 Event(BUTTON_NONE, EVENT_THRUST);
  936.                 thrust_begin_millis_ = millis();
  937.               }
  938.             }
  939.             else
  940.             {
  941.               thrust_begin_millis_ = millis();
  942.             }
  943.           }
  944.           DetectMenuTurn();
  945.           #ifdef ENABLE_AUDIO
  946.           TrackPlayer();
  947.           #else
  948.           STDOUT.println("Audio disabled.");
  949.           #endif
  950.         }
  951.         //End of Loop()
  952.  
  953.         // Fett263 Track Player
  954.         enum TrackMode {
  955.           PLAYBACK_OFF,
  956.           PLAYBACK_LOOP,
  957.           PLAYBACK_ROTATE,
  958.           PLAYBACK_RANDOM,
  959.         };
  960.  
  961.         void StartTrackPlayer() {
  962.           num_tracks_ = RunCommandAndGetSingleLine("list_current_tracks", nullptr, 0, 0, 0);
  963.           if (num_tracks_ > 0)
  964.           {
  965.             sound_library_.SaySelect();
  966.             StartMenu(MENU_TRACK_PLAYER);
  967.             track_num_ = 1;
  968.             PlayTrack();
  969.           }
  970.           else
  971.           {
  972.             // Loop default track if tracks not found
  973.             sound_library_.SayLoop();
  974.             track_num_ = 0;
  975.             track_mode_ = PLAYBACK_LOOP;
  976.             StartOrStopTrack();
  977.           }
  978.         }
  979.  
  980.         void TrackPlayer() {
  981.           if (track_mode_ != PLAYBACK_OFF)
  982.           {
  983.             if (!track_player_)
  984.             {
  985.               if (track_num_ <= 0 && track_mode_ == PLAYBACK_LOOP)
  986.               {
  987.                 StartOrStopTrack();
  988.               }
  989.               else
  990.               {
  991.                 switch (track_mode_) {
  992.                   case PLAYBACK_ROTATE:
  993.                   track_num_ += 1;
  994.                   if (track_num_ > num_tracks_) track_num_ = 1;
  995.                   break;
  996.                   case PLAYBACK_RANDOM:
  997.                   track_num_ = rand() % num_tracks_;
  998.                   if (track_num_ <= 0) track_num_ = num_tracks_;
  999.                   break;
  1000.                   default:
  1001.                   break;
  1002.                 }
  1003.                 PlayTrack();
  1004.               }
  1005.             }
  1006.           }
  1007.           else
  1008.           {
  1009.             if (track_player_ && !track_player_->isPlaying())
  1010.             {
  1011.               track_player_.Free();
  1012.             }
  1013.           }
  1014.         }
  1015.  
  1016.         void PlayTrack() {
  1017.           char playtrack[128];
  1018.           RunCommandAndGetSingleLine("list_current_tracks", nullptr, track_num_, playtrack, sizeof(playtrack));
  1019.           MountSDCard();
  1020.           EnableAmplifier();
  1021.           track_player_ = GetFreeWavPlayer();
  1022.           if (track_player_)
  1023.           {
  1024.             track_player_->Play(playtrack);
  1025.           }
  1026.           else
  1027.           {
  1028.             STDOUT.println("No available WAV players.");
  1029.           }
  1030.         }
  1031.  
  1032.         void StopTrackPlayer() {
  1033.           track_mode_ = PLAYBACK_OFF;
  1034.           if (track_player_)
  1035.           {
  1036.             track_player_->Stop();
  1037.             track_player_.Free();
  1038.           }
  1039.           else
  1040.           {
  1041.             StartOrStopTrack();
  1042.           }
  1043.         }
  1044.  
  1045.         void SelectPreset(int preset) {
  1046.           #ifdef SAVE_PRESET
  1047.           SaveState(preset);
  1048.           #endif
  1049.           SetPreset(preset, true);
  1050.         }
  1051.  
  1052.         void DetectMenuTurn() {
  1053.           if (menu_ || color_mode_ == CC_COLOR_LIST)
  1054.           {
  1055.             float a = fusor.angle2() - current_menu_angle_;
  1056.             if (a > M_PI) a-=M_PI*2;
  1057.             if (a < -M_PI) a+=M_PI*2;
  1058.             if (a > twist_menu_ * 2/3)
  1059.             {
  1060.               Event(BUTTON_NONE, EVENT_TWIST_RIGHT);
  1061.               STDOUT.println("EVENT MENU TURN RIGHT");
  1062.               current_menu_angle_ = fusor.angle2();
  1063.             }
  1064.             if (a < -twist_menu_ * 2/3)
  1065.             {
  1066.               Event(BUTTON_NONE, EVENT_TWIST_LEFT);
  1067.               STDOUT.println("EVENT MENU TURN LEFT");
  1068.               current_menu_angle_ = fusor.angle2();
  1069.             }
  1070.           }
  1071.         }
  1072.  
  1073.         #ifdef FETT263_EDIT_MODE_MENU
  1074.         #define SUBMENUS 3
  1075.         #define COLOR_OPTIONS 3
  1076.         #define GESTURE_OPTIONS 12
  1077.         #define SET_SUBMENUS 5
  1078.         #endif
  1079.  
  1080.         enum MenuType {
  1081.           MENU_TOP,
  1082.           MENU_PRESET,
  1083.           MENU_VOLUME,
  1084.           MENU_TRACK_PLAYER,
  1085.           #ifdef FETT263_SAVE_CHOREOGRAPHY
  1086.           MENU_REHEARSE,
  1087.           #endif
  1088.           #ifdef FETT263_EDIT_MODE_MENU
  1089.           MENU_STYLE,
  1090.           MENU_COLOR,
  1091.           MENU_COLOR_CHANGE,
  1092.           MENU_FONT,
  1093.           MENU_TRACK,
  1094.           MENU_LENGTH,
  1095.           MENU_COPY,
  1096.           MENU_DELETE,
  1097.           MENU_RESET,
  1098.           MENU_EXIT,
  1099.           MENU_STYLE_SUB,
  1100.           MENU_COLOR_SUB,
  1101.           MENU_COLOR_MODE,
  1102.           MENU_EFFECT,
  1103.           MENU_RESET_COLOR,
  1104.           #if NUM_BLADES > 1
  1105.           MENU_COPY_COLOR,
  1106.           MENU_BLADE_STYLE,
  1107.           MENU_BLADE_COLOR,
  1108.           MENU_BLADE_COPY,
  1109.           MENU_BLADE_LENGTH,
  1110.           #endif
  1111.           MENU_GESTURE_SUB,
  1112.           MENU_SWINGON,
  1113.           MENU_TWISTON,
  1114.           MENU_THRUSTON,
  1115.           MENU_STABON,
  1116.           MENU_FORCEPUSH,
  1117.           MENU_TWISTOFF,
  1118.           MENU_POWERLOCK,
  1119.           MENU_SETTING_SUB,
  1120.           MENU_STYLE_SETTING_SUB,
  1121.           MENU_STYLE_OPTION,
  1122.           MENU_IGNITION_OPTION,
  1123.           MENU_IGNITION_TIME,
  1124.           MENU_IGNITION_POWER_UP_OPTION,
  1125.           MENU_IGNITION_DELAY,
  1126.           MENU_RETRACTION_OPTION,
  1127.           MENU_RETRACTION_TIME,
  1128.           MENU_RETRACTION_COOL_DOWN_OPTION,
  1129.           MENU_RETRACTION_DELAY,
  1130.           MENU_LOCKUP_POSITION,
  1131.           MENU_DRAG_SIZE,
  1132.           MENU_MELT_SIZE,
  1133.           MENU_SWING_OPTION,
  1134.           MENU_EMITTER_SIZE,
  1135.           MENU_PREON_OPTION,
  1136.           MENU_PREON_SIZE,
  1137.           MENU_DIM_BLADE,
  1138.           MENU_CLASH_THRESHOLD,
  1139.           MENU_SWINGON_SPEED,
  1140.           MENU_FORCEPUSH_LENGTH,
  1141.           MENU_LOCKUP_DELAY,
  1142.           MENU_CLASH_DETECT,
  1143.           MENU_MAX_CLASH,
  1144.           MENU_COLOR_BASE,
  1145.           MENU_COLOR_ALT,
  1146.           MENU_COLOR_BLAST,
  1147.           MENU_COLOR_CLASH,
  1148.           MENU_COLOR_LOCKUP,
  1149.           MENU_COLOR_DRAG,
  1150.           MENU_COLOR_LB,
  1151.           MENU_COLOR_STAB,
  1152.           MENU_COLOR_PREON,
  1153.           MENU_COLOR_IGNITE,
  1154.           MENU_COLOR_RETRACT,
  1155.           MENU_COLOR_PSTOFF,
  1156.           MENU_COLOR_SWING,
  1157.           MENU_COLOR_EMITTER,
  1158.           MENU_COLOR_OFF,
  1159.           #endif
  1160.         };
  1161.  
  1162.         enum MainMenu {
  1163.           EDIT_COLORS = 1,
  1164.           EDIT_BLADE_LENGTH = 2,
  1165.           EXIT_MENU = 3,
  1166.         };
  1167.  
  1168.         enum StyleSubMenu {
  1169.           EDIT_STYLE_SETTINGS = 2,
  1170.         };
  1171.  
  1172.         enum ColorSubMenu {
  1173.           EDIT_EFFECT_COLOR = 1,
  1174.           #if NUM_BLADES == 1
  1175.           RESET_COLORS = 2,
  1176.           #else
  1177.           COPY_COLORS = 2,
  1178.           RESET_COLORS = 3,
  1179.           #endif
  1180.         };
  1181.         enum ColorOptions {
  1182.           COLOR_LIST_MODE = 0,
  1183.           COLOR_HUE_MODE = 1,
  1184.           WHITE_MODE = 2,
  1185.           BLACK_MODE = 3,
  1186.         };
  1187.  
  1188.         enum SettingsMenu {
  1189.           EDIT_VOLUME = 1,
  1190.           EDIT_CONTROL_SETTINGS = 2,
  1191.           EDIT_CLASH_THRESHOLD = 3,
  1192.           EDIT_BRIGHTNESS = 5,
  1193.         };
  1194.  
  1195.         // Gesture Control Boolean Menu
  1196.         void EnterBooleanMenu(MenuType t, bool current_value) {
  1197.           menu_type_ = t;
  1198.           choice_ = current_value;
  1199.           sound_library_.SayBool(current_value);
  1200.         }
  1201.  
  1202.         int GetStyleNumber(int blade_num) {
  1203.           const char* tmp;
  1204.           tmp = current_preset_.GetStyle(blade_num);
  1205.           int style_num = FirstWord(tmp, "builtin") ? atoi(SkipWord(tmp)) : 0;
  1206.           return style_num;
  1207.         }
  1208.  
  1209.         void ChangeStyleNumber(int blade_num, int direction) {
  1210.           int num_presets = current_config->num_presets;
  1211.           int style_num = GetStyleNumber(blade_num);
  1212.           style_num += direction;
  1213.           if (style_num < 0) style_num = num_presets - 1;
  1214.           if (style_num >= num_presets) style_num = 0;
  1215.           char style_arg[10];
  1216.           itoa(style_num, style_arg, 10);
  1217.           current_preset_.SetStyle(blade_num, style_parser.SetArgument(current_preset_.GetStyle(blade_num), 1, style_arg));
  1218.         }
  1219.  
  1220.         void ChangeStyleNumberAllBlades(int direction) {
  1221.           for (int i = 1; i <= NUM_BLADES - 1; i++) {
  1222.             ChangeStyleNumber(i, direction);
  1223.           }
  1224.           current_preset_.Save();
  1225.           UpdateStyle();
  1226.         }
  1227.  
  1228.         // Uses font_num_ value for starting font, not current font
  1229.         void ChangeFont(int direction) {
  1230.           int num_fonts = RunCommandAndGetSingleLine("list_fonts", nullptr, 0, 0, 0);
  1231.           font_num_ += direction;
  1232.           if (font_num_ > num_fonts) font_num_ = 1;
  1233.           if (font_num_ <= 0) font_num_ = num_fonts;
  1234.           char font[128];
  1235.           RunCommandAndGetSingleLine("list_fonts", nullptr, font_num_, font, sizeof(font));
  1236.           strcat(font, ";common");
  1237.           current_preset_.font = mkstr(font);
  1238.           current_preset_.Save();
  1239.           SetPresetFast(current_preset_.preset_num);
  1240.           hybrid_font.SB_Effect(EFFECT_NEWFONT, 0);
  1241.         }
  1242.  
  1243.         int GetNumberOfPresets() {
  1244.           CurrentPreset tmp;
  1245.           tmp.SetPreset(-1);
  1246.           return tmp.preset_num + 1;
  1247.         }
  1248.  
  1249.         #ifdef FETT263_EDIT_MODE_MENU
  1250.         // Color Edit Helper Functions
  1251.         void SaveColorEdit() {
  1252.           menu_type_ = MENU_COLOR_MODE;
  1253.           edit_color_ = false;
  1254.           twist_menu_ = M_PI / 4;
  1255.           NewColor(blade_num_, effect_num_);
  1256.           current_preset_.Save();
  1257.           color_mode_ = NONE;
  1258.         }
  1259.  
  1260.         void RevertColorEdit() {
  1261.           menu_type_ = MENU_COLOR_MODE;
  1262.           edit_color_ = false;
  1263.           twist_menu_ = M_PI / 4;
  1264.           color_mode_ = NONE;
  1265.         }
  1266.  
  1267.         void StartEditMode() {
  1268.           if (track_player_)
  1269.           {
  1270.             StopTrackPlayer();
  1271.           }
  1272.  
  1273.           if (!SFX_medit)
  1274.           {
  1275.             talkie.Say(talkie_error_in_15, 15);
  1276.             talkie.Say(talkie_font_directory_15, 15);
  1277.             STDOUT.println("Edit Mode prompts missing");
  1278.           }
  1279.           else
  1280.           {
  1281.             STDOUT.println("Enter Edit Mode");
  1282.             GenerateIniFiles();
  1283.             sound_library_.SayEditMode();
  1284.             StartMenu(MENU_TOP);
  1285.             menu_top_pos_ = 0;
  1286.             FastOn();
  1287.           }
  1288.         }
  1289.         #endif
  1290.  
  1291.         void CopyPreset(bool announce) {
  1292.           int32_t pos = current_preset_.preset_num;
  1293.           current_preset_.preset_num = -1;
  1294.           current_preset_.SaveAt(pos);
  1295.           if (announce)
  1296.           {
  1297.             sound_library_.SayCopyPreset();
  1298.             wav_player.Free();
  1299.           }
  1300.         }
  1301.  
  1302.         // Check if ShowColor for ColorChange / Color Editing is active to prevent other events
  1303.         bool CheckShowColorCC() {
  1304.           if (color_mode_ == CC_COLOR_LIST || color_mode_ == CC_ZOOM_COLOR) return true;
  1305.           if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) return true;
  1306.           return false;
  1307.         }
  1308.  
  1309.         // Check to see if ShowColor style is being used and use MenuUndo to properly close if button presses or holds not in menu are used
  1310.         bool CancelShowColor() {
  1311.           if (color_mode_ != NONE)
  1312.           {
  1313.             if (CheckShowColorCC())
  1314.             {
  1315.               EndColorZoom();
  1316.               return true;
  1317.             }
  1318.             else
  1319.             {
  1320.               MenuUndo();
  1321.               return true;
  1322.             }
  1323.           }
  1324.           else
  1325.           {
  1326.             switch (menu_type_) {
  1327.               #ifdef FETT263_EDIT_MODE_MENU
  1328.               case MENU_LENGTH:
  1329.               #if NUM_BLADES > 1
  1330.               case MENU_BLADE_STYLE:
  1331.               case MENU_BLADE_COLOR:
  1332.               case MENU_BLADE_COPY:
  1333.               case MENU_BLADE_LENGTH:
  1334.               case MENU_COPY_COLOR:
  1335.               #endif
  1336.               MenuUndo();
  1337.               return true;
  1338.               break;
  1339.               #endif
  1340.               default:
  1341.               return false;
  1342.               break;
  1343.             }
  1344.           }
  1345.         }
  1346.  
  1347.         bool DoColorZoom() {
  1348.           if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_SMOOTH)
  1349.           {
  1350.             SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_ZOOMED);
  1351.             return true;
  1352.           }
  1353.  
  1354.           if (color_mode_ == COLOR_LIST || color_mode_ == CC_COLOR_LIST)
  1355.           {
  1356.             hsl_ = Color16(color_list_[dial_].color).toHSL();
  1357.           }
  1358.  
  1359.           switch (color_mode_) {
  1360.             case COLOR_LIST:
  1361.             case EDIT_COLOR:
  1362.             color_mode_ = ZOOM_COLOR;
  1363.             hsl_angle_ = fusor.angle2();
  1364.             return true;
  1365.             break;
  1366.             case CC_COLOR_LIST:
  1367.             color_mode_ = CC_ZOOM_COLOR;
  1368.             edit_color_ = true;
  1369.             hsl_angle_ = fusor.angle2();
  1370.             return true;
  1371.             break;
  1372.             default:
  1373.             return false;
  1374.             break;
  1375.           }
  1376.         }
  1377.  
  1378.         // If Color Zoom mode is active save NewColor and end ShowColor style
  1379.         bool EndColorZoom() {
  1380.           #ifndef DISABLE_COLOR_CHANGE
  1381.           if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_ZOOMED)
  1382.           {
  1383.             ToggleColorChangeMode();
  1384.             return true;
  1385.           }
  1386.           #endif
  1387.           switch(color_mode_) {
  1388.             case ZOOM_COLOR:
  1389.             case EDIT_COLOR:
  1390.             case COLOR_LIST:
  1391.             MenuChoice();
  1392.             return true;
  1393.             break;
  1394.             case CC_COLOR_LIST:
  1395.             case CC_ZOOM_COLOR:
  1396.             edit_color_ = false;
  1397.             NewColor(1, BASE_COLOR_ARG);
  1398.             current_preset_.Save();
  1399.             show_color_all_.Stop();
  1400.             UpdateStyle();
  1401.             wav_player.Free();
  1402.             return true;
  1403.             break;
  1404.             default:
  1405.             return false;
  1406.             break;
  1407.           }
  1408.         }
  1409.  
  1410.         // Start Menu Mode
  1411.         void StartMenu(MenuType menu) {
  1412.           current_menu_angle_ = fusor.angle2();
  1413.           menu_type_ = menu;
  1414.           menu_ = true;
  1415.         }
  1416.  
  1417.         // Edit Mode Menu Select (Aux Button)
  1418.         void MenuChoice() {
  1419.           switch (menu_type_) {
  1420.             case MENU_PRESET:
  1421.             sound_library_.SaySelect();
  1422.             menu_type_ = MENU_TOP;
  1423.             menu_ = false;
  1424.             break;
  1425.             #ifdef FETT263_SAVE_CHOREOGRAPHY
  1426.             case MENU_REHEARSE:
  1427.             if (choice_)
  1428.             {
  1429.               menu_ = false;
  1430.               BeginRehearsal();
  1431.             }
  1432.             else
  1433.             {
  1434.               MenuExit();
  1435.             }
  1436.             break;
  1437.             #endif
  1438.             case MENU_VOLUME:
  1439.             if (SaberBase::IsOn())
  1440.             {
  1441.               #ifdef FETT263_EDIT_MODE_MENU
  1442.               menu_type_ = MENU_SETTING_SUB;
  1443.               #endif
  1444.               MenuSave();
  1445.             }
  1446.             else
  1447.             {
  1448.               MenuExit();
  1449.             }
  1450.             break;
  1451.             #ifdef FETT263_EDIT_MODE_MENU
  1452.             case MENU_TOP:
  1453.             switch (menu_top_pos_) {
  1454.  
  1455.               case EDIT_COLORS:
  1456.               ToggleColorChangeMode();
  1457.               menu_type_ = MENU_COLOR;
  1458.               break;
  1459.  
  1460.               case EDIT_BLADE_LENGTH:
  1461.               sound_library_.SayEditBladeLength();
  1462.               #if (NUM_BLADES >= 2)
  1463.               menu_type_ = MENU_LENGTH;
  1464.               SaveState(current_preset_.preset_num);
  1465.               blade_num_ = 1;
  1466.               max_length_ = GetMaxBladeLength(blade_num_);
  1467.               blade_length_ = GetBladeLength(blade_num_);
  1468.               if (blade_length_ < 1) blade_length_ = max_length_;
  1469.               length_revert_ = blade_length_;
  1470.               LengthPreview(blade_num_);
  1471.               #endif
  1472.               break;
  1473.  
  1474.               case EXIT_MENU:
  1475.               sound_library_.SaySelect();
  1476.               MenuExit();
  1477.               break;
  1478.             }
  1479.             break;
  1480.             #if NUM_BLADES > 1
  1481.             case MENU_BLADE_STYLE:
  1482.             if (blade_num_ == 0) {
  1483.               sound_library_.SaySelectBlade();
  1484.               break;
  1485.             }
  1486.             show_preview_.Stop(blade_preview_);
  1487.             blade_preview_ = 0;
  1488.             menu_sub_pos_ = 0;
  1489.             menu_type_ = MENU_STYLE_SUB;
  1490.             sound_library_.SaySelectOption();
  1491.             break;
  1492.             case MENU_BLADE_COLOR:
  1493.             #if NUM_BLADES > 2
  1494.             copy_blade_ = blade_num_;
  1495.             sound_library_.SayColorMenu();
  1496.             #else
  1497.             if (blade_num_ == 1)
  1498.             {
  1499.               copy_blade_ = 2;
  1500.             }
  1501.             else
  1502.             {
  1503.               copy_blade_ = 1;
  1504.             }
  1505.             sound_library_.SaySelectOption();
  1506.             #endif
  1507.             menu_type_ = MENU_COLOR_SUB;
  1508.             menu_sub_pos_ = 0;
  1509.             show_preview_.Stop(blade_preview_);
  1510.             blade_preview_ = 0;
  1511.             break;
  1512.             case MENU_BLADE_COPY:
  1513.             menu_type_ = MENU_COPY_COLOR;
  1514.             CopyColors();
  1515.             MenuConfirm();
  1516.             break;
  1517.  
  1518.             case MENU_COPY_COLOR:
  1519.             if (choice_)
  1520.             {
  1521.               current_preset_.SetStyle(copy_blade_, style_parser.CopyArguments(current_preset_.GetStyle(blade_num_), current_preset_.GetStyle(copy_blade_), int_arg_menu_, NELEM(int_arg_menu_)));
  1522.               DOVALIDATE(current_preset_);
  1523.               current_preset_.Save();
  1524.               DOVALIDATE(current_preset_);
  1525.               show_preview_.Stop(blade_preview_);
  1526.               UpdateStyle();
  1527.               menu_type_ = MENU_COLOR_SUB;
  1528.               MenuSelect();
  1529.               choice_ = false;
  1530.               blade_preview_ = 0;
  1531.               break;
  1532.             }
  1533.             sound_library_.SayConfirm();
  1534.             break;
  1535.             #endif
  1536.  
  1537.             case MENU_STYLE_SUB:
  1538.             switch (menu_sub_pos_) {
  1539.               case EDIT_STYLE_SETTINGS:
  1540.               effect_num_ = 0;
  1541.               menu_type_ = MENU_STYLE_SETTING_SUB;
  1542.               sound_library_.SaySelectOption();
  1543.               arg_dial_ = -1;
  1544.               break;
  1545.  
  1546.               default:
  1547.               sound_library_.SaySelectOption();
  1548.             }
  1549.             break;
  1550.  
  1551.             case MENU_COLOR:
  1552.             if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE)
  1553.             {
  1554.               ToggleColorChangeMode();
  1555.             }
  1556.             menu_type_ = MENU_TOP;
  1557.             MenuExit();
  1558.             break;
  1559.  
  1560.             case MENU_LENGTH:
  1561.             menu_type_ = MENU_TOP;
  1562.             current_preset_.Save();
  1563.             show_length_.Stop(blade_num_);
  1564.             UpdateStyle();
  1565.             MenuExit();
  1566.             break;
  1567.  
  1568.             case MENU_EXIT:
  1569.             sound_library_.SaySelect();
  1570.             MenuExit();
  1571.             break;
  1572.  
  1573.             #endif
  1574.             default:
  1575.             break;
  1576.           }
  1577.         }
  1578.         // end menu choice
  1579.  
  1580.         void MenuDialIncrement(int direction) {
  1581.           switch (menu_type_) {
  1582.             case MENU_PRESET:
  1583.             #ifdef FETT263_EDIT_MODE_MENU
  1584.             case MENU_FONT:
  1585.             case MENU_TRACK:
  1586.             case MENU_LENGTH:
  1587.             #endif
  1588.             direction *= 5;
  1589.             break;
  1590.  
  1591.             default:
  1592.             break;
  1593.           }
  1594.           MenuDial(direction);
  1595.         }
  1596.  
  1597.         // Edit Mode Dial
  1598.         void MenuDial(int direction) {
  1599.           switch (menu_type_) {
  1600.             default:
  1601.             break;
  1602.  
  1603.             case MENU_PRESET:
  1604.             if (SaberBase::IsOn())
  1605.             {
  1606.               if (direction > 0)
  1607.               {
  1608.                 next_preset_fast();
  1609.               }
  1610.               else
  1611.               {
  1612.                 previous_preset_fast();
  1613.               }
  1614.             }
  1615.             else
  1616.             {
  1617.               int num_presets = GetNumberOfPresets();
  1618.               int preset = (current_preset_.preset_num + num_presets * 10 + direction) % num_presets;
  1619.               SelectPreset(preset);
  1620.             }
  1621.             break;
  1622.  
  1623.             case MENU_VOLUME:
  1624.             if (direction > 0)
  1625.             {
  1626.               VolumeUp();
  1627.             }
  1628.             else
  1629.             {
  1630.               VolumeDown();
  1631.             }
  1632.             break;
  1633.  
  1634.             #ifdef FETT263_EDIT_MODE_MENU
  1635.             case MENU_TOP:
  1636.             menu_top_pos_ += direction;
  1637.             if (menu_top_pos_ <= 0) menu_top_pos_ = SUBMENUS;
  1638.             if (menu_top_pos_ > SUBMENUS) menu_top_pos_ = 1;
  1639.             switch (menu_top_pos_) {
  1640.               case EDIT_COLORS:
  1641.               sound_library_.SayEditColor();
  1642.               break;
  1643.  
  1644.               case EDIT_BLADE_LENGTH:
  1645.               sound_library_.SayEditBladeLength();
  1646.               break;
  1647.  
  1648.               case EXIT_MENU:
  1649.               sound_library_.SayExit();
  1650.               break;
  1651.             }
  1652.             break;
  1653.  
  1654.             #if NUM_BLADES > 1
  1655.             case MENU_BLADE_LENGTH:
  1656.             blade_num_ += direction;
  1657.             if (blade_num_ > NUM_BLADES) blade_num_ = 1;
  1658.             if (blade_num_ <= 0) blade_num_ = NUM_BLADES;
  1659.             BladePreview(blade_num_);
  1660.             sound_library_.SayNumber(blade_num_, SAY_WHOLE);
  1661.             break;
  1662.  
  1663.             break;
  1664.  
  1665.             #else
  1666.             if (menu_sub_pos_ <= 0) menu_sub_pos_ = 3;
  1667.             if (menu_sub_pos_ > 3) menu_sub_pos_ = 1;
  1668.             switch (menu_sub_pos_) {
  1669.               case RESET_COLORS:
  1670.               sound_library_.SayResetColors();
  1671.               break;
  1672.             }
  1673.             #endif
  1674.             break;
  1675.  
  1676.             case MENU_FONT:
  1677.             if (!restore_point.get()) restore_point = std::move(current_preset_.font);
  1678.             ChangeFont(direction);
  1679.             break;
  1680.  
  1681.             case MENU_LENGTH:
  1682.             if (direction>0)
  1683.             {
  1684.               hybrid_font.PlayCommon(&SFX_plus);
  1685.             }
  1686.             else
  1687.             {
  1688.               hybrid_font.PlayCommon(&SFX_minus);
  1689.             }
  1690.  
  1691.             blade_length_ += direction;
  1692.             if (blade_length_ >= max_length_)
  1693.             {
  1694.               sound_library_.SayMaximum();
  1695.               blade_length_ = max_length_;
  1696.             }
  1697.             if (blade_length_ <= 1)
  1698.             {
  1699.               sound_library_.SayMinimum();
  1700.               blade_length_ = 1;
  1701.             }
  1702.             length_edit_length = blade_length_ - 1;
  1703.             SetBladeLength(blade_num_, blade_length_);
  1704.             SaveState(current_preset_.preset_num);
  1705.             break;
  1706.  
  1707.             case MENU_TWISTON:
  1708.             case MENU_TWISTOFF:
  1709.             case MENU_STYLE_SETTING_SUB:
  1710.             for (int i = 0; i < (int)NELEM(int_arg_menu_); i++) {
  1711.               arg_dial_ += direction;
  1712.               if (direction > 0)
  1713.               {
  1714.                 // Detect preon.wav for setting preview (cannot be shown without so skip over)
  1715.                 if (!SFX_preon && (int_arg_menu_[arg_dial_] == PREON_OPTION_ARG || int_arg_menu_[arg_dial_] == PREON_SIZE_ARG)) arg_dial_ = 0;
  1716.                 if (arg_dial_ > (int)NELEM(int_arg_menu_) - 1) arg_dial_ = 0;
  1717.               }
  1718.               else
  1719.               {
  1720.                 // Detect preon.wav for setting preview (cannot be shown without so skip over)
  1721.                 if (!SFX_preon && (int_arg_menu_[arg_dial_] == PREON_OPTION_ARG || int_arg_menu_[arg_dial_] == PREON_SIZE_ARG)) arg_dial_ = NELEM(int_arg_menu_) - 3;
  1722.                 if (arg_dial_ < 0) arg_dial_ = NELEM(int_arg_menu_) - 1;
  1723.               }
  1724.               set_num_ = int_arg_menu_[arg_dial_];
  1725.               if (style_parser.UsesArgument(current_preset_.GetStyle(blade_num_), set_num_ + 2)) break;
  1726.             }
  1727.  
  1728.             #endif
  1729.           }
  1730.         }
  1731.  
  1732.         //end of EditMode Dial
  1733.  
  1734.         // Edit Mode Undo (POWER Button)
  1735.         void MenuUndo() {
  1736.           switch (menu_type_) {
  1737.             case MENU_TOP:
  1738.             sound_library_.SayExit();
  1739.             MenuExit();
  1740.             break;
  1741.  
  1742.             #ifdef FETT263_EDIT_MODE_MENU
  1743.             case MENU_VOLUME:
  1744.             menu_type_ = MENU_SETTING_SUB;
  1745.             dynamic_mixer.set_volume(VOLUME);
  1746.             MenuRevert();
  1747.             break;
  1748.  
  1749.             #if NUM_BLADES > 1
  1750.  
  1751.             case MENU_BLADE_LENGTH:
  1752.             menu_type_ = MENU_SETTING_SUB;
  1753.             show_preview_.Stop(blade_preview_);
  1754.             blade_preview_ = 0;
  1755.             MenuCancel();
  1756.             break;
  1757.  
  1758.             #endif
  1759.  
  1760.             case MENU_COLOR:
  1761.             ToggleColorChangeMode();
  1762.             SaberBase::SetVariation(0);
  1763.             current_preset_.SetStyle(blade_num_, style_parser.CopyArguments("~", current_preset_.GetStyle(blade_num_),  int_arg_menu_, NELEM(int_arg_menu_)));
  1764.             current_preset_.Save();
  1765.             UpdateStyle();
  1766.             menu_type_ = MENU_TOP;
  1767.             MenuRevert();
  1768.             MenuExit();
  1769.             break;
  1770.  
  1771.             case MENU_COLOR_SUB:
  1772.             #if NUM_BLADES == 1
  1773.             menu_type_ = MENU_TOP;
  1774.             MenuCancel();
  1775.             break;
  1776.  
  1777.             #else
  1778.             menu_type_ = MENU_BLADE_COLOR;
  1779.             show_preview_.Stop(blade_preview_);
  1780.             blade_preview_ = 0;
  1781.             blade_num_ = 0;
  1782.             MenuCancel();
  1783.             break;
  1784.  
  1785.             #endif
  1786.             case MENU_COLOR_MODE:
  1787.             edit_color_ = false;
  1788.             menu_type_ = MENU_EFFECT;
  1789.             MenuCancel();
  1790.             break;
  1791.  
  1792.             case MENU_LENGTH:
  1793.             menu_type_ = MENU_TOP;
  1794.             SetBladeLength(blade_num_, length_revert_);
  1795.             SaveState(current_preset_.preset_num);
  1796.             MenuRevert();
  1797.             show_length_.Stop(blade_num_);
  1798.             UpdateStyle();
  1799.             MenuExit();
  1800.             break;
  1801.  
  1802.             case MENU_EXIT:
  1803.             default:
  1804.             choice_ = false;
  1805.             menu_type_ = MENU_TOP;
  1806.             MenuCancel();
  1807.             break;
  1808.             #endif
  1809.           }
  1810.         }
  1811.         // End of Edit MenuUndo
  1812.  
  1813.         // Exit Edit Mode
  1814.         void MenuExit() {
  1815.           switch (menu_type_) {
  1816.  
  1817.             case MENU_PRESET:
  1818.             first_preset();
  1819.             sound_library_.SaySelect();
  1820.             break;
  1821.  
  1822.             case MENU_VOLUME:
  1823.             if (SFX_vmend)
  1824.             {
  1825.               sound_library_.SayVolumeMenuEnd();
  1826.             }
  1827.             else
  1828.             {
  1829.               sound_library_.SayExit();
  1830.             }
  1831.             break;
  1832.  
  1833.             default:
  1834.             break;
  1835.           }
  1836.           menu_type_ = MENU_TOP;
  1837.           menu_ = false;
  1838.           wav_player.Free();
  1839.         }
  1840.  
  1841.         void MenuSave() {
  1842.           sound_library_.SaySave();
  1843.           //MenuPrompt();
  1844.         }
  1845.  
  1846.         void MenuSelect() {
  1847.           sound_library_.SaySelect();
  1848.           MenuPrompt();
  1849.         }
  1850.  
  1851.         void MenuConfirm() {
  1852.           sound_library_.SayConfirm();
  1853.           MenuPrompt();
  1854.         }
  1855.  
  1856.         void MenuCancel() {
  1857.           sound_library_.SayCancel();
  1858.           MenuPrompt();
  1859.         }
  1860.  
  1861.         void MenuRevert() {
  1862.           sound_library_.SayRevert();
  1863.           //MenuPrompt();
  1864.         }
  1865.  
  1866.         void MenuPrompt() {
  1867.           switch (menu_type_) {
  1868.             case MENU_TOP:
  1869.             sound_library_.SayMainMenu();
  1870.             break;
  1871.  
  1872.             default:
  1873.             break;
  1874.           }
  1875.         }
  1876.  
  1877.         enum EditColorMode {
  1878.           NONE,
  1879.           CC_COLOR_LIST,
  1880.           CC_ZOOM_COLOR,
  1881.           COLOR_LIST,
  1882.           EDIT_COLOR,
  1883.           EDIT_BLACK,
  1884.           EDIT_WHITE,
  1885.           ZOOM_COLOR,
  1886.         };
  1887.  
  1888.         bool Parse(const char *cmd, const char* arg) override {
  1889.           if (PropBase::Parse(cmd, arg)) return true;
  1890.           if (!strcmp(cmd, "list_current_tracks"))
  1891.           {
  1892.             // Tracks must be in: font/tracks/*.wav
  1893.             LOCK_SD(true);
  1894.             for (const char* dir = current_directory; dir; dir = next_current_directory(dir)) {
  1895.               PathHelper path(dir, "tracks");
  1896.               ListTracks(path);
  1897.             }
  1898.             LOCK_SD(false);
  1899.             return true;
  1900.           }
  1901.           if (!strcmp(cmd, "get_gesture") && arg)
  1902.           {
  1903.             saved_gesture_control.Print(arg);
  1904.             return true;
  1905.           }
  1906.           if (!strcmp(cmd, "set_gesture") && arg)
  1907.           {
  1908.             saved_gesture_control.Set(arg);
  1909.             SaveGestureState();
  1910.             return true;
  1911.           }
  1912.           #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1913.           if (!strcmp(cmd, "left") || !strcmp(cmd, "l"))
  1914.           {
  1915.             MenuDial(-1);
  1916.             return true;
  1917.           }
  1918.           if (!strcmp(cmd, "right") || !strcmp(cmd, "r"))
  1919.           {
  1920.             MenuDial(1);
  1921.             return true;
  1922.           }
  1923.           #endif
  1924.           return false;
  1925.         }
  1926.  
  1927.         void ToggleBattleMode() {
  1928.           if (!battle_mode_)
  1929.           {
  1930.             battle_mode_ = true;
  1931.             if (SFX_bmbegin)
  1932.             {
  1933.               hybrid_font.PlayCommon(&SFX_bmbegin);
  1934.             }
  1935.             else
  1936.             {
  1937.               hybrid_font.DoEffect(EFFECT_FORCE, 0);
  1938.             }
  1939.           }
  1940.           else
  1941.           {
  1942.             battle_mode_ = false;
  1943.             if (SFX_bmend)
  1944.             {
  1945.               hybrid_font.PlayCommon(&SFX_bmend);
  1946.             }
  1947.             else
  1948.             {
  1949.               beeper.Beep(0.5, 3000);
  1950.             }
  1951.           }
  1952.         }
  1953.  
  1954.         void ToggleMultiBlast() {
  1955.           swing_blast_ = !swing_blast_;
  1956.           if (swing_blast_)
  1957.           {
  1958.             if (SFX_blstbgn)
  1959.             {
  1960.               hybrid_font.PlayCommon(&SFX_blstbgn);
  1961.             }
  1962.             else
  1963.             {
  1964.               hybrid_font.SB_Effect(EFFECT_BLAST, 0);
  1965.             }
  1966.           }
  1967.           else
  1968.           {
  1969.             if (SFX_blstend)
  1970.             {
  1971.               hybrid_font.PlayCommon(&SFX_blstend);
  1972.             }
  1973.             else
  1974.             {
  1975.               hybrid_font.SB_Effect(EFFECT_BLAST, 0);
  1976.             }
  1977.           }
  1978.         }
  1979.  
  1980.         void ToggleBattleModeMultiBlast() {
  1981.           if (battle_mode_ && !swing_blast_) {
  1982.             check_blast_ = true;
  1983.             last_blast_millis_ = millis();
  1984.           }
  1985.           swing_blast_ = false;
  1986.         }
  1987.  
  1988.         void DoAutoMultiBlast() {
  1989.           if (check_blast_)
  1990.           {
  1991.             if (millis() - last_blast_millis_ < 2000)
  1992.             {
  1993.               swing_blast_ = true;
  1994.               SaberBase::DoBlast();
  1995.             }
  1996.             check_blast_ = false;
  1997.           }
  1998.         }
  1999.         void DoIgnition() {
  2000.           #ifdef FETT263_DUAL_MODE_SOUND
  2001.           SelectIgnitionSound();
  2002.           #endif
  2003.           if (SFX_preon)
  2004.           {
  2005.             #ifdef FETT263_DUAL_MODE_SOUND
  2006.             SelectPreonSound();
  2007.             #endif
  2008.             On();
  2009.           }
  2010.           else
  2011.           {
  2012.             FastOn();
  2013.           }
  2014.           #ifdef FETT263_BATTLE_MODE_ALWAYS_ON
  2015.           battle_mode_ = true;
  2016.           #endif
  2017.           #ifdef FETT263_BATTLE_MODE_START_ON
  2018.           battle_mode_ = true;
  2019.           #endif
  2020.         }
  2021.  
  2022.         #ifdef FETT263_DUAL_MODE_SOUND
  2023.         // Select preon wav based on blade orientation if more than 1 file, up = odd, down = even
  2024.         void SelectPreonSound() {
  2025.           SelectSFXEvenOdd(&SFX_preon);
  2026.         }
  2027.  
  2028.         // Select ignition wav based on blade orientation, up = odd, down = even
  2029.         void SelectIgnitionSound() {
  2030.           Effect* effect;
  2031.           if (SFX_poweron)
  2032.           {
  2033.             effect = &SFX_poweron;
  2034.           }
  2035.           else
  2036.           {
  2037.             effect = &SFX_out;
  2038.           }
  2039.           SelectSFXEvenOdd(effect);
  2040.         }
  2041.  
  2042.         // Select retraction wav based on blade orientation, up = odd, down = even
  2043.         // Do not mix poweroff.wav and pwroff.wav files in font or selection will not work
  2044.         void SelectRetractionSound() {
  2045.           Effect* effect;
  2046.           if (SFX_poweroff)
  2047.           {
  2048.             effect = &SFX_poweroff;
  2049.           }
  2050.           else if (SFX_pwroff)
  2051.           {
  2052.             effect = &SFX_pwroff;
  2053.           }
  2054.           else
  2055.           {
  2056.             effect = &SFX_in;
  2057.           }
  2058.           SelectSFXEvenOdd(effect);
  2059.           if (SFX_pstoff) SelectSFXEvenOdd(&SFX_pstoff);
  2060.         }
  2061.  
  2062.         // Select wav file based on blade orientation, up = odd, down = even
  2063.         void SelectSFXEvenOdd(Effect* effect) {
  2064.           int f = effect->files_found();
  2065.           if (f > 1)
  2066.           {
  2067.             if (fusor.angle1() > 0)
  2068.             {
  2069.               f = (rand() % ((f + 1)/ 2)) * 2;
  2070.             }
  2071.             else
  2072.             {
  2073.               f = 1 + (rand() % (f / 2)) * 2;
  2074.             }
  2075.             effect->Select(f);
  2076.           }
  2077.         }
  2078.         #endif
  2079.  
  2080.         void ForceQuote() {
  2081.           if (force_quote_) {
  2082.             SFX_quote.SelectNext();
  2083.             hybrid_font.PlayCommon(&SFX_quote);
  2084.           } else {
  2085.             SaberBase::DoForce();
  2086.           }
  2087.         }
  2088.  
  2089.         // Battery Level
  2090.         void DoBattery() {
  2091.           SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0);
  2092.         }
  2093.  
  2094.         // Go to first Preset.
  2095.         void first_preset() {
  2096.           #ifdef SAVE_PRESET
  2097.           SaveState(0);
  2098.           #endif
  2099.           SetPreset(0, true);
  2100.         }
  2101.  
  2102.         // SA22C Volume Menu
  2103.         void VolumeUp() {
  2104.           STDOUT.println("Volume up");
  2105.           if (dynamic_mixer.get_volume() < VOLUME)
  2106.           {
  2107.             dynamic_mixer.set_volume(std::min<int>(VOLUME + VOLUME * 0.1,
  2108.               dynamic_mixer.get_volume() + VOLUME * 0.10));
  2109.               hybrid_font.PlayCommon(&SFX_volup);
  2110.               STDOUT.print("Current Volume: ");
  2111.               STDOUT.println(dynamic_mixer.get_volume());
  2112.             }
  2113.             else
  2114.             {
  2115.               hybrid_font.PlayCommon(&SFX_volmax);
  2116.             }
  2117.           }
  2118.  
  2119.           void VolumeDown() {
  2120.             STDOUT.println("Volume Down");
  2121.             if (dynamic_mixer.get_volume() > (0.10 * VOLUME))
  2122.             {
  2123.               dynamic_mixer.set_volume(std::max<int>(VOLUME * 0.1,
  2124.                 dynamic_mixer.get_volume() - VOLUME * 0.10));
  2125.                 hybrid_font.PlayCommon(&SFX_voldown);
  2126.                 STDOUT.print("Current Volume: ");
  2127.                 STDOUT.println(dynamic_mixer.get_volume());
  2128.               }
  2129.               else
  2130.               {
  2131.                 hybrid_font.PlayCommon(&SFX_volmin);
  2132.               }
  2133.             }
  2134.  
  2135.             // S3 - Soundbank Selection / Preset Menu
  2136.             void PresetMenu(bool preset_forward_)
  2137.             {
  2138.               if(preset_forward_)
  2139.               {
  2140.                 next_preset();
  2141.               }
  2142.               else
  2143.               {
  2144.                 previous_preset();
  2145.               }
  2146.             }
  2147.  
  2148.             RefPtr<BufferedWavPlayer> wav_player;
  2149.             float current_menu_angle_ = 0.0;
  2150.             float current_twist_angle_ = 0.0;
  2151.             float clash_t_ = 2.00;
  2152.             #ifdef FETT263_EDIT_MODE_MENU
  2153.             LSPtr<char> restore_point;
  2154.             int blade_length_;
  2155.             int max_length_;
  2156.             float dim;
  2157.             float dim_revert_;
  2158.             #endif
  2159.  
  2160.             bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
  2161.               {
  2162.                 switch (EVENTID(button, event, modifiers))
  2163.                 {
  2164.                   case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ON):
  2165.                   case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_ON):
  2166.                   if (accel_.x < -0.15)
  2167.                   {
  2168.                     pointing_down_ = true;
  2169.                   }
  2170.  
  2171.                   else
  2172.                   {
  2173.                     pointing_down_ = false;
  2174.                   }
  2175.                   return true;
  2176.  
  2177.                   // S3 - Saber ON , Volume Down & Preset Menu foward
  2178.                   case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_OFF):
  2179.                   case EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF):
  2180.  
  2181.                   //Volume Down if in mode volume
  2182.                   if (mode_volume_)
  2183.                   {
  2184.                     VolumeDown();
  2185.                   }
  2186.                   else
  2187.                   {
  2188.                     //Checks if in preset menu and moves preset forward
  2189.                     if(preset_menu_)
  2190.                     {
  2191.                       preset_forward_= false;
  2192.                       PresetMenu(preset_forward_);
  2193.                       return true;
  2194.                     }
  2195.  
  2196.                     //Checks if blade is not detected and plays no blade sound
  2197.                     if(!blade_detected_)
  2198.                     {
  2199.                       Off();
  2200.                       hybrid_font.PlayCommon(&SFX_no_blade);
  2201.                       return true;
  2202.                     }
  2203.                     //Saber only turns on out of preset menu
  2204.                     else
  2205.                     {
  2206.                       aux_on_ = false;
  2207.                       On();
  2208.                     }
  2209.                   }
  2210.  
  2211.                   return true;
  2212.  
  2213.                   // S3 - Battery Indicator & Volume Up
  2214.                   case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_OFF):
  2215.                   case EVENTID(BUTTON_AUX, EVENT_CLICK_LONG, MODE_OFF):
  2216.  
  2217.                   // Volume Up if in Volume Mode
  2218.                   if(mode_volume_)
  2219.                   {
  2220.                     VolumeUp();
  2221.                   }
  2222.                   else
  2223.                   {
  2224.                     //checks if saber is in preset menu and moves preset forward
  2225.                     if (preset_menu_)
  2226.                     {
  2227.                       preset_forward_ = true;
  2228.                       PresetMenu(preset_forward_);
  2229.                     }
  2230.  
  2231.                     //if saber not in preset menu and off, display battery level
  2232.                     else
  2233.                     {
  2234.                        SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0);
  2235.                     }
  2236.                   }
  2237.                   return true;
  2238.  
  2239.                   //S3 - Enter Volume Menu
  2240.                   case EVENTID(BUTTON_AUX, EVENT_HELD_LONG, MODE_OFF | BUTTON_POWER):
  2241.                   if (!mode_volume_ && !preset_menu_)
  2242.                   {
  2243.                     mode_volume_ = true;
  2244.                     if (SFX_vmbegin)
  2245.                     {
  2246.                       hybrid_font.PlayCommon(&SFX_vmbegin);
  2247.                     }
  2248.                     else
  2249.                     {
  2250.                       beeper.Beep(0.5, 3000);
  2251.                     }
  2252.                   }
  2253.                   else {
  2254.                     mode_volume_ = false;
  2255.                     if (SFX_vmend && !preset_menu_)
  2256.                     {
  2257.                       hybrid_font.PlayCommon(&SFX_vmend);
  2258.                     }
  2259.                     else
  2260.                     {
  2261.                       if(!preset_menu_)
  2262.                       {
  2263.                         beeper.Beep(0.5, 3000);
  2264.                       }
  2265.  
  2266.                     }
  2267.                   }
  2268.                   return true;
  2269.  
  2270.                   //S3 - Mute Mode
  2271.                   case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_AUX):
  2272.                   if(!mode_volume_ && blade_detected_)
  2273.                   {
  2274.                     hybrid_font.PlayCommon(&SFX_mute);
  2275.                     delay(500);
  2276.                     if(SetMute(true))
  2277.                     {
  2278.                       unmute_on_deactivation_ = true;
  2279.                       On();
  2280.                     }
  2281.                     return true;
  2282.  
  2283.                   }
  2284.                   return false;
  2285.  
  2286.                   //S3 - Detects Twist for Saber On
  2287.                   case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF):
  2288.                   if (!saved_gesture_control.gestureon) return true;
  2289.                   if (!saved_gesture_control.twiston) return true;
  2290.                   if(!mode_volume_ && !preset_menu_)
  2291.                   {
  2292.                     //Checks if blade is no detected and plays no blade sound
  2293.                     if(!blade_detected_)
  2294.                     {
  2295.                       Off();
  2296.                       hybrid_font.PlayCommon(&SFX_no_blade);
  2297.                       return true;
  2298.                     }
  2299.                     // Delays twist events to prevent false trigger from over twisting
  2300.                     else if (millis() - last_twist_ > 1000 && millis() - saber_off_time_ > 1000)
  2301.                     {
  2302.                       On();
  2303.                       last_twist_ = millis();
  2304.                     }
  2305.                     return true;
  2306.                   }
  2307.                   return false;
  2308.  
  2309.                   //S3 - Detects Twist for Saber Off
  2310.                   case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON):
  2311.                   if(menu_) return true;
  2312.                   if (!saved_gesture_control.twistoff) return true;
  2313.                   // Checks if blade is not detected and plays no blade sound
  2314.                   if(!blade_detected_)
  2315.                   {
  2316.                     Off();
  2317.                     hybrid_font.PlayCommon(&SFX_no_blade);
  2318.                     return true;
  2319.                   }
  2320.                   else if(!SaberBase::Lockup())
  2321.                   {
  2322.                     if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE && !preset_menu_)
  2323.                     {
  2324.                       // Delay twist events to prevent false trigger from over twisting
  2325.                       if (millis() - last_twist_ > 1000)
  2326.                       {
  2327.                         Off();
  2328.                         last_twist_= millis();
  2329.                       }
  2330.                     }
  2331.                   }
  2332.                   return true;
  2333.  
  2334.                   // Gesture Sleep Toggle
  2335.                   case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF | BUTTON_POWER):
  2336.                   if (!menu_)
  2337.                   {
  2338.                     if (!saved_gesture_control.gestureon)
  2339.                     {
  2340.                       saved_gesture_control.twistoff = true;
  2341.                       saved_gesture_control.gestureon = true;
  2342.                       sound_library_.SayGesturesOn();
  2343.                     }
  2344.                     else
  2345.                     {
  2346.                       saved_gesture_control.gestureon = false;
  2347.                       saved_gesture_control.twistoff = false;
  2348.                       sound_library_.SayGesturesOff();
  2349.                     }
  2350.                     SaveGestureState();
  2351.                   }
  2352.                   return true;
  2353.  
  2354.                   // Gesture Controls (not button specific)
  2355.                   case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_ON):
  2356.                   if (wav_player && wav_player->isPlaying())
  2357.                   {
  2358.                     current_menu_angle_ = fusor.angle2();
  2359.                     return true;
  2360.                   }
  2361.                   if (menu_) MenuDial(1);
  2362.                   return true;
  2363.  
  2364.                   case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_ON):
  2365.                   if (wav_player && wav_player->isPlaying())
  2366.                   {
  2367.                     current_menu_angle_ = fusor.angle2();
  2368.                     return true;
  2369.                   }
  2370.                   if (menu_) MenuDial(-1);
  2371.                   return true;
  2372.  
  2373.                   case EVENTID(BUTTON_NONE, EVENT_TWIST_RIGHT, MODE_OFF):
  2374.                   if (wav_player && wav_player->isPlaying())
  2375.                   {
  2376.                     current_menu_angle_ = fusor.angle2();
  2377.                     return true;
  2378.                   }
  2379.                   if (menu_) MenuDial(1);
  2380.                   return true;
  2381.  
  2382.                   case EVENTID(BUTTON_NONE, EVENT_TWIST_LEFT, MODE_OFF):
  2383.                   if (wav_player && wav_player->isPlaying())
  2384.                   {
  2385.                     current_menu_angle_ = fusor.angle2();
  2386.                     return true;
  2387.                   }
  2388.                   if (menu_) MenuDial(-1);
  2389.                   return true;
  2390.  
  2391.                   // S3 - Blade OFF
  2392.                   case EVENTID(BUTTON_POWER, EVENT_HELD_MEDIUM, MODE_ON):
  2393.                   if(menu_) return false;
  2394.  
  2395.                   //Checks if no blade detected and plays no blade sound
  2396.                   if(!blade_detected_)
  2397.                   {
  2398.                     Off();
  2399.                     hybrid_font.PlayCommon(&SFX_no_blade);
  2400.                     return true;
  2401.                   }
  2402.                   else if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE)
  2403.                   {
  2404.                     Off();
  2405.                     return true;
  2406.                   }
  2407.                   return false;
  2408.  
  2409.                   // S3 - Exits Volume Menu & Enters Soundbank Selection
  2410.                   case EVENTID(BUTTON_AUX, EVENT_HELD_LONG, MODE_OFF):
  2411.                   if(mode_volume_)
  2412.                   {
  2413.                     mode_volume_ = false;
  2414.                     if(SFX_vmend)
  2415.                     {
  2416.                       hybrid_font.PlayCommon(&SFX_vmend);
  2417.                     }
  2418.                     else
  2419.                     {
  2420.                       beeper.Beep(0.5,3000);
  2421.                     }
  2422.                     STDOUT.println("Exit Volume Menu");
  2423.                     return true;
  2424.                   }
  2425.  
  2426.                   // Enters Soundbank Selection / Preset Menu
  2427.                   if(!preset_menu_)
  2428.                   {
  2429.                     preset_menu_ = true;
  2430.                     preset_forward_ = true;
  2431.                     hybrid_font.PlayCommon(&SFX_fontmenu);
  2432.                     delay(2100);
  2433.                     hybrid_font.PlayCommon(&SFX_font);
  2434.                     SaberBase::DoNewFont();
  2435.                   }
  2436.                   else
  2437.                   // Exits Soundbank Selection / Preset Menu
  2438.                   {
  2439.                     preset_menu_ = false;
  2440.                     hybrid_font.PlayCommon(&SFX_boot);
  2441.                   }
  2442.                   return true;
  2443.  
  2444.                   // S3 - Exit Volume Menu, Saber On ,Previous Soundfont & Exit Soundfont
  2445.                   case EVENTID(BUTTON_POWER, EVENT_HELD_LONG, MODE_OFF):
  2446.  
  2447.                   // Exits volume menu
  2448.                   if(mode_volume_)
  2449.                   {
  2450.                     mode_volume_ = false;
  2451.                     if (SFX_vmend)
  2452.                     {
  2453.                       hybrid_font.PlayCommon(&SFX_vmend);
  2454.                     }
  2455.                     else
  2456.                     {
  2457.                       beeper.Beep(0.5, 3000);
  2458.                     }
  2459.                     STDOUT.println("Exit Volume Menu");
  2460.                   }
  2461.                   else
  2462.                   {
  2463.                     // Checks if in preset menu and exits
  2464.                     if(preset_menu_)
  2465.                     {
  2466.                       preset_menu_ = false;
  2467.                       hybrid_font.PlayCommon(&SFX_boot);
  2468.                       return true;
  2469.  
  2470.                     }
  2471.                     else
  2472.                     {
  2473.                       //Checks if blade is not detected and plays no blade sound
  2474.                       if(!blade_detected_)
  2475.                       {
  2476.                         Off();
  2477.                         hybrid_font.PlayCommon(&SFX_no_blade);
  2478.                         return true;
  2479.                       }
  2480.                       //Saber only turns on out of preset menu
  2481.                       else
  2482.                       {
  2483.                         aux_on_ = false;
  2484.                         On();
  2485.                       }
  2486.                     }
  2487.                   }
  2488.                   return true;
  2489.  
  2490.                   // S3 - Start or Stop Track
  2491.                   case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_OFF | BUTTON_POWER):
  2492.                   if(!mode_volume_)
  2493.                   {
  2494.                     StartOrStopTrack();
  2495.                   }
  2496.                   return true;
  2497.  
  2498.                   case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON | BUTTON_POWER):
  2499.                   if (!mode_volume_ && !menu_)
  2500.                   {
  2501.                     StartOrStopTrack();
  2502.                   }
  2503.                   return true;
  2504.  
  2505.                   case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_OFF):
  2506.                   case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_OFF):
  2507.                   SaberBase::RequestMotion();
  2508.                   return true;
  2509.  
  2510.                   // S3 - Saves Menu / Blaster Block
  2511.                   case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON):
  2512.  
  2513.                   // Handles exit/toggle while in KyberDial
  2514.                   if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE
  2515.                   && menu_type_ == MENU_COLOR)
  2516.                   {
  2517.                     MenuChoice();
  2518.                     MenuSave();
  2519.                     menu_time_ = millis();
  2520.                   }
  2521.  
  2522.                   // Saves Settings in Settings Menu
  2523.                   if(menu_)
  2524.                   {
  2525.                     MenuChoice();
  2526.                     MenuSave();
  2527.                     menu_time_ = millis();
  2528.                   }
  2529.  
  2530.                   if (!SaberBase::Lockup()&& !menu_)
  2531.                   {
  2532.                     if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE)
  2533.                     {
  2534.                       if(millis() - menu_time_ > 1000 )
  2535.                       {
  2536.                         DoBlast();
  2537.                       }
  2538.                     }
  2539.                   }
  2540.                   return true;
  2541.  
  2542.                   // S3 - Reverts options on Menu
  2543.                   case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_ON):
  2544.  
  2545.                   // Handles exit/toggle while in KyberDial
  2546.                   if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE
  2547.                   && menu_type_ == MENU_COLOR)
  2548.                   {
  2549.                     MenuUndo();
  2550.                     menu_time_ = millis();
  2551.                   }
  2552.  
  2553.                   if(menu_)
  2554.                   {
  2555.                     MenuUndo();
  2556.                     menu_time_ = millis();
  2557.                   }
  2558.                   return true;
  2559.  
  2560.                   // S3 - Cycles to next settings while in Menu
  2561.                   case EVENTID(BUTTON_AUX, EVENT_HELD_LONG, MODE_ON):
  2562.  
  2563.                   // Handles KyberDial exit/toggle to BladeLength transition
  2564.                   if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE
  2565.                   && menu_type_ == MENU_COLOR)
  2566.                   {
  2567.                     ToggleColorChangeMode();
  2568.                     menu_ = true;
  2569.                   }
  2570.  
  2571.                   // Switching from KyberDial to BladeLength
  2572.                   if(menu_)
  2573.                   {
  2574.                     if(menu_type_ == MENU_COLOR)
  2575.                     {
  2576.                       if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE)
  2577.                       {
  2578.                         ToggleColorChangeMode();
  2579.                       }
  2580.                       menu_type_ = MENU_TOP;
  2581.                       menu_top_pos_ = 2;
  2582.                       MenuChoice();
  2583.                     }
  2584.                     else if(menu_type_ == MENU_LENGTH)
  2585.                     {
  2586.                       MenuChoice();
  2587.                       menu_type_ = MENU_TOP;
  2588.                       menu_top_pos_ = 1;
  2589.                       MenuChoice();
  2590.                     }
  2591.                   }
  2592.                   return true;
  2593.  
  2594.                   // S3- Cycles to next settings while in Menu
  2595.                   case EVENTID(BUTTON_POWER, EVENT_HELD_LONG, MODE_ON):
  2596.  
  2597.                   // Handles KyberDial toggle/exit to BladeLength transition
  2598.                   if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE
  2599.                   && menu_type_ == MENU_COLOR)
  2600.                   {
  2601.                     ToggleColorChangeMode();
  2602.                     menu_ = true;
  2603.                   }
  2604.  
  2605.                   // Switching from KyberDial to BladeLength
  2606.                   if(menu_)
  2607.                   {
  2608.                     if(menu_type_ == MENU_COLOR)
  2609.                     {
  2610.                       if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE)
  2611.                       {
  2612.                         ToggleColorChangeMode();
  2613.                       }
  2614.                       menu_type_ = MENU_TOP;
  2615.                       menu_top_pos_ = 2;
  2616.                       MenuChoice();
  2617.                     }
  2618.                     else if(menu_type_ == MENU_LENGTH)
  2619.                     {
  2620.                       MenuChoice();
  2621.                       menu_type_ = MENU_TOP;
  2622.                       menu_top_pos_ = 1;
  2623.                       MenuChoice();
  2624.                     }
  2625.                   }
  2626.                   return true;
  2627.  
  2628.                   // S3 - Lightning Block
  2629.                   case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD, MODE_ON):
  2630.                   if(menu_) return true;
  2631.                   if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE)
  2632.                   {
  2633.                     SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK);
  2634.                     SaberBase::DoBeginLockup();
  2635.                     return true;
  2636.                   }
  2637.                   return false;
  2638.  
  2639.                   // S3 - Lockup & Drag
  2640.                   case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_AUX):
  2641.  
  2642.                   if (!SaberBase::Lockup())
  2643.                   {
  2644.                     if (pointing_down_)
  2645.                     {
  2646.                       SaberBase::SetLockup(SaberBase::LOCKUP_DRAG);
  2647.                     }
  2648.                     else
  2649.                     {
  2650.                       SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  2651.                     }
  2652.                     SaberBase::DoBeginLockup();
  2653.                     return true;
  2654.                   }
  2655.                   return false;
  2656.  
  2657.                   // S3 - Melt Lockup
  2658.                   case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_ON | BUTTON_AUX):
  2659.                   if (!SaberBase::Lockup())
  2660.                   {
  2661.                     SaberBase::SetLockup(SaberBase::LOCKUP_MELT);
  2662.                     SaberBase::DoBeginLockup();
  2663.                     return true;
  2664.                   }
  2665.                   return false;
  2666.  
  2667.                   // S3 - Toggle color change mode
  2668.                   case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_ON | BUTTON_AUX):
  2669.                   #ifdef FETT263_EDIT_MODE_MENU
  2670.                   // Enters KyberDial
  2671.                   if(!menu_)
  2672.                   {
  2673.                     StartMenu(MENU_TOP);
  2674.                     menu_top_pos_ = 1;
  2675.                     MenuChoice();
  2676.                   }
  2677.                   #else
  2678.                   // If Edit Mode Menu Disabled Save .ini files for SD card editing
  2679.                   if (menu_) return true;
  2680.                   GenerateIniFiles();
  2681.                   sound_library_.SaySave();
  2682.                   #endif
  2683.                   return true;
  2684.  
  2685.                   // S3 - Force
  2686.                   case EVENTID(BUTTON_AUX, EVENT_HELD_MEDIUM, MODE_ON):
  2687.                   if(menu_) return false;
  2688.                   if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE)
  2689.                   {
  2690.                     SaberBase::DoForce();
  2691.                     return true;
  2692.                   }
  2693.                   return false;
  2694.  
  2695.                   // S3 - Detects if blade is present
  2696.                   #ifdef BLADE_DETECT_PIN
  2697.                   case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_ON):
  2698.                   case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_OFF):
  2699.                   // Might need to do something cleaner, but let's try this for now.
  2700.                   blade_detected_ = true;
  2701.                   FindBladeAgain();
  2702.                   SaberBase::DoBladeDetect(true);
  2703.                   if(preset_menu_)
  2704.                   {
  2705.  
  2706.                   }
  2707.                   return true;
  2708.  
  2709.                   // S3 - Detects if blade is absent
  2710.                   case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_ON):
  2711.                   case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_OFF, MODE_ANY_BUTTON | MODE_OFF):
  2712.                   // Might need to do something cleaner, but let's try this for now.
  2713.                   Off();
  2714.                   blade_detected_ = false;
  2715.                   FindBladeAgain();
  2716.                   SaberBase::DoBladeDetect(false);
  2717.                   if(preset_menu_)
  2718.                   {
  2719.  
  2720.                   }
  2721.  
  2722.                   if(menu_)
  2723.                   {
  2724.                     if(menu_type_ == MENU_LENGTH)
  2725.                     {
  2726.                       MenuUndo();
  2727.                     }
  2728.                     menu_ = false;
  2729.                   }
  2730.  
  2731.                   return true;
  2732.  
  2733.                   #endif
  2734.  
  2735.                   // Events that needs to be handled regardless of what other buttons are pressed.
  2736.                   case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON):
  2737.                   case EVENTID(BUTTON_AUX, EVENT_RELEASED, MODE_ANY_BUTTON | MODE_ON):
  2738.                   if (SaberBase::Lockup())
  2739.                   {
  2740.                     SaberBase::DoEndLockup();
  2741.                     SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  2742.                     mode_volume_ = false;
  2743.                     preset_menu_ = false;
  2744.                     return true;
  2745.                   }
  2746.                 }
  2747.                 return false;
  2748.               }
  2749.             }
  2750.  
  2751.             void SB_Effect(EffectType effect, float location) override
  2752.             {
  2753.               switch (effect)
  2754.               {
  2755.                 case EFFECT_BATTERY_LEVEL:
  2756.                 if (SFX_battery)
  2757.                 {
  2758.                   hybrid_font.PlayCommon(&SFX_battery);
  2759.                 }
  2760.                 else
  2761.                 {
  2762.                   beeper.Beep(0.5, 3000);
  2763.                 }
  2764.                 return;
  2765.               }
  2766.             }
  2767.  
  2768.           private:
  2769.             bool swing_blast_ = false; // Multi-Blast Mode (Swing = Blast Deflect)
  2770.             bool check_blast_ = false; // Check if Multi-Blast mode should be enabled (Battle Mode)
  2771.             bool auto_lockup_on_ = false; // Battle Mode Lockup active
  2772.             bool auto_melt_on_ = false; // Battle Mode Melt/Drag active
  2773.             bool battle_mode_ = false; // Battle Mode active
  2774.             bool menu_ = false; // Edit Mode / Menu System active
  2775.  
  2776.             //S3 Preconfiguration variables
  2777.             bool aux_on_ = true;
  2778.             bool pointing_down_ = false;
  2779.             bool mode_volume_ = false;
  2780.             bool max_volume_reached = false;
  2781.             bool min_vol_reached_ = false;
  2782.             bool preset_menu_ = false;
  2783.             bool preset_forward_;
  2784.             uint32_t last_twist_ = millis();
  2785.             uint32_t saber_off_time_ = millis();
  2786.             uint32_t menu_time_ = millis();
  2787.  
  2788.             #ifdef FETT263_QUOTE_PLAYER_START_ON
  2789.             bool force_quote_ = true; // Quote Player active (in place of force effect)
  2790.             #else
  2791.             bool force_quote_ = false; // Quote Player active (in place of force effect)
  2792.             #endif
  2793.             #ifdef FETT263_SAVE_CHOREOGRAPHY
  2794.             bool rehearse_ = false; // Rehearsal Mode active
  2795.             bool choreo_ = false; // Choreography Mode active
  2796.             #endif
  2797.             uint32_t thrust_begin_millis_; // Thrust timer
  2798.             uint32_t push_begin_millis_; // Push timer
  2799.             uint32_t clash_impact_millis_; // Clash timer
  2800.             uint32_t last_push_millis_; // Last Push (to prevent gesture spamming)
  2801.             uint32_t last_blast_millis_; // Last Blast (for Battle Mode Multi-Blast detection)
  2802.             uint32_t saber_off_time_millis_; // Off timer
  2803.             uint32_t restart_millis_; // Used to time restarts to show preon timing.
  2804.             ClashType clash_type_ = CLASH_NONE;
  2805.             MenuType menu_type_ = MENU_TOP;
  2806.             int menu_top_pos_ = 0; // Top menu dial position
  2807.             int menu_sub_pos_ = 0; // Sub menu dial position
  2808.             TrackMode track_mode_ = PLAYBACK_OFF;
  2809.             int track_num_; // Track Number for Track Player
  2810.             int num_tracks_; // Number of Tracks Found
  2811.             int ignite_time_; // Ignition timer for Edit Mode retraction preview
  2812.             int dial_ = -1; // Menu dial "tick"
  2813.             int sub_dial_; // Sub menu dial "tick"
  2814.             int arg_dial_; // Argument Sub menu dial "tick"
  2815.             int gesture_num_;
  2816.             float twist_menu_ = M_PI / 3; // Twist Menu sensitivity
  2817.             bool choice_ = false;
  2818.             // Edit Mode selection confirmation
  2819.             // for True/False control when deleting, disabling/enabling or copying
  2820.             bool off_event_ = false; // Do off event in Edit Mode
  2821.             bool restart_ = false; // Ignite blade after off event in Edit Mode
  2822.             bool next_event_ = false;
  2823.             // Do next event in Edit Mode, allows an action/wav to complete before
  2824.             // "next event" begins, for use with choreography and ignition/retraction previews where menu sound
  2825.             // would otherwise be truncated by change in state
  2826.             EditColorMode color_mode_;
  2827.             bool edit_color_ = false; // Color Editing Mode active
  2828.             float hsl_angle_ = 0.0; // HSL angle for Color Editing
  2829.             int font_num_; // Font number from list_fonts array for use in Edit Mode dial
  2830.             #ifdef FETT263_EDIT_MODE_MENU
  2831.             uint32_t variation_revert_; // Variation revert value
  2832.             Color16 saved_color_;
  2833.             int blade_preview_ = 0; // Blade number for "preview" style
  2834.             int blade_num_; // Active Blade Number for editing
  2835.             int effect_num_; // Effect Arg Number
  2836.             int copy_blade_; // Blade to Copy from
  2837.             int set_num_; // Settings Arg Number
  2838.             int style_revert_; // Original Style Number for Revert
  2839.             int length_revert_; // Original Blade Length for Revert
  2840.             int arg_revert_; // Original Arg vale for Revert
  2841.             // Calculated value for IntArg (Settings Arguments)
  2842.             // used to add or subtract from original saved value by dial
  2843.             int calc_;
  2844.             #endif
  2845.             #ifdef FETT263_SAVE_CHOREOGRAPHY
  2846.             int clash_count_ = -1; // Choreography Mode Clash counter
  2847.             #endif
  2848.           };
  2849.  
  2850.           #endif
  2851.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement