void rudy_eye_update (void) { switch (EYE_DIRECTION (rudy_eyes)) { case EYES_STRAIGHT: eye_direction_stop (); break; case EYES_LEFT: eye_direction_start_reverse (); task_recreate_gid_while (GID_RUDY_EYE_TIMEOUT, rudy_eyedir_timeout, TASK_DURATION_INF); break; case EYES_RIGHT: eye_direction_start_forward (); task_recreate_gid_while (GID_RUDY_EYE_TIMEOUT, rudy_eyedir_timeout, TASK_DURATION_INF); break; } switch (EYE_LID(rudy_eyes)) { case EYELID_NORMAL: case EYELID_OPEN: sol_request (SOL_EYELIDS_OPEN); break; case EYELID_CLOSED: sol_request (SOL_EYELIDS_CLOSED); break; } task_sleep_sec (2); task_exit (); }
CALLSET_ENTRY (trapdoor, ball_search) { trap_door_state = DISABLED; sol_request (SOL_TRAP_DOOR_CLOSE); task_sleep_sec (2); sol_request (SOL_TRAP_DOOR_OPEN); task_sleep_sec (2); trap_door_state = OPEN; trapdoor_device_update (); }
static void trap_door_pulse (void) { if (trap_door_state == CLOSED) { sol_request (SOL_TRAP_DOOR_CLOSE); if (in_live_game && valid_playfield) sample_start (SND_BOOM1, SL_1S); } else sol_request (SOL_TRAP_DOOR_OPEN); task_sleep (TIME_500MS); /* ODO - read 'closed' sw. to see if it worked OK */ task_exit (); }
static void lock_and_outhole_monitor (void) { /* Wait for balls to settle/amode to start before emptying * locks/outhole */ task_sleep_sec (3); while (!in_live_game) { if (switch_poll (SW_LOCK_LOWER)) { device_request_kick (device_entry (DEVNO_LOCK)); } if (switch_poll (SW_OUTHOLE)) { sol_request (SOL_OUTHOLE); } if (!switch_poll (SW_AUTOFIRE2)) { callset_invoke (clear_autofire); } /* Wait for the balls to be cleared before starting again */ task_sleep_sec (3); } task_exit (); }
void eject_video_mode_finished(void) { // task_create_gid1(GID_EJECT_LEFF, eject_leff); sol_request (SOL_EJECT); //request to fire the eject sol ball_search_monitor_start (); // task_sleep (TIME_300MS); // flag_off(FLAG_KILL_NORMAL_EJECT); } //end of function
/** A task that manages the autolaunching of balls. Upon entry, the autofire divertor solenoid is already pulsing and a ball is being kicked from the trough. */ void autofire_monitor (void) { /* Open the divertor to catch the ball. Because it may be coming from either the trough or a ramp divert, the timings are variable. */ if (shooter_div_delay_time) task_sleep_sec (shooter_div_delay_time); autofire_busy = TRUE; //if (autofire_full () // don't open to catch shooter_div_start (); /* TODO - If the autofire switch trips during the 'open time', we can abort this delay early and go ahead and close the divertor. This is safe because only one ball can appear here at a time. */ //task_sleep_sec (shooter_div_open_time); autofire_ball_catch_wait (); shooter_div_stop (); /* Wait a little longer for the ball to settle */ task_sleep (TIME_200MS); /* If Right inlane -> Left ramp combo, start tnf mode */ if (event_did_follow (left_ramp_exit, tnf) && single_ball_play ()) { callset_invoke (tnf_start); } /* Wait until allowed to kickout */ while (kickout_locks > 0) task_sleep (TIME_100MS); /* Open diverter again */ shooter_div_start (); /* Wait for the diverter to fully open before firing */ U8 timeout = 20; while (--timeout != 0) task_sleep (TIME_100MS); if (in_live_game && single_ball_play ()) { sound_send (SND_EXPLOSION_1); leff_start (LEFF_STROBE_UP); } /* Say that the ball is heading into the right loop */ timer_restart_free (GID_BALL_LAUNCH, TIME_3S); event_can_follow (autolaunch, right_loop, TIME_4S); /* Clear the magnet so we can fire a ball */ magnet_disable_catch (MAG_RIGHT); /* Launch the ball */ sol_request (SOL_AUTOFIRE); /* Wait for the ball to clear the divertor * before closing*/ task_sleep (TIME_700MS); shooter_div_stop (); autofire_busy = FALSE; task_exit (); }
CALLSET_ENTRY (outhole, ball_search) { while (switch_poll_logical (SW_OUTHOLE)) { sol_request (SOL_OUTHOLE); task_sleep_sec (1); } }
CALLSET_ENTRY (autofire, clear_autofire) { /* Used to empty autofire if found full * during attract mode */ shooter_div_start (); task_sleep_sec (2); sol_request (SOL_AUTOFIRE); task_sleep_sec (1); shooter_div_stop (); }
void rules_hitchhiker_leff (void) { for (;;) { lamp_tristate_flash (LM_RIGHT_JET); lamp_tristate_flash (LM_CAMERA); task_sleep_sec (2); lamp_tristate_off (LM_RIGHT_JET); lamp_tristate_off (LM_CAMERA); sol_request (SOL_UR_FLIP_POWER); task_sleep (TIME_500MS); } }
void rules_spiralaward2_leff (void) { for (;;) { lamplist_apply (LAMPLIST_SPIRAL_AWARDS, lamp_flash_on); lamp_tristate_flash (LM_RIGHT_SPIRAL); lamp_tristate_flash (LM_RIGHT_POWERBALL); task_sleep_sec (1); sol_request (SOL_LL_FLIP_POWER); lamp_tristate_off (LM_RIGHT_SPIRAL); lamp_tristate_off (LM_RIGHT_POWERBALL); task_sleep (TIME_500MS); } }
void rules_doinks_leff (void) { for (;;) { lamp_tristate_flash (LM_RIGHT_INLANE); task_sleep_sec (1); sol_request (SOL_LR_FLIP_POWER); lamp_tristate_off (LM_RIGHT_INLANE); lamp_tristate_flash (LM_SUPER_SKILL); task_sleep (TIME_500MS); lamp_tristate_off (LM_SUPER_SKILL); lamp_tristate_flash (LM_MULTIBALL); task_sleep (TIME_500MS); lamp_tristate_off (LM_MULTIBALL); lamp_tristate_flash (LM_BONUS_X); task_sleep (TIME_500MS); lamp_tristate_off (LM_BONUS_X); } }
void gumball_release_task (void) { event_should_follow (gumball_release, gumball_exit, TIME_4S); while (gumball_pending_releases > 0) { gumball_geneva_tripped = FALSE; gumball_exit_tripped = FALSE; /* Original timeout was 90x33ms = 3sec */ /* Shorter timeout will work when geneva is broken */ timeout = 25; while ((gumball_geneva_tripped == FALSE) && (gumball_exit_tripped == FALSE) && (--timeout > 0)) { sol_request (SOL_GUMBALL_RELEASE); task_sleep (TIME_33MS); } sol_stop (SOL_GUMBALL_RELEASE); gumball_running = FALSE; bounded_decrement (gumball_pending_releases, 0); } task_exit (); }
/** 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)); } }
CALLSET_ENTRY (eject, dev_eyeball_eject_enter) { //HUXLEY MADE if ( !flag_test(FLAG_BACK_IN_THE_FRIDGE_RUNNING) && task_kill_gid(GID_LEFT_INLANE_MADE) ) { // flag_on(FLAG_KILL_NORMAL_EJECT); huxley_made(); return; // task_sleep (TIME_300MS);//give time for huxley to catch up } //START VIDEO MODE else if ( !flag_test(FLAG_BACK_IN_THE_FRIDGE_RUNNING) && !flag_test(FLAG_IS_FORTRESS_MB_RUNNING) && !flag_test(FLAG_IS_MUSEUM_MB_RUNNING) && !flag_test(FLAG_IS_CRYOPRISON_MB_RUNNING) && !flag_test(FLAG_IS_WASTELAND_MB_RUNNING) && !flag_test(FLAG_IS_DEMOTIME_RUNNING) && flag_test (FLAG_VIDEO_MODE_ENABLED) ) { // flag_on(FLAG_KILL_NORMAL_EJECT); start_video_mode(3); sound_start (ST_ANY, SPCH_PLAYER_TWO, SL_1S, PRI_GAME_QUICK5); task_sleep (TIME_500MS); task_sleep (TIME_500MS); task_sleep (TIME_500MS); task_sleep (TIME_500MS); combo_rehit_check (); //check to see if enough combos to relight video mode sol_request (SOL_EJECT); //request to fire the eject sol ball_search_monitor_start (); // return; task_sleep (TIME_300MS); }//end of start video mode //NORMAL RETINA SCAN else //if (!flag_test(FLAG_KILL_NORMAL_EJECT) ) { leff_start (LEFF_EJECT); sound_start (ST_SAMPLE, RETINA_SCAN_LONG, SL_4S, PRI_GAME_QUICK1); score (EJECT_SCORE); //100k per jet hit here if (jet_shots_made > 0) { score_zero (temp_score);//zero out temp score score_add (temp_score, score_table[SC_100K]);//multiply 100K by jet count score_mul (temp_score, jet_shots_made);//multiply 100K by jet count score_long (temp_score); //add temp score to player's score }//end of if if (retina_scan_multiplier == 2) { sound_start(ST_SPEECH, SPCH_DOUBLE_RETINA_SCAN, SL_4S, PRI_GAME_QUICK5); score (EJECT_SCORE); score_long (temp_score); //add temp score to player's score } deff_start (DEFF_EJECT_EFFECT); task_sleep_sec (1); sol_request (SOL_EJECT); //request to fire the eject sol }//end of else !flag_test(FLAG_KILL_NORMAL_EJECT) }//end of function
void rules_deff (void) { music_disable (); music_set (MUS_RESTART_PLUNGER); sol_request (SOL_EYELIDS_OPEN); rule_begin (); rule_msg ("HOW TO PLAY", "FUNHOUSE"); task_create_gid1 (GID_RULES_LEFF, rules_flasher_leff); rule_complete (); rule_begin (); rule_msg ("ALL TARGETS", "ADVANCE CLOCK"); task_create_gid1 (GID_RULES_LEFF, rules_clock_leff); rule_complete (); rule_begin (); rule_msg ("REACH 11:30", "TO LIGHT LOCK"); fh_clock_set (11, TIME_30_MIN); lamp_tristate_flash (LM_LOCK); rule_complete (); rule_begin (); rule_msg ("LOCK BALL 1", "FOR 11:45"); fh_clock_set (11, TIME_45_MIN); lamp_tristate_flash (LM_LOCK); rule_complete (); rule_begin (); rule_msg ("LOCK BALL 2", " FOR 12:00"); fh_clock_set (12, TIME_0_MIN); lamp_tristate_flash (LM_MILLION); sol_request (SOL_EYELIDS_CLOSED); rule_complete (); rule_begin (); rule_msg ("WAKE RUDY", "FOR MULTIBALL"); sol_request (SOL_EYELIDS_OPEN); rule_complete (); rule_begin (); rule_msg ("SHOOT TRAP DOOR", "FOR JACKPOTS"); lamp_tristate_flash (LM_MILLION_PLUS); rule_complete (); rule_begin (); rule_msg ("SHOOT MIRROR TO", "COLLECT AWARDS"); lamplist_apply (LAMPLIST_MIRROR_AWARDS, lamp_flash_on); lamp_tristate_flash (LM_MIRROR_VALUE); rule_complete (); rule_begin (); rule_msg ("HIT RUDY TO", "RELIGHT MIRROR"); lamplist_apply (LAMPLIST_MIRROR_AWARDS, lamp_on); rule_complete (); rule_begin (); rule_msg ("BLUE TARGETS", "LIGHT THE STEPS"); lamplist_apply (LAMPLIST_STEP_TARGETS, lamp_flash_on); lamplist_apply (LAMPLIST_STEPS_AWARDS, lamp_flash_on); rule_complete (); rule_begin (); rule_msg ("SHOOT STEPS FROM", "LEFT PLUNGER"); lamplist_apply (LAMPLIST_STEPS_AWARDS, lamp_flash_on); lamp_tristate_flash (LM_STEPS_GATE_OPEN); lamp_tristate_flash (LM_RAMP_STEPS); rule_complete (); rule_begin (); rule_msg ("SHOOT LOOPS TO", "COLLECT GANGWAYS"); lamp_flash_on (LM_LEFT_GANGWAY); lamp_flash_on (LM_RIGHT_GANGWAY); lamplist_apply (LAMPLIST_GANGWAYS, lamp_on); rule_complete (); rule_begin (); rule_msg ("EXTRA BALLS LIT", "AT TOP LOOP"); lamp_on (LM_EXTRA_BALL); lamp_flash_on (LM_MIRROR_EX_BALL); lamp_flash_on (LM_GANGWAY_EX_BALL); lamp_flash_on (LM_STEPS_EB); rule_complete (); rule_begin (); rule_msg ("PLAY", "FUNHOUSE"); task_create_gid1 (GID_RULES_LEFF, rules_flasher_leff); task_create_gid1 (GID_RULES_LEFF, rules_clock_leff); rule_complete (); sol_request (SOL_EYELIDS_CLOSED); music_enable (); deff_exit (); }
CALLSET_ENTRY (slot, ball_search) { sol_request (SOL_SLOT); }
CALLSET_ENTRY (outhole, init) { if (switch_poll_logical (SW_OUTHOLE)) sol_request (SOL_OUTHOLE); }
//this should reach her if there are two balls on the saucer in rapid succession or if first ball doesn't leave CALLSET_ENTRY (eject, dev_eyeball_eject_kick_failure) { eject_kick_attempts++; task_sleep (TIME_1S); sol_request (SOL_EJECT); //request to fire the eject sol }//end of function
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 (eject, dev_eyeball_eject_enter) { if (!in_game) { sol_request (SOL_EJECT); task_sleep (TIME_1S); } else { //HUXLEY MADE if ( !get_back_in_the_fridge_running() && !get_back_in_the_fridge_ending() && task_kill_gid(GID_LEFT_INLANE_MADE) && !in_tilt) { is_normal_eject_killed = TRUE; huxley_made(); return; }//end of huxley made //START VIDEO MODE else if ( (system_config.disable_kaboom == NO || system_config.disable_simon_says == NO || system_config.disable_shooter == NO) && flag_test (FLAG_VIDEO_MODE_ENABLED) && !in_tilt && !get_back_in_the_fridge_running() && !get_back_in_the_fridge_ending() && !get_fortress_running () && !get_museum_running () && !get_cryoprison_running () && !get_wasteland_running () && !get_demotime_running () && !is_frenzy_running () ) { //this is here since starting frenzy dumps ball into eject hole switch (get_next_video_mode() ) { case 0: start_kaboom(); break; case 1: start_simon_says(); break; case 2: start_shooter(); break; }//end of switch combo_rehit_check (); //check to see if enough combos to relight video mode //we just returned from video mode so flash the lights before you fire the coil leff_start (LEFF_EJECT); task_sleep (TIME_500MS); sol_request (SOL_EJECT); //request to fire the eject sol task_create_gid1 (GID_EJECT_1, eject_switch_task); ball_search_monitor_start (); task_sleep (TIME_300MS); }//end of start video mode //NORMAL RETINA SCAN else if (!is_normal_eject_killed ) { if (!in_tilt) { score (EJECT_SCORE); //100k per jet hit here if (jet_shots_made > 0) { score_zero (temp_score);//zero out temp score score_add (temp_score, score_table[SC_100K]);//multiply 100K by jet count score_mul (temp_score, jet_shots_made);//multiply 100K by jet count score_long (temp_score); //add temp score to player's score }//end of if if (retina_scan_multiplier == 2) { sound_start(ST_SPEECH, SPCH_DOUBLE_RETINA_SCAN, SL_4S, PRI_GAME_QUICK4); score (EJECT_SCORE); score_long (temp_score); //add temp score to player's score } leff_start (LEFF_EJECT); sound_start (ST_SAMPLE, RETINA_SCAN_LONG, SL_4S, PRI_GAME_QUICK1); deff_start (DEFF_EJECT_EFFECT); task_sleep (TIME_300MS); }//end of not in tilt sol_request (SOL_EJECT); //request to fire the eject sol task_create_gid1 (GID_EJECT_1, eject_switch_task);//this is a 2 second ball save timer for eject coil }//end of else !is_normal_eject_killed = FALSE }//end of else in game }//end of function
CALLSET_ENTRY (outhole, sw_outhole) { /* Wait for ball to settle */ task_sleep (TIME_100MS); sol_request (SOL_OUTHOLE); }
/** The core function for handling a device. * This function is invoked (within its own task context) whenever * a switch closure occurs on a device, or when a request is made to * kick a ball from a device. * The update task will get killed and restarted anytime a switch * closure occurs on the device, so it may be interrupted, but only * while it is sleeping. */ void device_update (void) { device_t *dev = &device_table[device_getgid () - DEVICE_GID_BASE]; wait_and_recount: /* We are really interested in the total count of the * device, not which switches contributed to it. * Since multiple switch transitions occur as a ball * "slides through", don't act on a transition right * away. Instead, wait awhile until no further transitions * occur, so that the count is stable. If another closure on * this device happens while we sleep here, this task will * be killed and restarted. */ task_sleep (dev->props->settle_delay); start_update: task_sleep (TIME_50MS); /* The device is probably stable now. Poll all of the * switches and recount */ device_recount (dev); device_update_globals (); device_debug (dev); dbprintf ("Updating device %s\n", dev->props->name); /***************************************** * Handle "count" changes *****************************************/ if (dev->state == DEV_STATE_IDLE) { /* Device is idle */ if (dev->actual_count == dev->previous_count) { /* Switch closures were detected but in the end, after becoming * stable, the count did not change. This is OK, perhaps * there is some vibration... * Also, when transitioning back to idle after a device kick, * we repoll the switches one extra time and if all is well, * we'll end up here. */ } else if (dev->actual_count < dev->previous_count) { /* Also unusual in that a ball came out of the device without * explicitly kicking it. (Although this can happen in test mode.) * Note that the number of balls in play went up */ if (in_game) { device_call_op (dev, surprise_release); device_add_live (); } } else if (dev->actual_count > dev->previous_count) { /* More typical : when idle, the count should only go up. * Treat this as an enter event (or multiple events, if the * count goes up by more than 1). */ U8 enter_count = dev->actual_count - dev->previous_count; if (!trough_dev_p (dev)) set_valid_playfield (); while (enter_count > 0) { callset_invoke (any_device_enter); device_call_op (dev, enter); enter_count--; } } } else if ((dev->state == DEV_STATE_RELEASING) && (dev->kicks_needed > 0)) { /* Device is in the middle of a release cycle. * See if the count changed. */ if (unlikely (dev->actual_count >= dev->previous_count)) { /* After attempting a release, the count did not go down ... the kick * probably failed, and we should retry up to a point. Since dev->state * is unchanged below, the kick attempt will get reinvoked. */ /* Note: during multiball, it is possible for a second ball to enter the device immediately after the kick. The kick didn't really fail, but there's no way to tell the difference. */ dev->kick_errors++; dbprintf ("Kick error %d\n", dev->kick_errors); device_call_op (dev, kick_failure); if (dev->kick_errors == (trough_dev_p (dev) ? 20 : 7)) { /* OK, we tried too many times and still no ball came out. * Cancel all kick requests for this device unless it is * the trough */ nonfatal (ERR_FAILED_KICK); dev->kicks_needed = 0; dev->state = DEV_STATE_IDLE; } else { /* Wait awhile before trying again. The more we fail, the longer the delay in between tries. */ if (dev->kick_errors <= 3) task_sleep_sec (1); else task_sleep_sec (5); } } else if (dev->actual_count < dev->previous_count) { /* The count decreased as expected. * As we only kick 1 ball at a time, then really it * only should have gone down by 1, but the logic * should work even if more than 1 ball is ejected. */ U8 kicked_balls = dev->previous_count - dev->actual_count; /* If too many balls were kicked, throw an error. Only process as many as were requested. */ if (kicked_balls > dev->kicks_needed) { nonfatal (ERR_KICK_TOO_MANY); /* akin to ERR_IDLE_BALL_LOST */ kicked_balls = dev->kicks_needed; } /* Throw a kick success event for each ball that was kicked */ while (kicked_balls > 0) { device_call_op (dev, kick_success); dev->kicks_needed--; kicked_balls--; } /* Go back to idle state. If there are more kicks left, we will switch back to DEV_STATE_RELEASING again later. The point is not to stay in DEV_STATE_RELEASING when we have not actually kicked the ball; if the request is held up for some reason, we want switch closures to be processed correctly. */ dev->state = DEV_STATE_IDLE; } } /***************************************** * Handle global count changes *****************************************/ device_update_globals (); /************************************************ * Handle counts larger than the device supports ************************************************/ if (dev->kicks_needed == 0 && dev->actual_count > dev->max_count) { /* When there are more balls in the device than we normally want to keep here, we must kick one of them out. If multiple kicks are needed, this check will occur again in the future. */ dev->kicks_needed++; dev->kick_errors = 0; /* TODO - device_request_kick (dev); would be more appropriate, * but that doesn't work when called from device context due * to live balls getting bumped */ } /************************************************ * Handle kicking out balls ************************************************/ if (dev->kicks_needed > 0) { if (dev->actual_count == 0) { /* Container has fewer balls in it than we * would like */ dbprintf ("Can't kick when no balls available!\n"); dev->kicks_needed = 0; } else if (kickout_locked_p () && !trough_dev_p (dev)) { /* Container ready to kick, but 1 or more * locks are held so we must wait. */ goto start_update; } else if (!device_call_boolean_op (dev, kick_request)) { /* Inform other modules that a kick was requested. These handlers can return FALSE to delay (but not cancel) the kick. */ goto start_update; } /* TODO - if multiple devices want to kick at the same time, * they should be staggered a bit. Another case should be * added here. */ else { /* The container is ready to kick */ /* Mark state as releasing if still idle */ if (dev->state == DEV_STATE_IDLE) dev->state = DEV_STATE_RELEASING; /* TODO - keep track of all pending kick attempts. Use a bit variable per device. If other devices are in the process of kicking, wait */ /* Generate events that a kick attempt is coming */ callset_invoke (any_kick_attempt); device_call_op (dev, kick_attempt); /* Pulse the solenoid. */ sol_request (dev->props->sol); /* In timed games, a device kick will pause the game timer. * TODO : this should be a global event that other modules * can catch as well. Deal with this like we do slowtimers. */ timed_game_pause (TIME_1S); /* We don't know if the kick was successful or not yet, so wait and see what happens. */ goto wait_and_recount; } } /* Just before exiting this task, poll the switches one more time, and see if something has changed during the update. It is important that no task context switches take place here, otherwise there would be a race condition where a switch closure gets missed. */ device_recount (dev); if (dev->actual_count != dev->previous_count) goto start_update; task_exit (); }