void racetrack_reset_track_state(void) { racetrack_encoder_mask = 0; racetrack_stall_ticks_remaining = LANE_STALL_DETECT_TICKS; racetrack_lanes[LANE_LEFT].state = LANE_STOP; racetrack_lanes[LANE_RIGHT].state = LANE_STOP; racetrack_lanes[LANE_LEFT].encoder_count = 0; racetrack_lanes[LANE_RIGHT].encoder_count = 0; racetrack_lanes[LANE_LEFT].desired_encoder_count = 0; racetrack_lanes[LANE_RIGHT].desired_encoder_count = 0; racetrack_lanes[LANE_LEFT].desired_position = 0; racetrack_lanes[LANE_RIGHT].desired_position = 0; // cause the speed tick tester to re-evaluate on the next tick. racetrack_lanes[LANE_LEFT].speed_ticks_remaining = 1; racetrack_lanes[LANE_RIGHT].speed_ticks_remaining = 1; if (switch_poll_logical (SW_LEFT_RACE_ENCODER)) { racetrack_encoder_mask |= RT_EM_PREVIOUS_STATE_LEFT; } if (switch_poll_logical (SW_RIGHT_RACE_ENCODER)) { racetrack_encoder_mask |= RT_EM_PREVIOUS_STATE_RIGHT; } }
CALLSET_ENTRY (autofire, ball_search) { /* The shooter divertor/autofire are both kicked here since there is a dependency between the two. The main ball search routine is told not to kick either one of them. */ if (switch_poll_logical (SW_AUTOFIRE2) || switch_poll_logical (SW_AUTOFIRE1)) { callset_invoke (clear_autofire); } }
static inline void racetrack_state_car_return_run(void) { if (switch_poll_logical (SW_LEFT_RACE_START) && switch_poll_logical (SW_RIGHT_RACE_START)) { racetrack_state = RACETRACK_READY; return; } sol_disable(SOL_RACE_DIRECTION); // TODO check lane 'stalled' bits, if either one gets set then disable the racetrack? }
/** * Activate the autolaunch mechanism. A ball must already have * been served from the trough ; this just fires the launch * solenoid. */ static void launch_ball_task (void) { #ifdef HAVE_AUTO_SERVE if (!switch_poll_logical (MACHINE_SHOOTER_SWITCH)) task_sleep (TIME_500MS); do { sol_request_async (MACHINE_LAUNCH_SOLENOID); task_sleep (LAUNCH_DELAY); } while (switch_poll_logical (MACHINE_SHOOTER_SWITCH)); #endif task_exit (); }
/** * Process a switch that has transitioned states. All debouncing * is fully completed prior to this call. * * The transition is first latched, so that polling the switch level * will return the new state. At the same time, IRQ-level scanning * is restarted, so that further transitions can be detected. * * Second, if the transition requires scheduling, a task is started * to handle it. Eligibility depends on whether or not the switch * is declared as an edge switch (meaning it is scheduled on both * types of transitions) and whether or not it is an opto (i.e. * do we schedule open-to-closed or closed-to-open?) * * The switch is also known not to be in the debounce queue prior to this * function being called. */ void switch_transitioned (const U8 sw) { /* Latch the transition. sw_logical is still an open/closed level. * By clearing the stable/unstable bits, IRQ will begin scanning * for new transitions at this point. */ disable_irq (); bit_toggle (sw_logical, sw); bit_off (sw_stable, sw); bit_off (sw_unstable, sw); enable_irq (); /* See if the transition requires scheduling. It does if the switch is declared 'edge' (it schedules when becoming active or inactive), otherwise only becoming active. Because most switches are not edge, check for the active state first. */ if (switch_poll_logical (sw) || bit_test (mach_edge_switches, sw)) { #ifdef CONFIG_BPT /* One extra condition : do not schedule any switches when the system is paused */ if (db_paused != 0) return; #endif /* Start a task to process the switch event. This task may sleep if necessary, but it should be as fast as possible and push long-lived operations into separate background tasks. It is possible for more than one instance of a task to exist for the same switch, if valid debounced transitions occur quickly. */ task_pid_t tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } }
void abort_monitor_task (void) { U8 count = 3; while (count > 0) { task_sleep (TIME_66MS); if (!switch_poll_logical (SW_LEFT_BUTTON) || !switch_poll_logical (SW_RIGHT_BUTTON)) { task_exit (); } count--; } callset_invoke (flipper_abort); task_exit (); }
/* Return the number of balls currently present in the device */ U8 device_recount (device_t *dev) { U8 i; U8 count = 0; /* Everytime a recount occurs, we remember the previous value that was counted. By comparing these two, we can tell if something changed. */ dev->previous_count = dev->actual_count; for (i=0; i < dev->size; i++) { switchnum_t sw = dev->props->sw[i]; U8 level = switch_poll_logical (sw); if (level) count++; } /* Each device keeps a 'virtual count' of balls that it knows are in the device but which are not seen by any switches. The core system cannot determine what this count should be, but just includes it in the overall count. See APIs for below for game code to update the virtual count. */ count += dev->virtual_count; dev->actual_count = count; return (count); }
/** Task that is restarted anytime a flipper button is initially * pressed. It polls the flipper switches continously to * see if it is being held down for the amount of time necessary * to start the status report. */ void status_report_monitor (void) { /* A count of 40 equates to a 4s hold down period. */ U8 count = 40; /* Wait until the player has kept the flipper button(s) down * for an extended period of time. Abort as soon as the * flippers are released. */ while (--count > 0) { if (!switch_poll_logical (SW_LEFT_BUTTON) && !switch_poll_logical (SW_RIGHT_BUTTON)) goto done; task_sleep (TIME_100MS); } /* OK, start the status report deff */ start: deff_start (DEFF_STATUS_REPORT); /* Wait until the flippers are released, or the status report * deff terminates. If the flippers are released, then * exit the task. If the status report finishes, go back to * the beginning -- after another 5sec, it will get started * again. */ for (;;) { if (!switch_poll_logical (SW_LEFT_BUTTON) && !switch_poll_logical (SW_RIGHT_BUTTON)) break; if (deff_get_active () != DEFF_STATUS_REPORT) { task_sleep_sec (5); goto start; } task_sleep (TIME_100MS); } /* Stop the status report */ deff_stop (DEFF_STATUS_REPORT); done: task_exit (); }
CALLSET_ENTRY (outhole, ball_search) { while (switch_poll_logical (SW_OUTHOLE)) { sol_request (SOL_OUTHOLE); task_sleep_sec (1); } }
void pb_test_draw (void) { dmd_alloc_low_clean (); font_render_string_center (&font_mono5, 64, 0, "POWERBALL TEST"); if (pb_location == PB_MISSING) sprintf ("LOST"); else if (pb_location & PB_IN_LOCK) sprintf ("LOCK"); else if (pb_location & PB_IN_TROUGH) sprintf ("TROUGH"); else if (pb_location & PB_IN_GUMBALL) sprintf ("GUMBALL"); else if (pb_location & PB_IN_PLAY) sprintf ("P.F."); else if (pb_location & PB_MAYBE_IN_PLAY) sprintf ("NOT SURE"); else sprintf ("ERROR"); font_render_string_left (&font_mono5, 0, 6, sprintf_buffer); if (pb_location & (PB_IN_LOCK | PB_IN_TROUGH)) { sprintf ("POS. %d", pb_depth); font_render_string_right (&font_mono5, 127, 6, sprintf_buffer); } sprintf ("TROUGH SW. %s", switch_poll_logical (SW_TROUGH_PROXIMITY) ? "CLOSED" : "OPEN"); font_render_string_left (&font_mono5, 0, 12, sprintf_buffer); sprintf ("SLOT SW. %s", switch_poll_logical (SW_SLOT_PROXIMITY) ? "CLOSED" : "OPEN"); font_render_string_left (&font_mono5, 0, 18, sprintf_buffer); switch (pb_test_command) { case KICK_TROUGH: sprintf ("REL. TROUGH"); break; case KICK_LOCK: sprintf ("REL. LOCK"); break; case RELEASE_GUMBALL: sprintf ("REL. GUMBALL"); break; case LOAD_GUMBALL: sprintf ("LOAD GUMBALL"); break; } font_render_string_left (&font_mono5, 0, 24, sprintf_buffer); dmd_show_low (); }
bool sssmb_can_divert_to_plunger (void) { if (global_flag_test (GLOBAL_FLAG_SSSMB_RUNNING) && sssmb_ramps_to_divert == 0 && !switch_poll_logical (SW_SHOOTER)) return TRUE; else return FALSE; }
CALLSET_ENTRY (trivial, sw_plunger) { if (!switch_poll_logical (SW_PLUNGER)) { sample_start (SND_BURNOUT_01, SL_2S); // @TODO leff_start (LEFF_SHOOTER); } callset_invoke (sw_shooter); }
/* Function to wait 6 seconds or until the autofire is detected as loaded */ static inline void autofire_ball_catch_wait (void) { U8 timeout = 60; /* 6 seconds */ while (!switch_poll_logical (SW_AUTOFIRE2) && --timeout != 0) { task_sleep (TIME_100MS); } }
CALLSET_ENTRY (trivial, sw_right_plunger) { if (!switch_poll_logical (SW_RIGHT_PLUNGER)) { sample_start (SND_PLUNGE, SL_2S); leff_start (LEFF_SHOOTER); } callset_invoke (sw_shooter); }
CALLSET_ENTRY (coin_door, sw_coin_door_closed) { /* Be kind and ignore slam tilt switch briefly after the coin door is opened/closed */ event_can_follow (sw_coin_door_closed, sw_slam_tilt, TIME_5S); if (switch_poll_logical (SW_COIN_DOOR_CLOSED)) coin_door_closed (); else coin_door_opened (); }
/** Display effect when locating missing balls prior to game start */ void locating_balls_deff (void) { /* For Funhouse/Road Show, if a ball is sitting in the left plunger lane, announce that, as ball search isn't going to fix it. */ #ifdef MACHINE_LEFT_SHOOTER_SWITCH if (switch_poll_logical (MACHINE_LEFT_SHOOTER_SWITCH)) generic_deff ("CLEAR BALL FROM", "LEFT SHOOTER"); else #else generic_deff ("LOCATING BALLS", "PLEASE WAIT..."); #endif }
void amode_right_button_detect (void) { /* Exit early if the player releases the button or presses the other * button at the same time */ U8 hold = TIME_5S / TIME_100MS; while (hold > 0) { if (!switch_poll_logical (SW_RIGHT_BUTTON) || switch_poll_logical (SW_LEFT_BUTTON)) { task_exit (); } task_sleep (TIME_100MS); hold--; } #ifdef MACHINE_AMODE_FLIPPER_SOUND_CODE sound_send (MACHINE_AMODE_FLIPPER_SOUND_CODE); #endif /* Switch to the scores page on the next page change */ amode_show_scores_long = TRUE; }
/** * If we see the shooter at any other time than a trough kick, * we will autolaunch it but not if the door is open or we are * in tournament mode. */ CALLSET_ENTRY (serve, sw_shooter) { #ifdef MACHINE_SHOOTER_SWITCH if (!switch_poll_logical (MACHINE_SHOOTER_SWITCH)) return; ball_search_timer_reset (); if ( valid_playfield && !tournament_mode_enabled && !global_flag_test (GLOBAL_FLAG_COIN_DOOR_OPENED) && !MB_SERVING) { task_sleep (MACHINE_SHOOTER_SWITCH_DELAY); launch_ball (); }//end of if #endif } //end of function
CALLSET_ENTRY (skill_menu, any_pf_switch) { dbprintf ("skill_menu: any_pf_switch\n"); if (skill_menu_enabled == 0) { return; } // wait for the ball to be fired off the plunger switch, which is marked as a playfield switch so // that ball-search does not start when a ball is resting on it. if (switch_poll_logical (SW_PLUNGER)) { return; } skill_menu_select(); }
/** Called at game start time to see if it is OK to * start a game. This routine should check that all * balls are accounted for, and at least 1 ball is * in the trough. */ bool device_check_start_ok (void) { U8 truly_missing_balls; /* Reset any kickout locks, just in case */ kickout_unlock_all (); /* If any balls are missing, don't allow the game to start * without first trying a device probe. * * If the device probe is already in progress, then just * return right away. */ if (task_find_gid (GID_DEVICE_PROBE)) return FALSE; truly_missing_balls = missing_balls; #ifdef MACHINE_SHOOTER_SWITCH if (switch_poll_logical (MACHINE_SHOOTER_SWITCH)) truly_missing_balls--; #endif /* If some balls are unaccounted for, and not on the shooter, * then start a device probe and a ball search. Alert the user * by displaying a message. * * After 3 probes, allow the game to start anyway. However, * if no balls are accounted for anywhere, then don't do that. */ if (truly_missing_balls > 0) { dbprintf ("%d balls missing.\n", truly_missing_balls); if ((++device_game_start_errors <= 3) || (counted_balls == 0)) { task_recreate_gid (GID_DEVICE_PROBE, device_probe); ball_search_now (); deff_start (DEFF_LOCATING_BALLS); return FALSE; } else return TRUE; } /* All checks pass : OK to start game now */ return TRUE; }
static bool coin_door_warning_needed (void) { #ifdef CONFIG_COIN_DOOR_WARNING static bool already_warned = 0; if (already_warned && switch_poll_logical (SW_COIN_DOOR_CLOSED)) return 0; if (!already_warned) { deff_start (DEFF_COIN_DOOR_BUTTONS); already_warned = 1; } return 1; #else return 0; #endif }
void shooter_update (void) { #ifdef MACHINE_SHOOTER_SWITCH /* A ball seen on the shooter switch means it is definitely there. * If not seen, it might be out of the plunger lane, or it may just be * "on its way up". During that interval, still consider the ball * at the plunger, so delay clearing the flag. */ if (switch_poll_logical (MACHINE_SHOOTER_SWITCH)) { task_kill_gid (GID_SHOOTER_CLEAR); global_flag_on (GLOBAL_FLAG_BALL_AT_PLUNGER); } else { task_create_gid1 (GID_SHOOTER_CLEAR, shooter_clear_monitor); } #endif }
CALLSET_ENTRY (skill, sw_shooter) { /* Because the shooter switch is declared as an 'edge' switch, an event is generated on both transitions. Check the current state of the switch to see which transition occurred. */ if (!in_live_game) return; if (!switch_poll_logical (SW_SHOOTER)) { sound_send (SND_SHOOTER_PULL); leff_restart (LEFF_STROBE_UP); timer_restart_free (GID_SHOOTER_SOUND_DEBOUNCE, TIME_3S); } else { leff_stop (LEFF_STROBE_UP); } }
/* Called whenever the far left trough switch is tripped. The sole purpose of this to determine when there are too many balls in the trough, and one needs to be fired into the gumball out of the way. If a ball remains on this switch for 3 seconds, then it is assumed there are 4 balls in the trough and one must be loaded. */ void sw_far_left_trough_monitor (void) { U8 timeout = TIME_3S / TIME_200MS; device_t *dev = device_entry (DEVNO_TROUGH); while (task_find_gid (GID_DEVICE_PROBE)) task_sleep_sec (1); dbprintf ("Far left trough check\n"); /* Poll the switch for up to 3 seconds. If it ever opens, then abort. It must stay closed and the trough must remain full in order for us to continue. */ while (timeout > 0) { task_sleep (TIME_200MS); if ((!switch_poll_logical (SW_FAR_LEFT_TROUGH)) || (dev->actual_count != dev->size)) { dbprintf ("Far left trough not stable\n"); task_exit (); } timeout--; } /* If a ball is known to be in play, then delay the load */ while (valid_playfield) { dbprintf ("Far left trough : waiting to load\n"); task_sleep_sec (1); } /* Start the load */ dbprintf ("Far left trough stable : loading gumball\n"); if (hold_balls_in_autofire) { autofire_add_ball (); } else gumball_load_from_trough (); task_exit (); }
CALLSET_ENTRY (tilt, sw_slam_tilt) { /* Ignore slam tilt switch entirely while coin door is open, and configured for tournament mode. This is to avoid inadvertent slam tilts while dealing with problems. */ if (system_config.tournament_mode && !switch_poll_logical (SW_COIN_DOOR_CLOSED)) return; /* Ignore right after a coin door open/close */ if (nonball_event_did_follow (sw_coin_door_closed, sw_slam_tilt)) return; /* Kill the current game */ stop_game (); /* Disable coins briefly, the whole point of the slam tilt */ event_can_follow (sw_slam_tilt, sw_coin, TIME_5S); /* Start the slam tilt effect */ callset_invoke (slam_tilt); deff_start (DEFF_SLAM_TILT); leff_restart (LEFF_TILT); /* Audit the event */ audit_increment (&system_audits.slam_tilts); /* When slamtilt penalty adjustment is enabled, remove a credit. */ if (price_config.slamtilt_penalty) remove_credit (); while (deff_get_active () == DEFF_SLAM_TILT) task_sleep (TIME_66MS); /* TODO: wait for slam switch to become stable, to avoid * endless restarts */ warm_reboot (); }
/** Request that a new ball be autolaunched into play. */ void autofire_add_ball (void) { autofire_request_count++; if (!in_game || switch_poll_logical (SW_FAR_LEFT_TROUGH)) { /* For special situations. If not in game, the kick_attempt hook won't be called to open for trough. If far left trough is set, the kick will appear failed because the trough count won't change (even though a ball was successfully kicked). In these cases, do it manually. However, you get no retry capability here. */ autofire_open_for_trough (); /* Wait for divertor to open */ task_sleep_sec (2); sol_request (SOL_BALL_SERVE); } else { /* The normal way to kick a ball from the trough. * dev_trough_kick_attempt will be called next */ device_request_kick (device_entry (DEVNO_TROUGH)); } }
/** A monitor task that checks whether or not a ball search is necessary. This task periodically bumps a counter, which is normally reset as scoring switches are triggered. If the counter reaches a threshold, and ball search is allowed to run, then it is initiated. This task is also responsible for incrementing the ball time statistic, when ball search is not necessary. */ void ball_search_monitor_task (void) { ball_search_timer_reset (); while (in_game) { task_sleep (TIME_1S); /* Step the ball search timer as long as a game * is in progess. But don't allow a ball search in * some situations: * * - ball is on the shooter switch * - either flipper button is held */ if (in_live_game && !in_bonus && (live_balls || !valid_playfield) #ifdef MACHINE_SHOOTER_SWITCH && !switch_poll_logical (MACHINE_SHOOTER_SWITCH) #endif && !switch_poll_logical (SW_LEFT_BUTTON) && !switch_poll_logical (SW_RIGHT_BUTTON)) { ball_time++; ball_search_timer_step (); if (ball_search_timed_out ()) { ball_search_count = 0; while (ball_search_timer != 0) { if ((ball_search_count >= 5) && chase_ball_enabled ()) { /* If chase ball is enabled, after the 5th ball search we will force endball. */ audit_increment (&system_audits.chase_balls); end_ball (); return; } else { /* Perform a ball search */ ball_search_run (); } /* After the third ball search, cancel the tilt lamp effect, to help the player find the missing ball. */ if (ball_search_count == 3) leff_stop (LEFF_TILT); if (ball_search_count < 10) { /* Delay a small amount for the first few ball searches */ task_sleep_sec (12); } else { /* Delay longer after many ball searches */ task_sleep_sec (20); } /* After a while, just give up -- but don't do that in tournament mode or on free play; this is just to keep a location game from cycling continuously. */ if (ball_search_count >= 25 && !price_config.free_play && !system_config.tournament_mode) { fatal (ERR_BALL_SEARCH_TIMEOUT); } } /* A ball was seen -- clear the counter and exit */ ball_search_count = 0; } } } task_exit (); }
CALLSET_ENTRY (skill_menu, sw_plunger) { if (!switch_poll_logical (SW_PLUNGER)) { dbprintf ("skill_menu: sw_plunger\n"); skill_menu_select(); } }
CALLSET_ENTRY (eject, amode_start) { if (switch_poll_logical (SW_EJECT) ) sol_request (SOL_EJECT); //request to fire the eject sol }//end of function
CALLSET_ENTRY (outhole, init) { if (switch_poll_logical (SW_OUTHOLE)) sol_request (SOL_OUTHOLE); }