Skip to content

Commit

Permalink
Merge branch 'dev' into 0.80.x
Browse files Browse the repository at this point in the history
# Conflicts:
#	mpf/modes/bonus/code/bonus.py
#	mpf/tests/test_Bonus.py
  • Loading branch information
avanwinkle committed Sep 15, 2024
2 parents ba32634 + 7a96651 commit 2884054
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 44 deletions.
1 change: 1 addition & 0 deletions mpf/config_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ fast_exp_board:
led_ports: list|subconfig(fast_led_port)|None
led_fade_time: single|ms|0
led_hz: single|float|30
ignore_led_errors: single|bool|false
fast_breakout:
port: single|enum(1,2,3)|
model: single|str|
Expand Down
19 changes: 9 additions & 10 deletions mpf/modes/bonus/code/bonus.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,28 @@ def _bonus_next_item(self):
# following names are reserved for end-of-bonus behavior.
assert entry not in ["subtotal", "multiplier", "total"], "Bonus entry cannot be reserved word '%s'" % entry

# Calling player.vars.get() instead of player.get() bypasses the
# auto-fill zero and will throw if there is no player variable.
# The fallback value of 1 is used for bonus entries that don't use
# a player score, which are multiplied by one to get the bonus.
hits = self.player.vars.get(entry['player_score_entry'], 1)
# If a player_score_entry is provided, use player getattr to get a
# fallback value of zero if the variable is not set. Otherwise
# use 1 as the multiplier for non-player-score bonuses.
hits = self.player[entry['player_score_entry']] if entry['player_score_entry'] else 1
score = entry['score'].evaluate([]) * hits

if (not score and entry['skip_if_zero']) or (score < 0 and entry['skip_if_negative']):
self.debug_log("Skipping bonus entry '%s' because its value is 0",
entry['entry'])
self.info_log("Skipping bonus entry '%s' because its value is 0",
entry['entry'])
self._bonus_next_item()
return

# pylint: disable=superfluous-parens
if self.settings["rounding_value"] and (r := (score % self.settings["rounding_value"])):
self.debug_log("rounding bonus score %s remainder of %s", score, r)
self.info_log("rounding bonus score %s remainder of %s", score, r)
if self.settings["rounding_direction"] == "down":
score -= r
else:
score += self.settings["rounding_value"] - r

self.debug_log("Bonus Entry '%s': score: %s player_score_entry: %s=%s",
entry['entry'], score, entry['player_score_entry'], hits)
self.info_log("Bonus Entry '%s': score: %s player_score_entry: %s=%s",
entry['entry'], score, entry['player_score_entry'], hits)

self.bonus_score += score
self.machine.events.post("bonus_entry", entry=entry['entry'],
Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/net_neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def update_switches_from_hw_data(self):
This will silently sync the switch.hw_state. If the logical state changes,
it will process it like any switch change.
"""
for switch in self.machine.switches:
for switch in self.machine.switches.values():
hw_state = self.platform.hw_switch_data[switch.hw_switch.number]

if hw_state != switch.hw_state:
Expand Down
8 changes: 5 additions & 3 deletions mpf/platforms/fast/fast_exp_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio
from base64 import b16decode
from binascii import Error as binasciiError
from importlib import import_module

from packaging import version
Expand Down Expand Up @@ -176,11 +177,12 @@ def update_leds(self):

try:
self.communicator.send_bytes(b16decode(f'{msg_header}{msg}'), log_msg)
except Exception as e:
except binasciiError as e:
self.log.error(
f"Error decoding the following message for board {breakout_address} : {msg_header}{msg}")
self.log.debug("Attempted update that caused this error: %s", dirty_leds)
raise e
self.log.info("Attempted update that caused this error: %s", dirty_leds)
if not self.config['ignore_led_errors']:
raise e

def set_led_fade(self, rate: int) -> None:
"""Set LED fade rate in ms."""
Expand Down
12 changes: 6 additions & 6 deletions mpf/platforms/virtual.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,16 @@ async def get_hw_switch_states(self):

if 'virtual_platform_start_active_switches' in self.machine.config:
initial_active_switches = []
for switch in Util.string_to_list(self.machine.config['virtual_platform_start_active_switches']):
if switch not in self.machine.switches:
if " " in switch:
for switch_name in Util.string_to_list(self.machine.config['virtual_platform_start_active_switches']):
if switch_name not in self.machine.switches.keys():
if " " in switch_name:
self.raise_config_error("MPF no longer supports lists separated by space in "
"virtual_platform_start_active_switches. Please separate "
"switches by comma: {}.".format(switch), 1)
"switches by comma: {}.".format(switch_name), 1)
else:
self.raise_config_error("Switch {} used in virtual_platform_start_active_switches was not "
"found in switches section.".format(switch), 1)
initial_active_switches.append(self.machine.switches[switch].hw_switch.number)
"found in switches section.".format(switch_name), 1)
initial_active_switches.append(self.machine.switches[switch_name].hw_switch.number)

for k in self.hw_switches:
if k in initial_active_switches:
Expand Down
6 changes: 6 additions & 0 deletions mpf/tests/machine_files/bonus/modes/bonus/config/bonus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ mode_settings:
score: 5000
player_score_entry: modes
reset_player_score_entry: False
- entry: bonus_undefined_var
score: 5000
skip_if_zero: false
player_score_entry: undefined_var
- entry: bonus_static
score: 2000
4 changes: 4 additions & 0 deletions mpf/tests/machine_files/shots/config/test_shot_groups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ switches:
number:
switch_4:
number:
switch_5:
number:
switch_6:
number:
s_rotate_l:
number:
s_rotate_r:
Expand Down
4 changes: 4 additions & 0 deletions mpf/tests/machine_files/shots/modes/base2/config/base2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ shots:
light: tag1
shot_4:
switch: switch_1
shot_5:
switch: switch_5
shot_6:
switch: switch_6
led_1:
switch: switch_1
show_tokens:
Expand Down
19 changes: 18 additions & 1 deletion mpf/tests/machine_files/shots/modes/mode1/config/mode1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ shots:
mode1_shot_3:
switch: switch_3
profile: mode1_shot_3
mode1_shot_5:
switch: switch_5
profile: mode1_shot_5
mode1_shot_6:
switch: switch_6
profile: mode1_shot_6

shot_profiles:
mode1_shot_2:
Expand All @@ -32,10 +38,21 @@ shot_profiles:
- name: mode1_one
- name: mode1_two
- name: mode1_three
mode1_shot_3:
mode1_shot_3: # Test block: True
show: rainbow2
block: True
states:
- name: mode1_one
- name: mode1_two
- name: mode1_three
mode1_shot_5: # Test block: False
show: rainbow2
block: False
states:
- name: mode1_one
- name: mode1_two
mode1_shot_6: # Test block default
show: rainbow2
states:
- name: mode1_one
- name: mode1_two
44 changes: 36 additions & 8 deletions mpf/tests/test_Bonus.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,23 @@ def testBonus(self):
self.assertEqual(10000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(2, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_undefined_var", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_static", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(2000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(1, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("subtotal", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(13000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(15000, self._last_event_kwargs["bonus_entry"]["score"])
self.advance_time_and_run(2)
self.assertEqual("multiplier", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(5, self._last_event_kwargs["bonus_entry"]["score"])
self.advance_time_and_run(2)
self.assertEqual("total", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(65000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(66337, self.machine.game.player.score)
self.assertEqual(75000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(76337, self.machine.game.player.score)

# check resets
self.assertEqual(0, self.machine.game.player.ramps)
Expand All @@ -110,15 +118,23 @@ def testBonus(self):
self.assertEqual(10000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(2, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_undefined_var", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_static", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(2000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(1, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("subtotal", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(10000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(12000, self._last_event_kwargs["bonus_entry"]["score"])
self.advance_time_and_run(2)
self.assertEqual("multiplier", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(5, self._last_event_kwargs["bonus_entry"]["score"])
self.advance_time_and_run(2)
self.assertEqual("total", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(50000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(116337, self.machine.game.player.score)
self.assertEqual(60000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(136337, self.machine.game.player.score)

# multiplier should stay the same
self.assertEqual(0, self.machine.game.player.ramps)
Expand Down Expand Up @@ -149,6 +165,10 @@ def testBonus(self):
self.post_event('flipper_cancel', .1)
self.assertEqual("bonus_modes", self._last_event_kwargs["bonus_entry"]["entry"])

self.advance_time_and_run(.5)
self.assertEqual("bonus_undefined_var", self._last_event_kwargs["bonus_entry"]["entry"])
self.advance_time_and_run(.5)
self.assertEqual("bonus_static", self._last_event_kwargs["bonus_entry"]["entry"])
self.advance_time_and_run(.5)
self.assertEqual("subtotal", self._last_event_kwargs["bonus_entry"]["entry"])

Expand Down Expand Up @@ -179,9 +199,17 @@ def testBonus(self):
self.assertEqual(10000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(2, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_undefined_var", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(0, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("bonus_static", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(2000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(1, self._last_event_kwargs["bonus_entry"]["hits"])
self.advance_time_and_run(2)
self.assertEqual("total", self._last_event_kwargs["bonus_entry"]["entry"])
self.assertEqual(11000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(149337, self.machine.game.player.score)
self.assertEqual(13000, self._last_event_kwargs["bonus_entry"]["score"])
self.assertEqual(175337, self.machine.game.player.score)


@test_config_directory("tests/machine_files/bonus_no_keep_multiplier/")
Expand Down
45 changes: 30 additions & 15 deletions mpf/tests/test_Shots.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,51 @@ def stop_game(self):
self.advance_time_and_run()
self.assertIsNone(self.machine.game)

def test_block(self):
def block_test(self, switch, shot, should_block):
high_priority_shot = "mode1_" + shot
self.mock_event("playfield_active")
self.hit_and_release_switch("switch_3")
self.hit_and_release_switch(switch)
self.advance_time_and_run(.1)
self.assertEventCalled("playfield_active")

self.start_game()
self.assertEqual("unlit", self.machine.shots["shot_3"].state_name)
self.assertEqual("unlit", self.machine.shots[shot].state_name)

self.hit_and_release_switch("switch_3")
self.hit_and_release_switch(switch)
self.advance_time_and_run(.1)
self.assertTrue(self.machine.shots["shot_3"].enabled)
self.assertEqual("lit", self.machine.shots["shot_3"].state_name)
self.assertFalse(self.machine.shots[high_priority_shot].enabled)
self.assertTrue(self.machine.shots[shot].enabled)
self.assertEqual("lit", self.machine.shots[shot].state_name)

self.machine.shots["shot_3"].reset()
self.assertEqual("unlit", self.machine.shots["shot_3"].state_name)
self.machine.shots[shot].reset()
self.assertEqual("unlit", self.machine.shots[shot].state_name)

# Start the mode and make sure those shots load
self.start_mode("mode1")

self.assertTrue(self.machine.shots["shot_3"].enabled)
self.assertTrue(self.machine.shots["mode1_shot_3"].enabled)
self.assertEqual("unlit", self.machine.shots["shot_3"].state_name)
self.assertEqual("mode1_one", self.machine.shots["mode1_shot_3"].state_name)
self.assertTrue(self.machine.shots[shot].enabled)
self.assertTrue(self.machine.shots[high_priority_shot].enabled)
self.assertEqual("unlit", self.machine.shots[shot].state_name)
self.assertEqual("mode1_one", self.machine.shots[high_priority_shot].state_name)

self.hit_and_release_switch("switch_3")
self.hit_and_release_switch(switch)
self.advance_time_and_run(.1)

self.assertEqual("unlit", self.machine.shots["shot_3"].state_name)
self.assertEqual("mode1_two", self.machine.shots["mode1_shot_3"].state_name)
if should_block:
self.assertEqual("unlit", self.machine.shots[shot].state_name)
else:
self.assertEqual("lit", self.machine.shots[shot].state_name)

self.assertEqual("mode1_two", self.machine.shots[high_priority_shot].state_name)

def test_block_true(self):
self.block_test("switch_3", "shot_3", True)

def test_block_false(self):
self.block_test("switch_5", "shot_5", False)

def test_block_default(self): #Default behaves as false
self.block_test("switch_6", "shot_6", False)

def test_loading_shots(self):
# Make sure machine-wide shots load & mode-specific shots do not
Expand Down

0 comments on commit 2884054

Please sign in to comment.