<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://pinballindex.com/index.php?action=history&amp;feed=atom&amp;title=Drop_Target_Handler_Library</id>
	<title>Drop Target Handler Library - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://pinballindex.com/index.php?action=history&amp;feed=atom&amp;title=Drop_Target_Handler_Library"/>
	<link rel="alternate" type="text/html" href="https://pinballindex.com/index.php?title=Drop_Target_Handler_Library&amp;action=history"/>
	<updated>2026-05-13T13:52:16Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.38.4</generator>
	<entry>
		<id>https://pinballindex.com/index.php?title=Drop_Target_Handler_Library&amp;diff=51&amp;oldid=prev</id>
		<title>DickHamill: Wrote page</title>
		<link rel="alternate" type="text/html" href="https://pinballindex.com/index.php?title=Drop_Target_Handler_Library&amp;diff=51&amp;oldid=prev"/>
		<updated>2022-12-05T02:04:52Z</updated>

		<summary type="html">&lt;p&gt;Wrote page&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;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. &lt;br /&gt;
&lt;br /&gt;
=== Constructor ===&lt;br /&gt;
DropTargetBank(byte s_numSwitches, byte s_numSolenoids, byte s_bankType, byte s_solenoidOnTime);&amp;lt;blockquote&amp;gt;s_numSwitches - total number of drop targets in this bank&lt;br /&gt;
&lt;br /&gt;
s_numSolenoids - total number of solenoids required to reset this bank&lt;br /&gt;
&lt;br /&gt;
s_bankType&lt;br /&gt;
&lt;br /&gt;
* DROP_TARGET_TYPE_BALLY_1 - normal Bally&lt;br /&gt;
* DROP_TARGET_TYPE_STERN_1 - normal Stern&lt;br /&gt;
* DROP_TARGET_TYPE_STERN_2 - Stern &amp;quot;memory&amp;quot; drops (each target can be retracted with it's own solenoid)&lt;br /&gt;
* DROP_TARGET_TYPE_WILLIAMS_1 - extra switch for all targets down&lt;br /&gt;
&lt;br /&gt;
s_solenoidOnTime - number of cycles for solenoid reset (12 is common for Bally/Stern)&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
void DefineSwitch(byte switchOrder, byte switchNum); // this function is called for each target's switch&amp;lt;blockquote&amp;gt;switchOrder - zero index (used for knowing if targets were dropped in order)&lt;br /&gt;
&lt;br /&gt;
switchNum - machine's switch matrix index&amp;lt;/blockquote&amp;gt;void DefineResetSolenoid(byte solIndex, byte solChannelNumber); // this function is called for each reset solenoid for this bank&amp;lt;blockquote&amp;gt;solIndex - zero index solenoid number&lt;br /&gt;
&lt;br /&gt;
solChannelNumber - machine's solenoid index&amp;lt;/blockquote&amp;gt;void AddAllTargetsSwitch(byte s_allTargetsSwitch); // this function is only called if the bank has an &amp;quot;all down&amp;quot; switch (i.e. early Williams)&amp;lt;blockquote&amp;gt;s_allTargetsSwitch - machine's switch matrix index&amp;lt;/blockquote&amp;gt;byte HandleDropTargetHit(byte switchNum); // this function is called whenever the game loop sees a drop target switch on the stack&amp;lt;blockquote&amp;gt;switchNum - machine's switch matrix index&lt;br /&gt;
&lt;br /&gt;
Return value - returns a bit mask of all targets down since last hit&lt;br /&gt;
&lt;br /&gt;
* b0 - drop target index 0 is newly down (true/false)&lt;br /&gt;
* b1 - drop target index 1 is newly down (true/false)&lt;br /&gt;
* etc.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;byte CheckIfBankCleared(); // this function is called after a target switch is seen to determine if bank has been cleared&amp;lt;blockquote&amp;gt;Return values:&lt;br /&gt;
&lt;br /&gt;
* 0 - bank not clear&lt;br /&gt;
* 1 - DROP_TARGET_BANK_CLEARED&lt;br /&gt;
* 2 - DROP_TARGET_BANK_CLEARED_IN_ORDER&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;void Update(unsigned long currentTime); // this function should be called in the program's main loop&amp;lt;blockquote&amp;gt;currentTime - the current time in milliseconds&amp;lt;/blockquote&amp;gt;void ResetDropTargets(unsigned long timeToReset, boolean ignoreQuickDrops=false); // call this function to reset the bank&amp;lt;blockquote&amp;gt;timeToReset - machine time in milliseconds to reset; typically CurrentTime + (any delay in ms)&lt;br /&gt;
&lt;br /&gt;
ignoreQuickDrops - set this parameter to ignore drops that come immediately after reset, in case of mechanical failure or Williams&amp;lt;/blockquote&amp;gt;byte GetStatus(); // call to get a bit mask of the current drops down&amp;lt;blockquote&amp;gt;Return value:&lt;br /&gt;
&lt;br /&gt;
* b0 - drop target index 0 is down (true/false)&lt;br /&gt;
* b1 - drop target index 1 is down (true/false)&lt;br /&gt;
* etc.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Common Usage ===&lt;br /&gt;
Global variable for each bank:&amp;lt;blockquote&amp;gt;DropTargetBank DropTargets6(6, 2, DROP_TARGET_TYPE_WILLIAMS_1, 50);&amp;lt;/blockquote&amp;gt;Targets are configured in setup() function:&amp;lt;blockquote&amp;gt;  DropTargets6.DefineSwitch(0, SW_LEFT_3_DROP_1);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineSwitch(1, SW_LEFT_3_DROP_2);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineSwitch(2, SW_LEFT_3_DROP_3);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineSwitch(3, SW_RIGHT_3_DROP_1);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineSwitch(4, SW_RIGHT_3_DROP_2);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineSwitch(5, SW_RIGHT_3_DROP_3);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.AddAllTargetsSwitch(SW_3_DROPS_COMPLETE);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineResetSolenoid(0, SOL_3_DROP_LEFT_RESET);&lt;br /&gt;
&lt;br /&gt;
  DropTargets6.DefineResetSolenoid(1, SOL_3_DROP_RIGHT_RESET);&amp;lt;/blockquote&amp;gt;Targets are reset in InitNewBall&amp;lt;blockquote&amp;gt;    DropTargets6.ResetDropTargets(CurrentTime + 200, true);&amp;lt;/blockquote&amp;gt;Switch hits are sent to a handler function&amp;lt;blockquote&amp;gt;    case SW_LEFT_3_DROP_1:&lt;br /&gt;
&lt;br /&gt;
    case SW_LEFT_3_DROP_2:&lt;br /&gt;
&lt;br /&gt;
    case SW_LEFT_3_DROP_3:&lt;br /&gt;
&lt;br /&gt;
    case SW_RIGHT_3_DROP_1:&lt;br /&gt;
&lt;br /&gt;
    case SW_RIGHT_3_DROP_2:&lt;br /&gt;
&lt;br /&gt;
    case SW_RIGHT_3_DROP_3:&lt;br /&gt;
&lt;br /&gt;
    case SW_3_DROPS_COMPLETE:&lt;br /&gt;
&lt;br /&gt;
      if (Handle6Drops(switchHit)) {&lt;br /&gt;
&lt;br /&gt;
        LastSwitchHitTime = CurrentTime;&lt;br /&gt;
&lt;br /&gt;
        if (BallFirstSwitchHitTime == 0) BallFirstSwitchHitTime = CurrentTime;&lt;br /&gt;
&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      break;&amp;lt;/blockquote&amp;gt;Score is added in switch handler, and targets are checked to see if they have been cleared&amp;lt;blockquote&amp;gt;void Handle6Drops(byte switchHit) {  &lt;br /&gt;
&lt;br /&gt;
byte result = DropTargets6.HandleDropTargetHit(switchHit);&lt;br /&gt;
&lt;br /&gt;
  unsigned long numTargetsDown = (unsigned long)CountBits(result);&lt;br /&gt;
&lt;br /&gt;
  if (numTargetsDown) CurrentScores[CurrentPlayer] += PlayfieldMultiplier * numTargetsDown * 100;&lt;br /&gt;
&lt;br /&gt;
  byte cleared = DropTargets6.CheckIfBankCleared();&lt;br /&gt;
&lt;br /&gt;
  if (cleared) {&lt;br /&gt;
&lt;br /&gt;
    // currently no award for clearing in order&lt;br /&gt;
&lt;br /&gt;
    DropTargets6.ResetDropTargets(CurrentTime + 500, true);&lt;br /&gt;
&lt;br /&gt;
    if (Drop6Clears[CurrentPlayer]&amp;lt;4) {&lt;br /&gt;
&lt;br /&gt;
      CurrentScores[CurrentPlayer] += Drop6ClearRewards[Drop6Clears[CurrentPlayer]] * PlayfieldMultiplier;&lt;br /&gt;
&lt;br /&gt;
      Drop6Clears[CurrentPlayer] += 1;&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
      AwardSpecial();&lt;br /&gt;
&lt;br /&gt;
      Drop6Clears[CurrentPlayer] = 0;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IncreasePlayfieldMultiplier(30000);&lt;br /&gt;
&lt;br /&gt;
    PlaySoundEffect(SOUND_EFFECT_DROP_TARGET_RESET);&lt;br /&gt;
&lt;br /&gt;
  } else if (numTargetsDown) {&lt;br /&gt;
&lt;br /&gt;
    PlaySoundEffect(SOUND_EFFECT_DROP_TARGET_HIT);&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
...&amp;lt;/blockquote&amp;gt;Targets are updated in loop() or ManageGameMode()&amp;lt;blockquote&amp;gt;  DropTargets6.Update(CurrentTime);&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where to find it ===&lt;br /&gt;
[https://github.com/BallySternOS/BallySternOS/tree/master/HelperClasses Latest code on GitHub - DropTargets.h]&lt;/div&gt;</summary>
		<author><name>DickHamill</name></author>
	</entry>
</feed>