diff --git a/mpf/config_spec.yaml b/mpf/config_spec.yaml index 759100af8..48edb0cca 100644 --- a/mpf/config_spec.yaml +++ b/mpf/config_spec.yaml @@ -1585,6 +1585,7 @@ shots: switches: list|machine(switches)|None start_enabled: single|bool|None delay_switch: dict|machine(switches):ms|None + delay_event_list: dict|event_handler:ms|None persist_enable: single|bool|true playfield: single|machine(playfields)|playfield priority: single|int|0 @@ -1599,6 +1600,8 @@ shots: show_tokens: dict|str:template_str|None shot_control_events: events: list|event_handler| + action: single|enum(set_state,mute)|set_state + duration: single|template_ms|None state: single|int| force: single|bool|true force_show: single|bool|false diff --git a/mpf/devices/shot.py b/mpf/devices/shot.py index cd8466415..b1ad954be 100644 --- a/mpf/devices/shot.py +++ b/mpf/devices/shot.py @@ -55,8 +55,7 @@ async def _initialize(self) -> None: for switch in self.config['switches'] + list(self.config['delay_switch'].keys()): # mark the playfield active no matter what - switch.add_handler(self._mark_active, - callback_kwargs={'playfield': switch.config['playfield']}) + switch.add_handler(self._mark_active, callback_kwargs={'playfield': switch.config['playfield']}) def _mark_active(self, playfield, **kwargs): """Mark playfield active.""" @@ -85,17 +84,27 @@ def validate_and_parse_config(self, config: dict, is_mode_config: bool, debug_pr def _register_switch_handlers(self): self._handlers = [] + priority = self.mode.priority + self.config['priority'] for switch in self.config['switches']: self._handlers.append(self.machine.events.add_handler("{}_active".format(switch.name), self.event_hit, - priority=self.mode.priority + self.config['priority'], + priority=priority, blocking_facility="shot")) - for switch in list(self.config['delay_switch'].keys()): + for switch, ms in list(self.config['delay_switch'].items()): self._handlers.append(self.machine.events.add_handler("{}_active".format(switch.name), self._delay_switch_hit, - switch_name=switch.name, - priority=self.mode.priority + self.config['priority'], + name=switch.name, + ms=ms, + priority=priority, + blocking_facility="shot")) + + for event, ms in list(self.config['delay_event_list'].items()): + self._handlers.append(self.machine.events.add_handler(event, + self._delay_switch_hit, + name=event, + ms=ms, + priority=priority, blocking_facility="shot")) def _remove_switch_handlers(self): @@ -406,31 +415,40 @@ def _notify_monitors(self, profile, state): callback(name=self.name, profile=profile, state=state) @event_handler(4) - def _delay_switch_hit(self, switch_name, **kwargs): + def _delay_switch_hit(self, name, ms, **kwargs): del kwargs if not self.enabled: return - self.delay.reset(name=switch_name + '_delay_timer', - ms=self.config['delay_switch'] - [self.machine.switches[switch_name]], + self.delay.reset(name=name + '_delay_timer', + ms=ms, callback=self._release_delay, - switch=switch_name) + switch=name) - self.active_delays.add(switch_name) + self.active_delays.add(name) def _release_delay(self, switch): self.active_delays.remove(switch) def _register_control_event_handlers(self): + #TODO support priority for either control event type for control_event in self.config['control_events']: for event in control_event['events']: - self.machine.events.add_handler(event, self._control_events, - control_event_config=control_event) + match control_event['action']: + case 'mute': + self.machine.events.add_handler(event, self._control_event_mute, control_event_config=control_event) + case 'set_state' | _: + self.machine.events.add_handler(event, self._control_event_set_state, control_event_config=control_event) + + @event_handler(8) + def _control_event_mute(self, control_event_config, **kwargs): + """Control event binding for mute action, which temporarily blocks shot from registering hits""" + del kwargs + self._delay_switch_hit('action_mute', control_event_config['duration']) @event_handler(7) - def _control_events(self, control_event_config, **kwargs): - """Takes in a control_event to move the shot to a specific state.""" + def _control_event_set_state(self, control_event_config, **kwargs): + """Control event binding for set_state action, which moves the shot to a specific state.""" del kwargs self.jump(control_event_config['state'], control_event_config['force'], diff --git a/mpf/tests/machine_files/shots/config/test_shots.yaml b/mpf/tests/machine_files/shots/config/test_shots.yaml index 46677421f..a66c84f63 100644 --- a/mpf/tests/machine_files/shots/config/test_shots.yaml +++ b/mpf/tests/machine_files/shots/config/test_shots.yaml @@ -60,6 +60,8 @@ switches: number: switch_28: number: + switch_29: + number: lights: light_1: diff --git a/mpf/tests/machine_files/shots/modes/base2/config/base2.yaml b/mpf/tests/machine_files/shots/modes/base2/config/base2.yaml index cf82e013d..604ccce38 100644 --- a/mpf/tests/machine_files/shots/modes/base2/config/base2.yaml +++ b/mpf/tests/machine_files/shots/modes/base2/config/base2.yaml @@ -36,6 +36,10 @@ shots: switch: switch_15 delay_switch: switch_15: 2s + shot_delay_event: + switch: switch_29 + delay_event_list: + my_delay_event: 2s default_show_light: switch: switch_5 show_tokens: diff --git a/mpf/tests/test_Shots.py b/mpf/tests/test_Shots.py index c48360421..2e78be845 100644 --- a/mpf/tests/test_Shots.py +++ b/mpf/tests/test_Shots.py @@ -222,6 +222,7 @@ def test_shot_with_multiple_switches(self): def test_shot_with_delay(self): self.mock_event("shot_delay_hit") self.mock_event("shot_delay_same_switch_hit") + self.mock_event("shot_delay_event_hit") self.start_game() # test delay at the beginning. should not count @@ -247,6 +248,14 @@ def test_shot_with_delay(self): self.assertEventCalled("shot_delay_same_switch_hit") self.mock_event("shot_delay_same_switch_hit") + # test delay with event. should not count + self.post_event("my_delay_event") + self.advance_time_and_run(.5) + self.hit_and_release_switch("switch_29") + self.advance_time_and_run(1) + self.assertEventNotCalled("shot_delay_event_hit") + self.advance_time_and_run(3) + # test that shot works without delay self.hit_and_release_switch("switch_1") self.advance_time_and_run(.5) @@ -256,6 +265,11 @@ def test_shot_with_delay(self): self.hit_and_release_switch("switch_1") self.advance_time_and_run(.5) self.assertEventCalled("shot_delay_hit") + + self.hit_and_release_switch("switch_29") + self.advance_time_and_run(.5) + self.assertEventCalled("shot_delay_event_hit") + self.mock_event("shot_delay_hit") self.hit_and_release_switch("s_delay")