diff --git a/common/saber_base.h b/common/saber_base.h index 0fe5cff9d..ed4f9f8bb 100644 --- a/common/saber_base.h +++ b/common/saber_base.h @@ -78,6 +78,8 @@ extern SaberBase* saberbases; DEFINE_EFFECT(UNJAM) \ DEFINE_EFFECT(PLI_ON) \ DEFINE_EFFECT(PLI_OFF) \ + DEFINE_EFFECT(DESTRUCT) \ + DEFINE_EFFECT(BOOM) \ /* Mini game effects */ \ DEFINE_EFFECT(GAME_START) \ DEFINE_EFFECT(GAME_ACTION1) \ diff --git a/props/blaster.h b/props/blaster.h index db93eba66..5afb6f4b2 100644 --- a/props/blaster.h +++ b/props/blaster.h @@ -17,7 +17,7 @@ Optional #defines on your config file that customize the blaster's behavior. Optional defines: #define ENABLE_BLASTER_AUTO - DEPRECATED. Enable Autofire/rapid fire mode. Please replace with BLASTER_ENABLE_AUTO. #define BLASTER_ENABLE_AUTO - Enable Autofire/rapid fire mode. - #define BLASTER_DEFAULT_MODE - Sets the mode at startup MODE_STUN|MODE_KILL|MODE_AUTO. Defaulst to MODE_STUN. + #define BLASTER_DEFAULT_MODE - Sets the mode at startup MODE_STUN|MODE_KILL|MODE_AUTO. Defaulst to MODE_STUN. #define BLASTER_SHOTS_UNTIL_EMPTY 15 - Whatever number, not defined = unlimited shots. #define BLASTER_JAM_PERCENTAGE - Range 0-100 percent. If this is not defined, random from 0-100%. @@ -29,9 +29,8 @@ This prop manages up to six different buttons. *Dual Buttons Mode* -Buttons: FIRE and MODE This is the "stock" configuration. - -Weapon will always start on the default mode (define with BLASTER_DEFAULT_MODE, - STUN is default). - Default is to powered on if it finds poweron.wav file present or off otherwise. + Weapon will always start on the default mode (define with BLASTER_DEFAULT_MODE, STUN is default). + Default is to start powered OFF if the font has a poweron.wav file, otherwise ON. Fire - Click FIRE. Cycle Modes - Click MODE. @@ -129,7 +128,13 @@ class Blaster : public PROP_INHERIT_PREFIX PropBase { }; BlasterMode blaster_mode = BLASTER_DEFAULT_MODE; - + +#ifdef BLASTER_SHOTS_UNTIL_EMPTY + const int max_shots_ = BLASTER_SHOTS_UNTIL_EMPTY; +#else + const int max_shots_ = -1; +#endif + int GetBlasterMode() const { return blaster_mode; } @@ -159,59 +164,73 @@ class Blaster : public PROP_INHERIT_PREFIX PropBase { } } - bool auto_firing_ = false; - int shots_fired_ = 0; - bool is_jammed_ = false; - -#ifdef BLASTER_SHOTS_UNTIL_EMPTY - const int max_shots_ = BLASTER_SHOTS_UNTIL_EMPTY; -#else - const int max_shots_ = -1; -#endif + bool CheckEmpty() const { + return max_shots_ != -1 && shots_fired_ >= max_shots_; + } - virtual int GetBulletCount() { + int GetBulletCount() { return max_shots_ - shots_fired_; } + virtual bool DoEmpty() { + if (CheckEmpty()) { + SaberBase::DoEffect(EFFECT_EMPTY, 0); // Trigger the empty effect + return true; + } + return false; + } + virtual bool CheckJam(int percent) { int random = rand() % 100; return random < percent; } - virtual void Fire() { -#ifdef ENABLE_MOTION -#ifdef BLASTER_JAM_PERCENTAGE + bool DoJam() { +#if defined(ENABLE_MOTION) && defined(BLASTER_JAM_PERCENTAGE) // If we're already jammed then we don't need to recheck. If we're not jammed then check if we just jammed. is_jammed_ = is_jammed_ ? true : CheckJam(BLASTER_JAM_PERCENTAGE); if (is_jammed_) { SaberBase::DoEffect(EFFECT_JAM, 0); - return; + return true; + } else { + return false; } #endif -#endif + } - if (max_shots_ != -1) { - if (shots_fired_ >= max_shots_) { - SaberBase::DoEffect(EFFECT_EMPTY, 0); - return; - } - } + virtual void DoStun() { + SaberBase::DoEffect(EFFECT_STUN, 0); + shots_fired_++; + } - if (blaster_mode == MODE_AUTO) { - SelectAutoFirePair(); // Set up the autofire pairing if the font suits it. - SaberBase::SetLockup(LOCKUP_AUTOFIRE); - SaberBase::DoBeginLockup(); - auto_firing_ = true; - } else { - if (blaster_mode == MODE_STUN) { - SaberBase::DoEffect(EFFECT_STUN, 0); - } else { - SFX_blast.Select(-1); - SaberBase::DoEffect(EFFECT_FIRE, 0); - } + virtual void DoKill() { + SFX_blast.Select(-1); + SaberBase::DoEffect(EFFECT_FIRE, 0); + shots_fired_++; + } + + virtual void DoAutoFire() { + SelectAutoFirePair(); // Set up the auto-fire pairing if the font suits it + SaberBase::SetLockup(LOCKUP_AUTOFIRE); + SaberBase::DoBeginLockup(); + auto_firing_ = true; + } - shots_fired_++; + virtual void Fire() { + if (DoJam()) return; + if (DoEmpty()) return; + + switch (blaster_mode) { + case MODE_STUN: + DoStun(); + break; + case MODE_KILL: + DoKill(); + break; + case MODE_AUTO: + DoAutoFire(); + break; } } @@ -286,7 +305,7 @@ class Blaster : public PROP_INHERIT_PREFIX PropBase { SetNextAction(what, when * 1000); } - void PollNextAction() { + virtual void PollNextAction() { if (millis() - time_base_ > next_event_time_) { switch (next_action_) { case NEXT_ACTION_NOTHING: @@ -314,7 +333,7 @@ class Blaster : public PROP_INHERIT_PREFIX PropBase { SetNextActionF(NEXT_ACTION_ARM, len); } - void selfDestruct() { + virtual void selfDestruct() { SaberBase::DoEndLockup(); #ifdef ENABLE_AUDIO float len = hybrid_font.GetCurrentEffectLength(); @@ -484,7 +503,11 @@ class Blaster : public PROP_INHERIT_PREFIX PropBase { break; } } + + bool auto_firing_ = false; + int shots_fired_ = 0; + bool is_jammed_ = false; + }; #endif - diff --git a/props/blaster_BC_buttons.h b/props/blaster_BC_buttons.h new file mode 100644 index 000000000..6003d2b8f --- /dev/null +++ b/props/blaster_BC_buttons.h @@ -0,0 +1,787 @@ +/* +blaster_BC_buttons.h prop file. + http://fredrik.hubbe.net/lightsaber/proffieos.html + Copyright (c) 2016-2023 Fredrik Hubinette + Copyright (c) 2023 Brian Conner with contributions by: + Fredrik Hubinette, Fernando da Rosa, Matthew McGeary, and Scott Weber. + Distributed under the terms of the GNU General Public License v3. + http://www.gnu.org/licenses/ + +Includes: +- Volume Menu with QuickMAX and QuickMIN. +- Spoken battery level in both volts or percentage. +- On-Demand battery level (Shows Green to Red if EFFECT_BATTERY_LEVEL is used in Blade Style). +- Quote player with On-the-fly sequential option. +- Next / previous preset. +- Self-Destuct overload. +- Defaults to KILL on boot. +- Separate clipin/clipout/reload sounds to match stun mode. + (requires clipins, clipouts, and reloads.wavs in font.) +- No jam if empty. +- Autofire mode counts bullets and causes empty if + #define BLASTER_SHOTS_UNTIL_EMPTY is specified. + Empty repeats at same rate auto was firing for added realism. Cool, eh? + +This prop version requires a ProffieOS Voicepack for menus to work right. + +Button assignment: +This prop requires a minimum of 2 buttons for operation. +If there's a 3rd button installed, it should be set in the config as: +Button ReloadButton(BUTTON_RELOAD, aux2Pin, "reload"); +It's coded to be Reload by default. +However, to use it as a dedicated POWER ON/OFF button instead, use the following define: +#define RELOAD_BUTTON_IS_POWER +The blaster defaults to auto-powered ON at boot and on Change Preset. +To use the RELOAD-as-POWER button to initially power ON instead, +use a poweron.wav file in the font. + + *Notes - When using dual_prop, the config file can optionally be set to use NUM_BUTTONS 1 for saber mode, + and the blaster mode will still use 2 buttons as FIRE and MODE. + +** This version is designed to work well together with saber_BC_buttons.h when +using dual_prop.h; a toggle between 2 prop files triggered by Blade Detect or Blade ID. +Blade insertion and removal switches between the 2 props, (ie: Ezra blaster and saber). +With dual_prop use, the config file should use SABER buttons in the CONFIG_BUTTONS section, like: +Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); +Button AuxButton(BUTTON_AUX, auxPin, "aux"); + +For a stand-alone blaster setup, the config file should use BLASTER buttons in +the CONFIG_BUTTONS section, like: +Button FireButton(BUTTON_FIRE, powerButtonPin, "fire"); +Button ModeButton(BUTTON_MODE_SELECT, auxPin, "modeselect"); + +Optional defines: + #define BLASTER_ENABLE_AUTO - Enable Autofire/rapid fire mode. + #define BLASTER_SHOTS_UNTIL_EMPTY 15 - whatever number. Not defined = unlimited shots. + #define BLASTER_JAM_PERCENTAGE - If this is not defined, random from 0-100%. + #define BLASTER_DEFAULT_MODE - MODE_STUN|MODE_KILL|MODE_AUTO. Not defined defaults to MODE_KILL. + +Depending on the install, one of the following orientation defines might be appropiate to use: + #define ORIENTATION ORIENTATION_FETS_TOWARDS_BLADE // default + #define ORIENTATION ORIENTATION_USB_TOWARDS_BLADE + #define ORIENTATION ORIENTATION_TOP_TOWARDS_BLADE + #define ORIENTATION ORIENTATION_BOTTOM_TOWARDS_BLADE + #define ORIENTATION ORIENTATION_USB_CCW_FROM_BLADE // This means the BATT(+) pad side. + #define ORIENTATION ORIENTATION_USB_CW_FROM_BLADE // This means the speaker pads side. + #define ORIENTATION ORIENTATION_SDA_TOWARDS_BLADE // Previous version of USB_CCW_FROM_BLADE. + #define ORIENTATION ORIENTATION_SERIAL_TOWARDS_BLADE // Previous version of USB_CW_FROM_BLADE. + +Optional Blade style elements: + EFFECT_DESTRUCT - Use in blade style to animate self destruct. + EFFECT_BATTERY_LEVEL - Use EFFECT_BATTERY_LEVEL in blade style, + and uses battery.wav sound effect. + EFFECT_VOLUME_LEVEL - Use EFFECT_VOLUME_LEVEL in blade style. + Useful if the blaster has a segmented accent LED strip or similar. +================================================== +| 2 Buttons: FIRE and MODE (saber's POW and AUX) | +================================================== +Power ON / OFF - Hold FIRE then Double Click MODE. + Default is auto-powered ON at Boot and on Change Preset. + Power ON required after Self Destruct. + Plays out.wavs for power ON and in.wavs for power OFF. +Cycle Modes - Hold MODE. Cycles through KILL, AUTOFIRE, and STUN modes. +Change Preset - Long Click and release MODE. + Next (while NOT pointing UP or DOWN). + Previous (while pointing DOWN). + Jump to First (while pointing UP). +Start/Stop Track - 4x Click MODE. +Volume Menu: + * Note - must exit Volume Menu before any other functions will work. + Enter Menu - Hold MODE then Click FIRE. + Volume UP - Rotate Right. + Volume DOWN - Rotate Left. + Quick MAX Vol - Hold FIRE. + Quick MIN Vol - Hold MODE. + Save and Exit - Click FIRE. + Cancel and Exit - Click MODE. +Spoken Battery Level + in percentage - 3x Click and Hold MODE. + in volts - 3x Click and Hold MODE (while pointing DOWN). + * Will show On-blade display if EFFECT_BATTERY_LEVEL is used in blade style. +On-Demand Batt Level - 3x Click and Hold MODE, release after a second. (Double Click then Long Click) + * Requires EFFECT_BATTERY_LEVEL to be in blade style. + * Plays battery.wav sound effect if it exists in font or common folder, + otherwise a familiar tune of beeps :) + +Self-Destruct - Hold MODE then Hold FIRE until overload starts....then run! + Self-Destructed blaster needs to be powered ON manually to use again. +Quote Player - Double Click MODE (while NOT pointing DOWN). (requires quote.wavs in font.) +Toggle quotes playback, + random or sequential - Double Click MODE (while pointing DOWN). +Reload - Click MODE. +Fire - Click FIRE. (Hold to Auto / Rapid Fire when AUTO mode selected.) +Clip In - Clip Detect pad Latched On. +Clip out - Clip Detect pad Latched Off. +Unjam - Bang the blaster or Reload, or Change Preset. + +================================= +| 3 Buttons: FIRE, MODE, RELOAD | +================================= +*** Controls are the same as 2 button above, but with these changes *** + +Reload - Click RELOAD. +Cycle Modes - Click MODE. +Change Preset - Hold MODE + Next (while NOT pointing UP or DOWN). + Previous (while pointing DOWN). + Jump to First (while pointing UP). + +- If defined RELOAD_BUTTON_IS_POWER + Power ON/OFF - Hold RELOAD until power ON/OFF. + + +Wavs to use for talking Mode (else Talkie voice): +- If these are not present, mode.wav will be used for all modes. +- If no mode.wav either, then Talkie voice speaks selected mode. + mdstun.wav + mdkill.wav + mdauto.wav +Additionally: + quote.wav // for playing quotes + in.wav // for blaster power off + out.wav // for blaster power on + destruct.wav // for self destruct overload + boom.wav // for self destruct explosion +These can be used to match a more "plasmatic" stun sound. + clipins.wav + clipouts.wav + reloads.wav + +The following are likely best kept in a 'common' folder in your Font Search Path, but of course +can be specific versions within a font as well. +Choose a Voice Pack containing all these sounds here: https://fredrik.hubbe.net/lightsaber/sound/ + + Battery level wavs: + mzero.wav + mnum 1-20 wavs, as well as the "tens" thirty.wav - ninety.wav + battery.wav // for accompanying EFFECT_BATTERY_LEVEL + battlevl.wav // for Spoken Battery Level + mpercent.wav + mpoint.wav + mvolts.wav + vmbegin.wav // for Begin Volume Menu + vmend.wav // for End Volume Menu + volup.wav // for increse volume + voldown.wav // for decrease volume + volmin.wav // for minimum volume reached + volmax.wav // for maximum volume reached + mfalse.wav // "disabled" used for sequential quote mode + mtrue.wav // "enabled" used for sequential quote mode + mrandom.wav // "ramdom" used for sequential quote mode +*/ + +#ifndef PROPS_BLASTER_BC_BUTTONS_H +#define PROPS_BLASTER_BC_BUTTONS_H + +#ifndef BLASTER_DEFAULT_MODE +#define BLASTER_DEFAULT_MODE MODE_KILL +#endif + +#include "blaster.h" +#include "../sound/sound_library.h" +#include "../modes/settings_menues.h" + +#ifdef PROP_TYPE +#undef PROP_TYPE +#endif + +#define PROP_TYPE BlasterBCButtons + +#ifndef BUTTON_HELD_LONG_TIMEOUT +#define BUTTON_HELD_LONG_TIMEOUT 1200 +#endif + +// Additional effects for BC buttons prop. +EFFECT(clipins); // s for stun mode +EFFECT(clipouts); // s for stun mode +EFFECT(reloads); // s for stun mode +EFFECT(destruct); +#ifndef PROPS_DUAL_PROP_H +EFFECT(battery); // for EFFECT_BATTERY_LEVEL +EFFECT(vmbegin); // for Begin Volume Menu +EFFECT(vmend); // for End Volume Menu +EFFECT(volup); // for increse volume +EFFECT(voldown); // for decrease volume +EFFECT(volmin); // for minimum volume reached +EFFECT(volmax); // for maximum volume reached +#endif + +template +struct BCBlasterVolumeMode : public SPEC::SteppedMode { + const int max_volume_ = VOLUME; + const int min_volume_ = VOLUME * 0.10; + float initial_volume_ = 0.0; + int initial_percentage_ = 0; + int percentage_ = 0; + + int steps_per_revolution() override { + return 12; // adjust for sensitivity + } + + void mode_activate(bool onreturn) override { + PVLOG_NORMAL << "** Enter Volume Menu\n"; + initial_volume_ = dynamic_mixer.get_volume(); + initial_percentage_ = round((initial_volume_ / max_volume_) * 10) * 10; + SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); + mode::getSL()->SayEditVolume(); + announce_volume(); + SPEC::SteppedMode::mode_activate(onreturn); + } + + void announce_volume() { + if (percentage_ <= 10) { + mode::getSL()->SayMinimumVolume(); + } else if (percentage_ >=100) { + mode::getSL()->SayMaximumVolume(); + } else { + mode::getSL()->SayWhole(percentage_); + mode::getSL()->SayPercent(); + } + } + + void mode_deactivate() override { + announce_volume(); + mode::getSL()->SayVolumeMenuEnd(); + SPEC::SteppedMode::mode_deactivate(); + } + + void next() override { + int current_volume_ = dynamic_mixer.get_volume(); + if (current_volume_ < max_volume_) { + current_volume_ += max_volume_ * 0.10; + if (current_volume_ >= max_volume_) { + current_volume_ = max_volume_; + QuickMaxVolume(); + } else { + mode::getSL()->SayVolumeUp(); + } + dynamic_mixer.set_volume(current_volume_); + } + } + + void prev() override { + int current_volume_ = dynamic_mixer.get_volume(); + if (current_volume_ > min_volume_) { + current_volume_ -= max_volume_ * 0.10; + if (current_volume_ <= min_volume_) { + current_volume_ = min_volume_; + QuickMinVolume(); + } else { + mode::getSL()->SayVolumeDown(); + } + dynamic_mixer.set_volume(current_volume_); + } + } + + void QuickMaxVolume() { + dynamic_mixer.set_volume(max_volume_); + PVLOG_NORMAL << "** Maximum Volume\n"; + mode::getSL()->SayMaximumVolume(); + } + + void QuickMinVolume() { + dynamic_mixer.set_volume(min_volume_); + PVLOG_NORMAL << "** Minimum Volume\n"; + mode::getSL()->SayMinimumVolume(); + } + + void update() override { // Overridden to substitute the tick sound + float volume = dynamic_mixer.get_volume(); + percentage_ = round((volume / max_volume_) * 10) * 10; + SaberBase::DoEffect(EFFECT_VOLUME_LEVEL, 0); + } + + void select() override { + PVLOG_NORMAL << "** Saved - Exiting Volume Menu\n"; + mode::getSL()->SaySave(); + SPEC::SteppedMode::select(); + } + + void exit() override { + PVLOG_NORMAL << "** Cancelled - Exiting Volume Menu\n"; + percentage_ = initial_percentage_; + dynamic_mixer.set_volume(initial_volume_); + mode::getSL()->SayCancel(); + SPEC::SteppedMode::exit(); + } + +// For Blasters + bool mode_Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override { + switch (EVENTID(button, event, 0)) { + + case EVENTID(BUTTON_FIRE, EVENT_FIRST_HELD_MEDIUM, 0): // fire m1 + QuickMaxVolume(); + return true; + case EVENTID(BUTTON_MODE_SELECT, EVENT_FIRST_HELD_MEDIUM, 0): // mode m1 + QuickMinVolume(); + return true; + + case EVENTID(BUTTON_FIRE, EVENT_FIRST_SAVED_CLICK_SHORT, 0): // fire s1 + select(); + return true; + case EVENTID(BUTTON_MODE_SELECT, EVENT_FIRST_SAVED_CLICK_SHORT, 0): // mode s1 + exit(); + return true; + } + return SPEC::SelectCancelMode::mode_Event2(button, event, modifiers); + } +}; + +template +struct BCBlasterMenuSpec { + typedef BCBlasterVolumeMode BCBlasterVolumeMenu; + typedef mode::SelectCancelMode SelectCancelMode; + typedef mode::SteppedMode SteppedMode; + typedef mode::SteppedModeBase SteppedModeBase; + typedef mode::MenuBase MenuBase; + typedef SoundLibraryV2 SoundLibrary; +}; + + +class BlasterBCButtons : public Blaster { +public: + BlasterBCButtons() : Blaster() {} + const char* name() override { return "BlasterBCButtons"; } + + void Setup() override { + MKSPEC::SoundLibrary::init(); + } + + void EnterVolumeMenu() { + pushMode::BCBlasterVolumeMenu>(); + } + + void Fire() override { + if (DoJam()) return; + if (blaster_mode != MODE_AUTO && DoEmpty()) return; + + switch (blaster_mode) { + case MODE_STUN: + SaberBase::DoEffect(EFFECT_STUN, 0); + shots_fired_++; + PVLOG_NORMAL << "******** STUN - Remaining shots = " << GetBulletCount() << "\n"; + break; + case MODE_KILL: + SFX_blast.Select(-1); + SaberBase::DoEffect(EFFECT_FIRE, 0); + shots_fired_++; + PVLOG_NORMAL << "******** FIRE - Remaining shots = " << GetBulletCount() << "\n"; + break; + case MODE_AUTO: + if (!CheckEmpty()) { + SelectAutoFirePair(); + auto_firing_ = true; + SaberBase::SetLockup(LOCKUP_AUTOFIRE); + SaberBase::DoBeginLockup(); + } + break; + } + } + + void DoAutoFire() override { + if (!SFX_auto) return; + // First time through, let bgnauto finish first if we have one + if (SFX_bgnauto && GetWavPlayerPlaying(&SFX_bgnauto)) return; + + // No bgnauto or bgnauto has finished playing, so initialize and start auto-firing + if (auto_firing_ && !auto_player_) { + auto_player_ = GetWavPlayerPlaying(&SFX_auto); + shots_fired_++; + auto_time_ = millis(); + PVLOG_NORMAL << "******** AUTOFIRING - Remaining shots = " << GetBulletCount() << "\n"; + } + // use auto.wav's length to count shots + if (auto_firing_ && auto_player_ && (millis() - auto_time_) > auto_sound_length) { + auto_sound_length = auto_player_->length() * 1000; + shots_fired_++; + if (CheckEmpty()) { + SaberBase::DoEndLockup(); + SaberBase::SetLockup(SaberBase::LOCKUP_NONE); + auto_firing_ = false; + auto_player_.Free(); + auto_time_ = millis(); // Reset for the first empty sound + return; + } + auto_time_ = millis(); + PVLOG_NORMAL << "******** AUTOFIRING - Remaining shots = " << GetBulletCount() << "\n"; + } + + // This will mimic the hammer running on no bullets at the same rate it was firing. + // if there are different length auto.wavs, the last played's length is used. + if (!auto_firing_ && CheckEmpty() && trigger_is_pressed_ && (millis() - auto_time_) > auto_sound_length) { + SaberBase::DoEffect(EFFECT_EMPTY, 0); + auto_time_ = millis(); // Reset for the next empty sound + } + } + + void Loop() override { + PropBase::Loop(); + PollNextAction(); + sound_library_.Poll(wav_player); + if (wav_player && !wav_player->isPlaying()) wav_player.Free(); + + if (blaster_mode == MODE_AUTO) { + DoAutoFire(); + } + } + + void Reload() override { + Blaster::Reload(); + is_jammed_ = false; + } + + void ClipOut() override { + Blaster::ClipOut(); + no_clip_ = true; + } + + void ClipIn() override { + Blaster::ClipIn(); + is_jammed_ = false; + no_clip_ = false; + } + + // Pull in parent's SetPreset, but turn the blaster on. + void SetPreset(int preset_num, bool announce) override { + PropBase::SetPreset(preset_num, announce); + if (!SFX_poweron && !SaberBase::IsOn()) { + On(); + } + } + + void selfDestruct() override { + Blaster::selfDestruct(); + SaberBase::DoEffect(EFFECT_DESTRUCT, 0); + SetNextActionF(NEXT_ACTION_BLOW, hybrid_font.GetCurrentEffectLength()); + } + + // Clash to unjam or Enter/Exit Volume Menu. + void Clash(bool stab, float strength) override { + PropBase::Clash(stab, strength); + if (is_jammed_) { + is_jammed_ = false; + SaberBase::DoEffect(EFFECT_UNJAM, 0); + } + } + + // Previous, next, or first preset, depending on angle + void DoChangePreset() { + if (fusor.angle1() > M_PI / 3) { + // Muzzle pointing UP + first_preset(); + PVLOG_NORMAL << "** Jumped to first preset\n"; + } else if (fusor.angle1() < -M_PI / 3) { + // Muzzle pointing DOWN + previous_preset(); + PVLOG_NORMAL << "** Previous preset\n"; + } else { + // Muzzle NOT pointing UP or DOWN + next_preset(); + PVLOG_NORMAL << "** Next preset\n"; + } + } + + void DoSpokenBatteryLevel() { + // Avoid weird battery readings when using USB + if (battery_monitor.battery() < 0.5) { + sound_library_.SayTheBatteryLevelIs(); + sound_library_.SayDisabled(); + return; + } + sound_library_.SayTheBatteryLevelIs(); + // pointing DOWN + if (fusor.angle1() < -M_PI / 4) { + sound_library_.SayNumber(battery_monitor.battery(), SAY_DECIMAL); + sound_library_.SayVolts(); + PVLOG_NORMAL << "Battery Voltage: " << battery_monitor.battery() << "\n"; + is_speaking_ = true; + SaberBase::DoEffect(EFFECT_BATTERY_LEVEL, 0); + } else { + sound_library_.SayNumber(battery_monitor.battery_percent(), SAY_WHOLE); + sound_library_.SayPercent(); + PVLOG_NORMAL << "Battery Percentage: " < auto_player_; + RefPtr wav_player; +}; + + +#endif // PROPS_BLASTER_BC_H diff --git a/sound/hybrid_font.h b/sound/hybrid_font.h index c0b2e6788..ae8dcca55 100644 --- a/sound/hybrid_font.h +++ b/sound/hybrid_font.h @@ -526,6 +526,7 @@ class HybridFont : public SaberBase, public Looper { } break; case OFF_BLAST: + SaberBase::DoEffect(EFFECT_BOOM, 0); if (monophonic_hum_) { if (SFX_boom) PlayMonophonic(getNext(hum_player_, &SFX_boom), NULL); else PlayMonophonic(getNext(hum_player_, &SFX_clash), NULL); // Thermal-D fallback @@ -533,6 +534,7 @@ class HybridFont : public SaberBase, public Looper { state_ = STATE_HUM_FADE_OUT; PlayPolyphonic(getNext(lock_player_, &SFX_boom)); } + PVLOG_NORMAL << "+++++ BOOM!! +++++\n"; break; }