Пример #1
0
/**
 * Autolaunch a new ball into play from the trough.  This is the
 * preferred API to use by ballsavers.
 */
void serve_ball_auto (void) {
#ifdef DEVNO_TROUGH
	/* Fall back to manual ball serve if there is no autoplunger. */
	if (!have_auto_serve_p ())  serve_ball ();
	else {
		set_valid_playfield ();
		/* TZ's autoplunger is a little different, so it is handled specially. */
#if defined(MACHINE_TZ)
		autofire_add_ball ();
#else
		device_request_kick (device_entry (DEVNO_TROUGH));
#endif
	} //end of else
#endif /* DEVNO_TROUGH */
} //end of function
Пример #2
0
CALLSET_ENTRY (tilt, sw_tilt)
{
	extern U8 in_tilt;

	/* Ignore tilt switch activity while already in tilt state.
	 * But restart the timer that tells us that the tilt is still
	 * moving, so we can delay endball. */
	if (in_tilt)
	{
		free_timer_restart (TIM_IGNORE_TILT, TIME_2S);
		return;
	}

	/* IDEA : Disable tilt while a ball search is in progress? */

	else if (++tilt_warnings == system_config.tilt_warnings)
	{
		/* Warnings exceeded... tilt the current ball */
		sound_reset ();
#ifdef CONFIG_GI
		gi_disable (PINIO_GI_STRINGS);
#endif
		deff_start (DEFF_TILT);
		leff_start (LEFF_TILT);
		free_timer_restart (TIM_IGNORE_TILT, TIME_2S);
		in_tilt = TRUE;
		task_remove_duration (TASK_DURATION_LIVE);
		task_duration_expire (TASK_DURATION_LIVE);
		flipper_disable ();
		set_valid_playfield ();
		audit_increment (&system_audits.tilts);
		audit_increment (&system_audits.plumb_bob_tilts);
		callset_invoke (tilt);
	}
	else
	{
		/* Give a warning this time */
		deff_start (DEFF_TILT_WARNING);
		leff_start (LEFF_TILT_WARNING);
		callset_invoke (tilt_warning);
	}
}
Пример #3
0
/*
 * The entry point for processing a switch transition.  It performs
 * some of the common switch handling logic before calling all
 * event handlers.  Then it also performs some common post-processing.
 * This function runs in a separate task context for each switch that
 * needs to be processed.
 */
void switch_sched_task (void)
{
	const U8 sw = (U8)task_get_arg ();
	const switch_info_t * const swinfo = switch_lookup (sw);

	/* Ignore any switch that doesn't have a processing function.
	   This shouldn't ever happen if things are working correctly, but it
		was observed on PIC games when the PIC code is broken and reporting
		bad switch returns.  genmachine ensures that all defined switches
		have a non-null value (null_function if nothing needs to be done) */
	if (swinfo->fn == 0)
		goto cleanup;

	/* For test mode : this lets it see what was the last switch
	 * to be scheduled.  Used by the Switch Edges test. */
	sw_last_scheduled = sw;

	log_event (SEV_INFO, MOD_SWITCH, EV_SW_SCHEDULE, sw);

	/* Don't service switches marked SW_IN_GAME if we're
	 * not presently in a game */
	if ((swinfo->flags & SW_IN_GAME) && !in_game)
		goto cleanup;

	/* Don't service switches not marked SW_IN_TEST, unless we're
	 * actually in test mode */
	if (!(swinfo->flags & SW_IN_TEST) && in_test)
		goto cleanup;

	/* If the switch has an associated lamp, then flicker the lamp when
	 * the switch triggers. */
	if ((swinfo->lamp != 0) && in_live_game)
	{
		task_pid_t tp = task_create_gid (GID_SWITCH_LAMP_PULSE,
			switch_lamp_pulse);

		lamp_pulse_data_t *cdata = task_init_class_data (tp, lamp_pulse_data_t);
		cdata->swinfo = swinfo;
	}

	/* If we're in a live game and the switch declares a standard
	 * sound, then make it happen. */
	if ((swinfo->sound != 0) && in_live_game)
		sound_send (swinfo->sound);

#ifdef DEBUGGER
	if (swinfo->fn != null_function && sw < 72)
	{
		dbprintf ("SW: ");
		sprintf_far_string (names_of_switches + sw);
		dbprintf1 ();
#ifdef DEBUG_SWITCH_NUMBER
		dbprintf (" (%d) ", sw);
#endif
		dbprintf ("\n");
	}
#endif

	/* If the switch declares a processing function, call it. */
	if (swinfo->fn)
		callset_pointer_invoke (swinfo->fn);

	/* Declare this as a hardware event that affects the random number
	generator. */
	random_hw_event ();

	/* If a switch is marked SW_PLAYFIELD and we're in a game,
	 * then call the global playfield switch handler and check for
	 * valid playfield.  Also, reset the ball search timer so that
	 * it doesn't expire.
	 */
	if ((swinfo->flags & SW_PLAYFIELD) && in_game)
	{
		callset_invoke (any_pf_switch);

		/* If valid playfield was not asserted yet, then see if this
		 * switch validates it.  Most playfield switches do this right
		 * away, but for some switches, like special solenoids (jets
		 * or slings), which could repeatedly trigger if misaligned,
		 * count the activations and validate only when some number
		 * of different switches have triggered.  Device counting
		 * switches are ignored here, but an 'enter' event will
		 * set it. */
		if (!valid_playfield)
		{
			if (swinfo->flags & SW_NOVALID)
			{
				if (!SW_HAS_DEVICE (swinfo))
					try_validate_playfield (sw);
			}
			else
				set_valid_playfield ();
		}
		ball_search_timer_reset ();
	}

cleanup:
	/* If the switch is part of a device, then let the device
	 * subsystem process the event.  Note this will always occur
	 * regardless of any of the above conditions checked. */
	if (SW_HAS_DEVICE (swinfo))
		device_sw_handler (SW_GET_DEVICE (swinfo));

	task_exit ();
}
Пример #4
0
CALLSET_ENTRY (skill, any_skill_switch)
{
	set_valid_playfield ();
}
Пример #5
0
/** 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 ();
}