Blaster_Baldusi

I’ve solved my compiling issues, so I will use this thread to present the prop.
My objective is to replace blaster.h. As such, I’m not adding any bells and whistles like menus, edition nor anything of that. But I want to give a reasonable level of customizability without having to touch the code, and take advantage of everything we have defined already. First, I will talk about what is my view for the user:

Functionality:
-------------------------------------------------------------------------------
* PROP DEFINES *
Optional #defines that customize the blaster's behavior.
-------------------------------------------------------------------------------

Optional defines:
  #define ENABLE_BLASTER_AUTO           - DEPRECATED. Enable Autofire/rapid fire mode.
  #define BLASTER_ENABLE_AUTO           - Enable Autofire/rapid fire mode.
  #define BLASTER_ENABLE_AUTO_ON_HOLD   - Enable Autofire/rapid fire mode when holding the FIRE button. Implicit BLASTER_ENABLE_AUTO.
  #define BLASTER_DEFAULT_MODE			- Sets the mode at startup MODE_STUN|MODE_KILL|MODE_AUTO.
  #define BLASTER_DEFAULT_POWERON		- Sets the state at startup as ON.
  #define BLASTER_DEFAULT_POWEROFF		- Sets the state at startup as OFF.
  #define BLASTER_RANGE_LEVELS			- Sets the steps of Range/Power levels, also used to define PLI steps. Should be 1 to 64. Defaults to 6.
  #define BLASTER_RANGE_LEVELS_BY_FONT  - Sets the steps of Range/Power levels according to each fonts number of rangexx.wavs.
  #define BLASTER_SHOTS_UNTIL_EMPTY 15  - Whatever number, not defined = unlimited shots.
  #define BLASTER_AUTO_LIMITED_ROUNDS	- When enabled, AUTO mode respects the round limit set by BLASTER_SHOTS_UNTIL_EMPTY.
  #define BLASTER_JAM_PERCENTAGE        - If this is not defined, random from 0-100%.
  #define BLASTER_SINGLE_POWER_BUTTON   - Only Power Button will turn on/off the blaster.
  #define BLASTER_SINGLE_RELOAD_BUTTON  - Only Reload Button will reload the blaster.
  #define BLASTER_RELOAD_THROUGH_CLIP   - Only Clip Out then Clip In will reload the blaster.
  #define BLASTER_CLIP_OUT_BLOCK        - Clip Out will block blaster even without BLASTER_SHOTS_UNTIL_EMPTY.

-------------------------------------------------------------------------------
* PROP BUTTONS *
This prop manages up to six different buttons.
-------------------------------------------------------------------------------

*Single Button Mode*
 -Buttons: FIRE
  This case quite limited since you can only fire and reload. Weapon will always
  be on the default mode (STUN is the defined in the prop, if you wish another
  you will have to define BLASTER_DEFAULT_MODE). Blaster can be turned on or off.
  And can be reloaded, which takes precedence over turning it on/off.
  
  Default Mode: Fire or Stun
    Fire 			- Click FIRE.
	Power On / Off	- Hold FIRE.
	Reload			- Hold FIRE until Reloaded. Takes priority over power on/off.
    Unjam			- Bang the blaster.
  
  Default Mode: Auto *This mode is not compatible with clip capacity and really needs
                      an on/off switch on the power line.
    Fire (Semi)     - Click FIRE.
    Fire (Auto)     - Hold FIRE.
    Power On        - Hold FIRE. You can't power off.
    Unjam           - Bang the blaster.

*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.
  Use BLASTER_DEFAULT_POWERON or BLASTER_DEFAULT_POWEROFF to define a different
  default.
  
    Fire 				- Click FIRE.
    Cycle Modes			- Click MODE.
    Next Preset			- Long click and release MODE.
    Previous Preset		- Double click and hold MODE, release after a second.
    Reload				- Hold MODE until Reloaded.
    Start/Stop Track	- Double click MODE.
    Unjam				- Bang the blaster.

*Extra Buttons*
-Button: POWER
	Power On / Off	- Click POWER.

-Button: RELOAD
  You can make this the only button to reload by defining BLASTER_SINGLE_RELOAD_BUTTON.
	Reload			- Hold RELOAD until Reloaded.
	
-Button: CLIP
  You can make that shooting will be blocked when the clip is out by defining
  define BLASTER_CLIP_OUT_BLOCK.
  You can make that you only reload through the clip by defining
  BLASTER_RELOAD_THROUGH_CLIP (takes precedence over BLASTER_SINGLE_RELOAD_BUTTON).
	Clip In			- Latch CLIP
	Clip Out		- Unlatch CLIP

-Button: RANGE
  You have to define the number of levels by 
	Increase Range	- Click RANGE.
	Decrease Range	- Long click RANGE.
	

Also, I want actually use the PLI. So I’m proposing that when I increase range, the PLI will increase, and when I decrease it, it will decrease. If we define some effects that can be seen from the styles, a simple WS2812 can do as PLI. Optionally it could be done on the OLED. But that might go for the future.

DONE:

  • Define initial power state.
  • Define initial blaster mode.
  • Consolidate all defines as BLASTER_XX defines.
  • Single Button semi-auto mode.
  • Single Button auto mode.
  • Allowing for a single Power and Reload buttons.
  • Option for Clip out to block all firing modes.
  • Option for Clip in to reload.
  • Option for reload only through Clip Out/In.
  • Initial range support.
  • Setting range notification fall back options.
  • Adding the option of letting the amount of rangeXX.wavs determine the range levels.
  • Adding the option of differentiated sounds for STUN reload, clipin and clipout. (NoSloppy had solved it, thanks NoSloppy!)

TODO list:

  • Actually making use of the full.wav sound and effect.
  • Enabling that Autofire respects the clip limitation. (NoSloppy had solved it)
  • Enable Auto fire from blaster by holding the FIRE button.
  • Allow for Range to change firing volume/speed.
  • Allow for Range on hold and multishot.
  • Enable Save preset.
  • Enable Save state.
  • Adding a simple function so a Style can act as the PLI.
  • Clean Up OLED support.
  • Allow OLED to work as PLI.
  • Dividing everything in pieces so it can be merged back.

Questions:

  1. will your new blaster prop be backwards compatible? If not, it would probably be better to create a new file.
  2. In terms of code reviews, I would much rather see a bunch of small changes transforming blaster.h into what you think it should be, rather than one large rewrite. Is that possible?

PLI stands for Power Level Indicator, so I think you mean something else. “Range Indicator” ? Making styles for showing range should be easy, making it show up on an OLED is also possible, but little harder since we don’t have easy customizations for what to show on the OLED yet…

I copied the code and moved it to my own file. My idea is to make it fully compatible, since whatever new I define is to help users to not having to modify the code. The bulk of “rewrite” comes from that and doing more comments.
The code has a copy and paste of the detonator code, that’s not used, defines the Range button but does not uses it, and has the PLI events but does not uses them, either. So I wanted to do some cleanup and add that features.

I will try to make the changes as small as possible, but first wanted to discuss a couple of things:
We have Range and PLI defined, but not used. I’m assuming this is because those were taken from a BlasterBoard sound definition.
When I thought about generalizing, I just tried to keep the “standard” names, but I will propose how I pretend to generalize them:

Range is some way to change a power level, range or anything that can increase the output of the blaster. PLI should be an indication of such power level. Some user might want to use it to increase range, other to increase power, other to increase rate, I don’t know. But I know that if we just say range, define a scale (as little as binary or as big as 1 to 4096) the mechanics are the same.
So we put one way to increase it, one way to decrease it, and add those effect plus PLI_MAX, PLI_MIN, we can do any sort of style that can go to any pixel blade and display that Power Level however the user wants.

I would like to add EFFECT_RANGE_MAX and EFFECT_RANGE_MAX. Eventually this would allow for a sound that you’ve reached the limit of your range/power increase, might even reflect on the blades. How can I go around adding new effects on the prop file?

I don’t want to call it PLI, because that term tends to mean a lot of things already, and most of them are confused with battery level indicators.

Maybe range meter?

The enum values would need to be added to DEFINE_ALL_EFFECTS in common/saber_base.h once that’s done, you can add both the code that triggers those effects, and the code that plays sounds for those effects to the prop file. You can see examples of how this is done in the Fett263 prop.

(EFFECT_POWERSAVE might be a good example.)

1 Like

In most of the style code we use 32768 to be the maximum.
However, for the range, it might be better if there was a set number of levels, and the maximum level was a define. The number of levels could even be defined by the number of files available for an effect in the font…

1 Like

I will try to make some small changes, then. But I’ve been thinking about things like overheating barrels and energy weapons that can slowly recharge. Is there a way to count some “ticks” and do some math with that? Say, each shot generates X heat, the rate of cooling is Y, if heat reaches T, then we have to wait until it cools to W. Something along those lines. Clearly not for the basic prop.

Is there some way to know if a certain button has been defined? Because knowing is there is a BUTTON_CLIP_DETECT or BUTTON_RANGE etc.

No.
In some cases you can infer it from NUM_BUTTONS, but when that is not possible you would need new defines for it.

In the range case, you could assume that there is a range button if a maximum range has been specified.

1 Like

Ok, I think I’m getting hold of this. This is the 1 button version of the prop.

It’s not compatible with AUTO mode, obviously. But that’s more useful than the previous one. I will have to add the option so one can boot in AUTO mode and use a physical on/off, I guess. Now I will go and test the 2 and more buttons options.

1 Like

@profezzorn I’m trying to put some sanity checks in the prop, but I clearly am trying to do procedural C in an event-driven C++ environment.

The relevant code is this:

class Blaster_Baldusi : public PROP_INHERIT_PREFIX PropBase {
public:
  Blaster_Baldusi() : PropBase() {}
  const char* name() override { return "Blaster_Baldusi"; }

  // Mode states to handle kill vs stun effects
  enum BlasterMode {
    MODE_STUN,
    MODE_KILL,
    MODE_AUTO
  };

  BlasterMode blaster_mode = BLASTER_DEFAULT_MODE;
  #if !(defined (ENABLE_BLASTER_AUTO) || defined (BLASTER_ENABLE_AUTO) || defined (BLASTER_ENABLE_AUTO_ON_HOLD))
    if (blaster_mode == MODE_AUTO) {
        blaster_mode = MODE_STUN;
  //      #warning BLASTER_DEFAULT_MODE set to MODE_AUTO without BLASTER_ENABLE_AUTO.
    }
  #endif

  #ifdef BLASTER_ENABLE_AUTO_ON_HOLD
    BlasterMode mode_memory_ = blaster_mode;
  #endif

  virtual void SetBlasterMode(BlasterMode to_mode) {
    if (!auto_firing_) {
      blaster_mode = to_mode;
      SaberBase::DoEffect(EFFECT_MODE, 0);
    }
  }

For which I get a

D:\Sabers\ProffieOS-7.x\ProffieOS\props\blaster_baldusi.h:235:3: error: expected unqualified-id before 'if'
  235 |   if (blaster_mode == MODE_AUTO) {
      |   ^~

I guess I can’t do conditionals outside of a method. So, I’m wondering if I should do a constructor, and thus, do you know an example of a custom constructor for the PropBase?

Is this “just in case” someone writes the define incorrectly in their config?

What is the plain English talk-through of this conditional?
If BLASTER_ENABLE_AUTO not defined , then the default mode is set to MODE_AUTO anyway?
May I ask why?

I’m no pro, but I think you should lose the trailing underscore on BlasterMode mode_memory_ = blaster_mode;

There is a lot of things mixed up here.

Not only can you not do conditionals outside of functions. (That is only possible in languages where function creation is done by the interpreter, like javascript, or python.) but also, it’s not actually when you want to do this.

Also, you can’t put a #warning inside of an if statement and expected it to only trigger if the if statement is true.

To understand why, let’s talk a little bit about how the different stages of C++ compilation and execution…

  1. the pre-processor. The pre-process is what is known as a “macro processor”. In general, the pre-processor is a kind of search-and-replace engine on steroids. The most important thing to know about the pre-processor is that it just operates on text. The input is text, and the output is text. All the stuff that starts with a # is an instruction for the pre-processor. The pre-processor runs first, and once it’s done, the output is either an error, or a chunk or text that is sent to the compiler. #warning is a pre-processor statement.
  2. The compiler. The compiler is primarily responsible for turning text into binary data that your processor can understand and run. In it’s simplest form, it just takes the each piece of code and turns it into machine code. Note that the compiler is not capable of running arbitrary code. For the most part, it just translates into something that can be executed later.
  3. Running the code. In ProffieOS, this doesn’t happen until it’s on the proffieboard. At this point, the translated if statements get executed, but it’s too late for compile errors, and it’s not possible to compile more code anymore.

So, by the time the if statements execute (3), it’s far too late to generate a #warning, which executes in (1).

Let’s talk about what we can do:

Option #1: Use the preprocessor.

If we change BlasterMode from an enum into three defines, then the we can simply do something like

#if (BLASTER_DEFAULT_MODE - 0 == MODE_AUTO) &&
#warning BLASTER_DEFAULT_MODE is set to auto
#endif

It’s not ideal though, because defines don’t have nice type names we can use for variables.

Option #2: a static assert

There are a form of conditions that can be put outside of code.
These are called static asserts, and they can be put almost anywhere because of how they are implemented.

You can find them in a few places in the code, like this:

In this case, the static assert would probably be something like:

#ifndef BLASTER_ENABLE_AUTO
static_assert(BLASTER_DEFAULT_MODE != MODE_AUTO,
  "BLASTER_DEFAULT_MODE cannot be MODE_AUDO without BLASTER_ENABLE_AUTO");
#endif
1 Like

I am enabling single button auto mode, besides the semi-auto ones. Since you can’t change the mode for lack of buttons, you have to set the default mode to MODE_AUTO. So I’m trying to sanitize the process and outright abort the compile with a clear error for the invalid configurations. I’m of the idea that this should help us reduce some troubleshooting in the future.
Please understand that I’m doing quite a few changes and bits of pieces of different parts might show up.

That’s a helper variable. I actually copy the trailing underscore from your config file for internal variables.

Thanks Proff! I used the static assert way. Exactly what I needed.
Now, I can show the single button auto mode:

Now I’ve enabled all buttons, save for the Clip. Range button can go up, down and has maximum and minimum. Now we have to define what to do with it. :rofl:

The block in the end of the video is actually a jam that I forgot to clear (you have to shake the whole thing).

I’ve extended the Clip functionality. Now you can use it for reloading (I set it to exclusively reload) an also to block all discharges, even in auto.

I think that with this clip is pretty much feature complete.

Next on the TODO list:
-Adding the option of letting the amount of rangeXX.wavs determine the range levels.
-Adding the option of differentiated sounds for STUN reload, clipin and clipout. (NoSloppy had solved it)
-Enabling that Autofire respects the clip limitation. (NoSloppy had solved it)
-Enable Auto fire from blaster by holding the FIRE button.
-Adding a simple function so a Style can act as the PLI.
-Dividing everything in pieces so it can be merged back.

@profezzorn I’m trying to get to use rangexx.wav for each level. So, range01.wav says “Range ONE”, range02.wav says “Range TWO” and so on (or some other words, it doesn’t matter. So, I want to test if there is, for example, a range05.wav. How should I do? Specially now that we have range/001/00x and range/alt/001 etc.

I would like to do something along this lines:

  void SayRange() {
    if (previous_range_>current_range_) {
        if (/*Test if number exists*/) {
        //Play number;
        } else if (SFX_rangeup) {
          hybrid_font.PlayCommon(&SFX_rangeup);
        } else {
          talkie.Say(spRANGE);
          talkie.Say(spUP);
        }
    } else if (previous_range_<current_range_) {
        if (/*Test if number exists*/) {
        //Play number;
        } else if (SFX_rangeup) {
          hybrid_font.PlayCommon(&SFX_rangeup);
        } else {
          talkie.Say(spRANGE);
          talkie.Say(spDOWN);
        }
    } else if (current_range_ == BLASTER_RANGE_LEVELS) {
        if (/*Test if number exists*/) {
        //Play number;
        } else if (SFX_rangemax) {
          hybrid_font.PlayCommon(&SFX_rangemax);
        } else {
          talkie.Say(spRANGE);
          talkie.Say(spMAXIMUM); //Not defined in 'voice_data', yet.
        }
    } else {
        //This should always be range001.wav
        if (/*Test if number exists*/) {
        //Play number;
        } else if (SFX_rangemin) {
          hybrid_font.PlayCommon(&SFX_rangemin);
        } else {
          talkie.Say(spRANGE);
          talkie.Say(spMINIMUM); //Not defined in 'voice_data', yet.
        }

    }
  }