Drop Target Handler Library

From Custom Pinball Index
Revision as of 20:04, 4 December 2022 by DickHamill (talk | contribs) (Wrote page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Drop targets on pinball machines can be as simple as a switch for each target and a solenoid to reset those targets. Why would you need a library to handle those? In some machines (early Williams solid state machines), there is a switch that momentarily makes contact as each switch falls, and then another daisy-chained switch to indicate that all the targets are down. In early Bally machines, the switch is triggered when the target is all the way down and a poorly-gapped switch can give multiple readings when vibrations move through the playfield. This library handles those cases and only returns valid closures (one per target per reset). It also keeps track if the targets were dropped in order, and it knows to ignore closures that happen during the reset.

Constructor

DropTargetBank(byte s_numSwitches, byte s_numSolenoids, byte s_bankType, byte s_solenoidOnTime);

s_numSwitches - total number of drop targets in this bank

s_numSolenoids - total number of solenoids required to reset this bank

s_bankType

  • DROP_TARGET_TYPE_BALLY_1 - normal Bally
  • DROP_TARGET_TYPE_STERN_1 - normal Stern
  • DROP_TARGET_TYPE_STERN_2 - Stern "memory" drops (each target can be retracted with it's own solenoid)
  • DROP_TARGET_TYPE_WILLIAMS_1 - extra switch for all targets down

s_solenoidOnTime - number of cycles for solenoid reset (12 is common for Bally/Stern)

Functions

void DefineSwitch(byte switchOrder, byte switchNum); // this function is called for each target's switch

switchOrder - zero index (used for knowing if targets were dropped in order) switchNum - machine's switch matrix index

void DefineResetSolenoid(byte solIndex, byte solChannelNumber); // this function is called for each reset solenoid for this bank

solIndex - zero index solenoid number solChannelNumber - machine's solenoid index

void AddAllTargetsSwitch(byte s_allTargetsSwitch); // this function is only called if the bank has an "all down" switch (i.e. early Williams)

s_allTargetsSwitch - machine's switch matrix index

byte HandleDropTargetHit(byte switchNum); // this function is called whenever the game loop sees a drop target switch on the stack

switchNum - machine's switch matrix index

Return value - returns a bit mask of all targets down since last hit

  • b0 - drop target index 0 is newly down (true/false)
  • b1 - drop target index 1 is newly down (true/false)
  • etc.

byte CheckIfBankCleared(); // this function is called after a target switch is seen to determine if bank has been cleared

Return values:

  • 0 - bank not clear
  • 1 - DROP_TARGET_BANK_CLEARED
  • 2 - DROP_TARGET_BANK_CLEARED_IN_ORDER

void Update(unsigned long currentTime); // this function should be called in the program's main loop

currentTime - the current time in milliseconds

void ResetDropTargets(unsigned long timeToReset, boolean ignoreQuickDrops=false); // call this function to reset the bank

timeToReset - machine time in milliseconds to reset; typically CurrentTime + (any delay in ms) ignoreQuickDrops - set this parameter to ignore drops that come immediately after reset, in case of mechanical failure or Williams

byte GetStatus(); // call to get a bit mask of the current drops down

Return value:

  • b0 - drop target index 0 is down (true/false)
  • b1 - drop target index 1 is down (true/false)
  • etc.

Common Usage

Global variable for each bank:

DropTargetBank DropTargets6(6, 2, DROP_TARGET_TYPE_WILLIAMS_1, 50);

Targets are configured in setup() function:

  DropTargets6.DefineSwitch(0, SW_LEFT_3_DROP_1);

  DropTargets6.DefineSwitch(1, SW_LEFT_3_DROP_2);

  DropTargets6.DefineSwitch(2, SW_LEFT_3_DROP_3);

  DropTargets6.DefineSwitch(3, SW_RIGHT_3_DROP_1);

  DropTargets6.DefineSwitch(4, SW_RIGHT_3_DROP_2);

  DropTargets6.DefineSwitch(5, SW_RIGHT_3_DROP_3);

  DropTargets6.AddAllTargetsSwitch(SW_3_DROPS_COMPLETE);

  DropTargets6.DefineResetSolenoid(0, SOL_3_DROP_LEFT_RESET);

  DropTargets6.DefineResetSolenoid(1, SOL_3_DROP_RIGHT_RESET);

Targets are reset in InitNewBall

    DropTargets6.ResetDropTargets(CurrentTime + 200, true);

Switch hits are sent to a handler function

    case SW_LEFT_3_DROP_1:

    case SW_LEFT_3_DROP_2:

    case SW_LEFT_3_DROP_3:

    case SW_RIGHT_3_DROP_1:

    case SW_RIGHT_3_DROP_2:

    case SW_RIGHT_3_DROP_3:

    case SW_3_DROPS_COMPLETE:

      if (Handle6Drops(switchHit)) {

        LastSwitchHitTime = CurrentTime;

        if (BallFirstSwitchHitTime == 0) BallFirstSwitchHitTime = CurrentTime;

      }

      break;

Score is added in switch handler, and targets are checked to see if they have been cleared

void Handle6Drops(byte switchHit) {

byte result = DropTargets6.HandleDropTargetHit(switchHit);

  unsigned long numTargetsDown = (unsigned long)CountBits(result);

  if (numTargetsDown) CurrentScores[CurrentPlayer] += PlayfieldMultiplier * numTargetsDown * 100;

  byte cleared = DropTargets6.CheckIfBankCleared();

  if (cleared) {

    // currently no award for clearing in order

    DropTargets6.ResetDropTargets(CurrentTime + 500, true);

    if (Drop6Clears[CurrentPlayer]<4) {

      CurrentScores[CurrentPlayer] += Drop6ClearRewards[Drop6Clears[CurrentPlayer]] * PlayfieldMultiplier;

      Drop6Clears[CurrentPlayer] += 1;

    } else {

      AwardSpecial();

      Drop6Clears[CurrentPlayer] = 0;

    }

    IncreasePlayfieldMultiplier(30000);

    PlaySoundEffect(SOUND_EFFECT_DROP_TARGET_RESET);

  } else if (numTargetsDown) {

    PlaySoundEffect(SOUND_EFFECT_DROP_TARGET_HIT);

  }

...

Targets are updated in loop() or ManageGameMode()

  DropTargets6.Update(CurrentTime);

Where to find it

Latest code on GitHub - DropTargets.h