static void set_ball_count_task (void) { device_t *dev = device_entry (DEVNO_TROUGH); U8 max_live_balls; U8 retries; U8 temp_live_balls_wanted; max_live_balls = live_balls; temp_live_balls_wanted = live_balls_wanted - live_balls; retries = 2; //first pass - quick fire balls MB_SERVING = TRUE; while (temp_live_balls_wanted) { temp_live_balls_wanted--; sol_request_async (SOL_BALLSERVE); task_sleep (TIME_500MS); //worked good at 700 - just giving a little margin here task_sleep (TIME_400MS); sol_request_async (MACHINE_LAUNCH_SOLENOID); task_sleep (TIME_500MS);//worked good at 300 - just giving a little margin here }//end of rapid fire serving MB_SERVING = FALSE; //check trough to see if live balls is accurate task_sleep (TIME_1S); live_balls = (5 - device_recount(device_entry (DEVNO_TROUGH)) ); max_live_balls = live_balls; //if still not all balls out there, retry slower - 2 times // we will usually arrive at this place on a ball-saving type multiball //where balls are constantly draining and refiring //this is okay, since the refiring will slow down which is sort of a punishment to //the player for allowing the balls to drain so fast while (max_live_balls < live_balls_wanted && retries) { retries--; /* Are there enough balls in the trough to satisfy another kick request? * If not, then we need to add the balls from somewhere else.*/ if (dev->actual_count < dev->kicks_needed) callset_invoke (trough_rescue); else serve_ball_auto (); task_sleep (SET_BALL_COUNT_TASK_DELAY); /* As long as there is a ball on the shooter, wait before trying to continue. This flag will clear once the shooter switch has cleared for a few seconds. */ while (global_flag_test (GLOBAL_FLAG_BALL_AT_PLUNGER)) task_sleep (TIME_133MS); /* See if the ball count went up, indicating success */ if (live_balls > max_live_balls) max_live_balls = live_balls; }//end of retry section task_exit (); } //end of function
/** Returns true if the given solenoid is OK to fire during * a ball search. The following should be avoided: * - kickout coils from ball devices * - the knocker * - flashers * - anything else the machine description says not to fire */ static bool ball_search_solenoid_ok (U8 sol) { device_t *dev; #if !defined(MACHINE_SOL_FLASHERP) /* If the machine description is not proper, then we can't know * for sure which solenoids are OK, so don't fire _any_. */ return (FALSE); #endif #if defined(MACHINE_SOLENOID_P) /* If it's not a valid solenoid number, don't allow it. This skips coils that are not installed. */ if (!MACHINE_SOLENOID_P (sol)) return FALSE; #endif /* Skip known coils which should always be skipped. */ if (MACHINE_SOL_FLASHERP(sol) #ifdef MACHINE_BALL_SERVE_SOLENOID || (sol == MACHINE_BALL_SERVE_SOLENOID) #endif #ifdef MACHINE_KNOCKER_SOLENOID || (sol == MACHINE_KNOCKER_SOLENOID) #endif #ifdef MACHINE_SOL_NOSEARCHP || (MACHINE_SOL_NOSEARCHP(sol)) #endif ) return (FALSE); /* Also check for all ball device kick coils; skip them */ for (dev=device_entry(0); dev < device_entry(NUM_DEVICES); dev++) { if (sol == dev->props->sol) { /* This coil controls a ball device. */ /* If there are no balls detected here, pulse it */ if (dev->actual_count == 0) return TRUE; /* If chase ball is turned off, then during the 5th ball search, pulse it */ if (!chase_ball_enabled () && ball_search_count == 5) return (TRUE); /* Default is NOT to fire such a coil */ return (FALSE); } } /* OK, you can use it. */ return (TRUE); }
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 (); }
/** * Simulate a ball drain condition. * This is simulated occasionally during multiball play, and can be * forced by pressing the Start Button. The manual approach is the * only way to end a ball, otherwise the simulation goes on forever. */ void switch_stress_drain (void) { #ifdef DEVNO_TROUGH device_t *dev = device_entry (DEVNO_TROUGH); device_call_op (dev, enter); #endif }
/** * Serve a new ball to the manual shooter lane. * This function is the preferred method to serve a ball to a manual * plunger at the beginning of a ball and after a ball lock. * It is not used for autoplunges. */ void serve_ball (void) { #ifdef DEVNO_TROUGH valid_playfield = FALSE; callset_invoke (serve_ball); effect_update_request (); device_request_kick (device_entry (DEVNO_TROUGH)); #endif /* DEVNO_TROUGH */ } //end of function
void pb_test_enter (void) { sound_send (SND_TEST_ENTER); switch (pb_test_command) { case KICK_TROUGH: device_request_kick (device_entry (DEVNO_TROUGH)); break; case KICK_LOCK: device_request_kick (device_entry (DEVNO_LOCK)); break; case RELEASE_GUMBALL: gumball_release (); break; case LOAD_GUMBALL: gumball_load_from_trough (); break; } }
/** Called by the trough when a ball has entered it. * The intent is to signal that there is one less live ball than before. */ void device_remove_live (void) { /* If any balls were missing, and now one is rediscovered in the * trough, then just hold onto it. This condition was seen when * the game thought 1 ball was in play, but 2 were on the playfield. */ if (missing_balls > live_balls) { callset_invoke (missing_ball_found); missing_balls--; } else if (live_balls > 0) { /* Decrement the count of balls in play. Now what? */ live_balls--; if (in_game && !in_bonus) { /* Notify that the ball count changed */ callset_invoke (ball_count_change); /* See if this qualifies as a ball drain. Any event receiver can return FALSE here if it is not to be treated as a drain; e.g., when a ballsaver is active. In these cases, the event function is also responsible for putting the ball back into play. */ if (!callset_invoke_boolean (ball_drain)) return; /* OK, at this point, it is a true ball drain event. See how many balls are in play now. */ switch (live_balls #ifdef DEVNO_TROUGH + device_entry (DEVNO_TROUGH)->kicks_needed #endif ) { case 0: /* With zero balls in play, this is end of ball. This function usually does not return; it will stop just about every task running to reset for the next ball. */ end_ball (); return; case 1: /* Multiball modes like to know when single ball play resumes. */ callset_invoke (single_ball_play); break; default: break; } } } }
/** * 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
/** Update the global state of the machine. This happens nearly * anytime a change happens locally within a particular device. */ void device_update_globals (void) { devicenum_t devno; U8 held_balls_now = 0; /* TODO - mostly this is called from the context of a single device. Could simplify all the recounting of other devices... */ /* Recount the total number of balls that are held, excluding those that are locked and those in the trough. */ counted_balls = 0; held_balls_now = 0; for (devno = 0; devno < NUM_DEVICES; devno++) { device_t *dev = device_entry (devno); counted_balls += dev->actual_count; if (!trough_dev_p (dev)) if (dev->actual_count > dev->max_count) held_balls_now += dev->actual_count - dev->max_count; } /* Update held_balls atomically */ held_balls = held_balls_now; /* Update count of how many balls are missing */ missing_balls = max_balls - counted_balls; /* If 'missing' went negative, this means there are more balls detected than expected. */ if (missing_balls > 0xF0) { missing_balls = 0; } dbprintf ("Counted %d Missing %d Live %d Heldup %d\n", counted_balls, missing_balls, live_balls, held_balls); /* If any balls are held up temporarily (more than "max" are * in the device presently), then delay timers */ if (in_live_game) { if (held_balls > 0) timed_game_suspend (); else timed_game_resume (); } }
CALLSET_ENTRY (ball_lock, trunk_back_shot) { if (ball_lock_can_be_collected () && balls_locked < 2) { device_t *dev = device_entry (DEVNO_SUBWAY); if (device_recount (dev) < 2) { device_lock_ball (dev); } else { /* start kickout warning effect */ } ball_lock_award (); } }
static void set_ball_count_task (void) { device_t *dev = device_entry (DEVNO_TROUGH); U8 max_live_balls; U8 retries; /* While we are launching balls, we monitor 'live_balls' to check that it is going up. */ max_live_balls = live_balls; /* Set the number of times we will attempt to kick a ball. This is the number of balls that need to be added to play, plus 2 to handle errors. After this, we give up. */ retries = live_balls_wanted - max_live_balls + 2; while (retries && max_live_balls < live_balls_wanted) { retries--; /* Are there enough balls in the trough to satisfy another kick request? If not, then we need to add the balls from somewhere else. This is machine-specific. */ if (dev->actual_count < dev->kicks_needed) { callset_invoke (trough_rescue); } else { serve_ball_auto (); } /* Wait a bit for the ball to make it to the shooter lane. */ task_sleep (TIME_2S + TIME_500MS); /* As long as there is a ball on the shooter, wait before trying to continue. This flag will clear once the shooter switch has cleared for a few seconds. */ while (global_flag_test (GLOBAL_FLAG_BALL_AT_PLUNGER)) task_sleep (TIME_133MS); /* See if the ball count went up, indicating success */ if (live_balls > max_live_balls) max_live_balls = live_balls; } task_exit (); }
/* 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 (); }
/** Initialize the device subsystem */ void device_init (void) { device_t *dev; U8 i; max_balls = MACHINE_MAX_BALLS; counted_balls = MACHINE_TROUGH_SIZE; missing_balls = 0; live_balls = 0; kickout_unlock_all (); held_balls = 0; device_game_start_errors = 0; for (i=0; i < NUM_DEVICES; i++) { dev = device_entry (i); device_clear (dev); device_register (i, &device_properties_table[i]); } }
static void set_ball_count_task (void) { device_t *dev = device_entry (DEVNO_TROUGH); U8 max_live_balls; U8 retries; /* While we are launching balls, we monitor 'live_balls' to check that it is going up. */ max_live_balls = live_balls; /* Set the number of times we will attempt to kick a ball. This is the number of balls that need to be added to play, plus 2 to handle errors. After this, we give up. */ retries = live_balls_wanted - max_live_balls + 2; while (retries && max_live_balls < live_balls_wanted) { retries--; /* Are there enough balls in the trough to satisfy another kick request? If not, then we need to add the balls from somewhere else. This is machine-specific. */ if (dev->actual_count < dev->kicks_needed) { callset_invoke (trough_rescue); } else { serve_ball_auto (); } /* Delay slightly, and see if the live count changed. We only track its maximum */ task_sleep (TIME_1500MS); /* See if the ball count went up, indicating success */ if (live_balls > max_live_balls) max_live_balls = live_balls; } task_exit (); }
/** Requests that a device allow a ball lock. */ void device_lock_ball (device_t *dev) { #ifdef DEVNO_TROUGH device_t *trough = device_entry (DEVNO_TROUGH); #endif /* If the device is already locking as many balls as it can hold, then trying to lock another ball here is an error. */ if (dev->max_count >= dev->size) fatal (ERR_LOCK_FULL_DEVICE); dbprintf ("Lock ball in devno %d\n", dev->devno); /* Update count of balls that will be held here. */ device_enable_lock (dev); /* Say that there is one less active ball on the playfield now, assuming a ball is still physically present */ if (device_kickable_count (dev)) live_balls--; /* If the trough is not empty, we can serve another ball from the trough to continue play. Otherwise, it will have to come from somewhere else. The default is to serve it from the same device, but this can be overriden by the 'empty_trough_kick' event. */ #ifdef DEVNO_TROUGH if (trough->actual_count > 0) { serve_ball (); } else #endif { if (!callset_invoke_boolean (empty_trough_kick)) device_unlock_ball (dev); } }
/** 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)); } }
/** * This task runs during a switch stress test. During a game, * it randomly invokes switch handlers as if the switches had actually * been activated by the pinball. */ void switch_stress_task (void) { U8 sw; const switch_info_t *swinfo; task_pid_t tp; /* Delay a few seconds before starting the simulation. This allows time for the Start Button to be used to add players, instead of simulating endball. */ task_sleep_sec (3); task_create_peer (switch_stress_flipper_task); for (;;) { task_sleep (TIME_100MS); if (in_test) continue; /* Choose a switch at random. Skip certain well-known switches that are * never to be activated. */ sw = random_scaled (NUM_SWITCHES); #ifdef SW_ALWAYS_CLOSED if (sw == SW_ALWAYS_CLOSED) continue; #endif #ifdef MACHINE_OUTHOLE_SWITCH if (sw == MACHINE_OUTHOLE_SWITCH) continue; #endif /* Lookup the switch properties. Skip switches which aren't normally * activated on the playfield. For switches in a ball container, * simulate device entry there, otherwise simulate a switch event. */ swinfo = switch_lookup (sw); if (SW_HAS_DEVICE (swinfo)) { device_t *dev = device_entry (SW_GET_DEVICE (swinfo)); if (trough_dev_p (dev)) { /* Don't always trigger the trough device. The probability of a ball drain is treated as proportional to the number of balls in play. So in big multiballs, we allow this to happen more frequently. We need to do this occasionally so that multiball modes will eventually end, else nothing else gets tested. */ if (random () < CONFIG_STRESS_DRAIN_PROB * live_balls) switch_stress_drain (); } else if (dev->max_count < dev->size) { /* Don't throw an enter event if the device thinks it is full: it has "locked" as many balls as it can hold. The device code will throw a fatal if it sees this, which should never happen with real balls. */ dbprintf ("Sim. enter dev %d\n", dev->devno); device_call_op (dev, enter); task_sleep (TIME_1S); } } else if (swinfo->flags & SW_PLAYFIELD) { /* Simulate the switch */ tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } } }
bool can_lock_ball; bool mb_jackpot_collected; U8 multiball_level; __local__ U8 balls_locked; extern U8 mpf_level; extern score_t total_jets_score; extern U8 rightramp_level; extern U8 left_loop_level; extern U8 right_loop_level; extern U8 total_body_parts; score_t super_jackpot_calc; score_t super_jackpot_value; device_t *dev = device_entry (DEVNO_WIRE_BALL_LOCK); void jackpot_deff (void) { seg_alloc_clean (); seg_write_row_center (0, "JACKPOT"); seg_write_row_center (1, "1 MILLION"); seg_show (); task_sleep_sec (2); deff_exit (); } void super_jackpot_deff (void) { seg_alloc_clean (); seg_write_row_center (0, "SUPER JACKPOT");
/** Probes all devices to see if any balls are present that * shouldn't be. Balls are kicked as necessary. * * For example, this clears out the balls of a lockup device * at the end of a game. * * This is always done as a background task, because it may * take some time and this function waits for any kicked * balls to successfully exit before it returns. */ void device_probe (void) { devicenum_t devno; U8 kicks; dbprintf ("Probing devices\n"); /* Keep track of the number of times we actually had to kick a device; equivalently, this is the number of times we sleep waiting for the kick to have an effect. After so long, if things aren't right, just give up. */ kicks = 0; probe_from_beginning: if (kicks >= 10) goto probe_exit; for (devno = 0; devno < NUM_DEVICES; devno++) { device_t *dev = device_entry (devno); /* Recount the number of balls in the device, and reset * other device data. */ device_recount (dev); dev->previous_count = dev->actual_count; dev->kicks_needed = 0; dev->kick_errors = 0; task_kill_gid (DEVICE_GID(devno)); /* If there are more balls in the device than ought to be, * schedule the extras to be emptied. Then rescan from * the beginning again. */ dev->max_count = dev->props->init_max_count; if (dev->actual_count > dev->max_count) { kicks++; device_request_kick (dev); task_sleep_sec (2); goto probe_from_beginning; } #if 0 /* TODO */ else if (dev->actual_count < dev->max_count) { /* The device normally holds more balls than are present in it. If possible, launch a ball here. (For example, ST:TNG or Whodunnit.) */ } #endif } probe_exit: /* At this point, all kicks have been made, but balls may be on the playfield heading for the trough. We still should wait until 'missing_balls' goes (hopefully) to zero. We'll wait for up to 10 seconds, but exit sooner if we find all balls before then. */ task_sleep_sec (4); if (missing_balls != 0) { task_sleep_sec (2); if (missing_balls != 0) { task_sleep_sec (2); if (missing_balls != 0) { task_sleep_sec (2); } } } dbprintf ("Checking globals after probe\n"); device_update_globals (); dbprintf ("\nDevices initialized.\n"); device_debug_all (); task_exit (); }
void run(int argc, char* argv[], char* env[]) { U_TRACE(5, "Application::run(%d,%p,%p)", argc, argv, env) UApplication::run(argc, argv, env); // LDAP attribute for devices // manage arg operation // manage file configuration // manage options // login to LDAP # include "common1.cpp" if (UApplication::isOptions()) { value = opt['b']; if (value.empty() == false) DN_filter = value; } // get filter attribute from LDAP int i, n = ldap.search(DN_filter.c_str(), LDAP_SCOPE_BASE, (char**)filter_attr_name); if (n != 1) U_ERROR("cannot get filter attribute from LDAP..."); ULDAPEntry filter_entry(FILTER_NUM_ATTR, filter_attr_name); ldap.get(filter_entry); // dato filtro avvio ricerca policy usando valore attributo filtro <tnetLrpFilterRule> const char* filtro = filter_entry.getCStr(FILTER_ATTR_CN_POS); const char* rule = filter_entry.getCStr(FILTER_ATTR_RULE_POS); U_INTERNAL_DUMP("RULE = %S FILTRO = %S", rule, filtro) // get policy attribute from LDAP ULDAP ldap_url; char first_char = rule[0]; bool policy_by_url = (first_char != '('); if (policy_by_url) { if (first_char == '/') { static char url[1024]; (void) snprintf(url, sizeof(url), "ldap://%s%s", LDAP_host.c_str(), rule); rule = url; } if (ldap_url.init(rule) == false || ldap_url.set_protocol() == false || ldap_url.simple_bind() == false) { U_ERROR("login to LDAP with URL failed..."); } n = ldap_url.search(); } else { n = ldap.search("o=Policies,o=tnet", LDAP_SCOPE_SUBTREE, (char**)policy_attr_name, rule); } if (n <= 0) U_ERROR("cannot find policy from LDAP..."); ULDAPEntry policy_entry(POLICY_NUM_ATTR, policy_attr_name, n); if (policy_by_url) ldap_url.get(policy_entry); else ldap.get(policy_entry); // init log const char* log_name = filtro; # include "common2.cpp" // loop for every policy int j, k; char ipmask[64]; const char* ptr; const char* policy; const char* ip_mask; const char* ip_device; char request_buffer[4096 * 4]; const char* binddn_device = LDAP_binddn_device.c_str(); for (i = 0; i < n; ++i) { policy = policy_entry.getCStr(POLICY_ATTR_CN_POS, i); U_INTERNAL_DUMP("POLICY = %S %S", policy_entry[i], policy) // data policy avvio ricerca lista device usando valore attributo policy <tnetLrpIpNetworkNumber> ip_mask = policy_entry.getCStr(POLICY_ATTR_IPMASK_POS, i); (void) snprintf(ipmask, sizeof(ipmask), "(tnetLrpIpHostNumber=%s*)", ip_mask); j = ldap.search(binddn_device, LDAP_SCOPE_SUBTREE, (char**)device_attr_name, ipmask); if (j <= 0) continue; ptr = policy_entry.getCStr(POLICY_ATTR_POLICY_POS, i); // check if to skip xml header... if (U_STRNEQ(ptr, "<?xml")) { ptr += 5; while (*ptr++ != '\n'); } // build request static const char* request_tmpl = \ // "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE REQUEST SYSTEM \"lrp_request.dtd\">" "<REQUEST sid=\"sid1\" version=\"1\"><IMPORT-POLICYLABEL name=\"%s\">%s</IMPORT-POLICYLABEL></REQUEST>" "<REQUEST sid=\"sid2\" version=\"1\"><EXECUTE-POLICYLABEL name=\"%s\" command=\"%s\"/></REQUEST>"; request_size = u__snprintf(request_buffer, sizeof(request_buffer), request_tmpl, policy, ptr, policy, operation); request = UString(request_buffer, request_size); // write request to file # include "common3.cpp" // set devices attribute for LDAP ULDAPEntry device_entry(DEVICE_NUM_ATTR, device_attr_name, j); // get devices attribute from LDAP // loop for every device // fork: child // send request to device // write response to file # include "common4.cpp" // parent } exit_value = proc.waitAll(); log.close(); }