Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/missionpinball/mpf into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
qcapen committed Apr 10, 2017
2 parents bec4557 + d810346 commit ff1b852
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 88 deletions.
2 changes: 1 addition & 1 deletion mpf/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"""

__version__ = '0.33.0-dev.17'
__version__ = '0.33.0-dev.18'
__short_version__ = '0.33'
__bcp_version__ = '1.1'
__config_version__ = '4'
Expand Down
41 changes: 28 additions & 13 deletions mpf/config_players/score_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,51 @@ def __init__(self, machine):
def play(self, settings, context, calling_context, priority=0, **kwargs):
"""Score variable."""
for var, s in settings.items():
if self._is_blocked(var, context, calling_context):
if self._is_blocked(var, context, calling_context, priority):
continue
if s['block']:
if var not in self.blocks:
self.blocks[var] = []
if (priority, context, calling_context) not in self.blocks[var]:
self.blocks[var].append((priority, context, calling_context))

self._score(var, s)
self._score(var, s, kwargs)

def _is_blocked(self, var, context, calling_context):
def _is_blocked(self, var, context, calling_context, priority):
if var not in self.blocks or not self.blocks[var]:
return False
priority_sorted = sorted(self.blocks[var], reverse=True)
return priority_sorted[0][1] != context + "_" + calling_context
return priority_sorted[0][0] > priority and priority_sorted[0][1] != context + "_" + calling_context

def _score(self, var, entry):
def _score(self, var: str, entry: dict, placeholder_parameters: dict) -> None:
if entry['string']:
self.machine.game.player[var] = entry['string']
elif entry['action'] == "add":
self.machine.game.player[var] += entry['score'].evaluate([])
return

# evaluate placeholder
value = entry['score'].evaluate(placeholder_parameters)

if entry['action'] == "add":
if entry['player']:
# specific player
self.machine.game.player_list[entry['player'] - 1][var] += value
else:
# default to current player
self.machine.game.player[var] += value
elif entry['action'] == "set":
self.machine.game.player[var] = entry['score'].evaluate([])
if entry['player']:
# specific player
self.machine.game.player_list[entry['player'] - 1][var] = value
else:
# default to current player
self.machine.game.player[var] = value
elif entry['action'] == "add_machine":
value = self.machine.get_machine_var(var)
if value is None:
value = 0
self.machine.create_machine_var(var, value + entry['score'].evaluate([]))
old_value = self.machine.get_machine_var(var)
if old_value is None:
old_value = 0
self.machine.create_machine_var(var, old_value + value)
elif entry['action'] == "set_machine":
self.machine.create_machine_var(var, entry['score'].evaluate([]))
self.machine.create_machine_var(var, value)
else:
raise AssertionError("Invalid value: {}".format(entry))

Expand Down
3 changes: 2 additions & 1 deletion mpf/core/ball_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def _add_new_balls_to_playfield(self):
self.info_log("Found a new ball which was captured from %s. Known balls: %s", capture.name,
self.num_balls_known)

self._balance_playfields()
if len(self.machine.playfields) > 1:
self._balance_playfields()

def _balance_playfields(self):
# find negative counts
Expand Down
2 changes: 2 additions & 0 deletions mpf/core/config_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
balls_to_save: single|int|1
enable_events: dict|str:ms|None
early_ball_save_events: dict|str:ms|None
delayed_eject_events: dict|str:ms|None
disable_events: dict|str:ms|ball_will_end, service_mode_entered
timer_start_events: dict|str:ms|None
bcp:
Expand Down Expand Up @@ -841,6 +842,7 @@
score: single|template_int|0
block: single|bool|False
action: single|enum(add,set,add_machine,set_machine)|add
player: single|int|None
string: single|str|None
scriptlets:
__valid_in__: machine # todo add to validator
Expand Down
2 changes: 1 addition & 1 deletion mpf/core/file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def get_file_interface(filename):
return None

@staticmethod
def load(filename, verify_version=False, halt_on_error=False, round_trip=False):
def load(filename, verify_version=False, halt_on_error=True, round_trip=False):
"""Load a file by name."""
if not FileManager.initialized:
FileManager.init()
Expand Down
70 changes: 48 additions & 22 deletions mpf/devices/ball_save.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
"""Device that implements a ball save."""
from typing import TYPE_CHECKING, Optional

from mpf.core.delays import DelayManager
from mpf.core.device_monitor import DeviceMonitor
from mpf.core.events import event_handler
from mpf.core.mode import Mode
from mpf.core.mode_device import ModeDevice
from mpf.core.system_wide_device import SystemWideDevice

if TYPE_CHECKING:
from mpf.core.machine import MachineController
from mpf.devices.playfield import Playfield


@DeviceMonitor("saves_remaining", "enabled", "timer_started", "state")
class BallSave(SystemWideDevice, ModeDevice):
Expand All @@ -15,10 +22,10 @@ class BallSave(SystemWideDevice, ModeDevice):
collection = 'ball_saves'
class_label = 'ball_save'

def __init__(self, machine, name):
def __init__(self, machine: "MachineController", name: str) -> None:
"""Initialise ball save."""
self.unlimited_saves = None
self.source_playfield = None
self.unlimited_saves = None # type: bool
self.source_playfield = None # type: Playfield
super().__init__(machine, name)

self.delay = DelayManager(machine.delayRegistry)
Expand All @@ -27,20 +34,18 @@ def __init__(self, machine, name):
self.saves_remaining = 0
self.early_saved = 0
self.state = 'disabled'
self._scheduled_balls = 0

def _initialize(self):
def _initialize(self) -> None:
self.unlimited_saves = self.config['balls_to_save'] == -1
self.source_playfield = self.config['source_playfield']

# todo change the delays to timers so we can add pause and extension
# events, but that will require moving timers out of mode conde

@property
def can_exist_outside_of_game(self):
def can_exist_outside_of_game(self) -> bool:
"""Return true if this device can exist outside of a game."""
return True

def validate_and_parse_config(self, config: dict, is_mode_config: bool):
def validate_and_parse_config(self, config: dict, is_mode_config: bool) -> dict:
"""Make sure timer_start_events are not in enable_events."""
config = super().validate_and_parse_config(config, is_mode_config)

Expand All @@ -49,9 +54,13 @@ def validate_and_parse_config(self, config: dict, is_mode_config: bool):
raise AssertionError("{}: event {} in timer_start_events will not work because it is also in "
"enable_events. Omit it!".format(event, str(self)))

if config['delayed_eject_events'] and config['eject_delay']:
raise AssertionError("cannot use delayed_eject_events and eject_delay at the same time.")

return config

def enable(self, **kwargs):
@event_handler(10)
def enable(self, **kwargs) -> None:
"""Enable ball save."""
del kwargs
if self.enabled:
Expand Down Expand Up @@ -79,7 +88,8 @@ def enable(self, **kwargs):
desc: The ball save called (name) has just been enabled.
'''

def disable(self, **kwargs):
@event_handler(1)
def disable(self, **kwargs) -> None:
"""Disable ball save."""
del kwargs
if not self.enabled:
Expand All @@ -99,7 +109,8 @@ def disable(self, **kwargs):
desc: The ball save called (name) has just been disabled.
'''

def timer_start(self, **kwargs):
@event_handler(9)
def timer_start(self, **kwargs) -> None:
"""Start the timer.
This is usually called after the ball was ejected while the ball save may have been enabled earlier.
Expand Down Expand Up @@ -131,7 +142,7 @@ def timer_start(self, **kwargs):
self.config['hurry_up_time']),
callback=self._hurry_up)

def _hurry_up(self):
def _hurry_up(self) -> None:
self.debug_log("Starting Hurry Up")

self.state = 'hurry_up'
Expand All @@ -141,7 +152,7 @@ def _hurry_up(self):
desc: The ball save called (name) has just entered its hurry up mode.
'''

def _grace_period(self):
def _grace_period(self) -> None:
self.debug_log("Starting Grace Period")

self.state = 'grace_period'
Expand All @@ -152,7 +163,7 @@ def _grace_period(self):
time.
'''

def _get_number_of_balls_to_save(self, available_balls):
def _get_number_of_balls_to_save(self, available_balls: int) -> int:
no_balls_in_play = False

try:
Expand Down Expand Up @@ -183,7 +194,7 @@ def _get_number_of_balls_to_save(self, available_balls):

return balls_to_save

def _reduce_remaining_saves_and_disable_if_zero(self, balls_to_save):
def _reduce_remaining_saves_and_disable_if_zero(self, balls_to_save: int) -> None:
if not self.unlimited_saves:
self.saves_remaining -= balls_to_save
self.debug_log("Saves remaining: %s", self.saves_remaining)
Expand All @@ -194,10 +205,10 @@ def _reduce_remaining_saves_and_disable_if_zero(self, balls_to_save):
self.debug_log("Disabling since there are no saves remaining")
self.disable()

def _ball_drain_while_active(self, balls, **kwargs):
def _ball_drain_while_active(self, balls: int, **kwargs) -> Optional[dict]:
del kwargs
if balls <= 0:
return
return {}

balls_to_save = self._get_number_of_balls_to_save(balls)

Expand All @@ -220,7 +231,8 @@ def _ball_drain_while_active(self, balls, **kwargs):

return {'balls': balls - balls_to_save}

def early_ball_save(self, **kwargs):
@event_handler(8)
def early_ball_save(self, **kwargs) -> None:
"""Perform early ball save if enabled."""
del kwargs
if not self.enabled:
Expand All @@ -246,27 +258,41 @@ def early_ball_save(self, **kwargs):

self._reduce_remaining_saves_and_disable_if_zero(1)

def _early_ball_save_drain_handler(self, balls, **kwargs):
def _early_ball_save_drain_handler(self, balls: int, **kwargs) -> dict:
del kwargs
if self.early_saved and balls > 0:
balls -= 1
self.early_saved -= 1
self.debug_log("Early saved ball drained.")
self.machine.events.remove_handler(self._early_ball_save_drain_handler)
return {'balls': balls}
else:
return {}

def _schedule_balls(self, balls_to_save):
def _schedule_balls(self, balls_to_save: int) -> None:
if self.config['eject_delay']:
# schedule after delay. to add some drama
self.delay.add(self.config['eject_delay'], self._add_balls, balls_to_save=balls_to_save)
elif self.config['delayed_eject_events']:
# unlimited delay. wait for event
self._scheduled_balls += balls_to_save
else:
# default: no delay. just eject balls right now
self._add_balls(balls_to_save)

@event_handler(4)
def delayed_eject(self, **kwargs):
"""Trigger eject of all scheduled balls."""
del kwargs
self._add_balls(self._scheduled_balls)
self._scheduled_balls = 0

def _add_balls(self, balls_to_save, **kwargs):
del kwargs
self.source_playfield.add_ball(balls=balls_to_save,
player_controlled=self.config['auto_launch'] ^ 1)

def device_removed_from_mode(self, mode):
def device_removed_from_mode(self, mode: Mode) -> None:
"""Disable ball save when mode ends."""
del mode
self.debug_log("Removing...")
Expand Down
5 changes: 3 additions & 2 deletions mpf/tests/MpfGameTestCase.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def add_player(self):
self.assertEqual(prev_players + 1, self.machine.game.num_players)

def assertBallNumber(self, number):
self.assertGameIsRunning()
self.assertEqual(number, self.machine.game.player.ball)

def assertBallsInPlay(self, balls):
Expand All @@ -64,7 +65,7 @@ def stop_game(self):
self.assertGameIsNotRunning()

def assertGameIsRunning(self):
self.assertIsNotNone(self.machine.game)
self.assertIsNotNone(self.machine.game, "Expected a running game but no game is active.")

def assertGameIsNotRunning(self):
self.assertIsNone(self.machine.game)
self.assertIsNone(self.machine.game, "Expected game to have ended but game is active.")
5 changes: 4 additions & 1 deletion mpf/tests/machine_files/ball_save/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,7 @@ ball_saves:
eject_delay:
enable_events: enable4
eject_delay: 1s
debug: yes
debug: yes
unlimited_delay:
enable_events: enable5
delayed_eject_events: eject5
3 changes: 2 additions & 1 deletion mpf/tests/machine_files/scoring/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ ball_devices:

modes:
- mode1
- mode2
- mode2
- mode3
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ scoring:
test_add_machine_var:
my_var:
score: 23
action: add_machine
action: add_machine
player_score:
my_var2:
score: change
action: add_machine
20 changes: 20 additions & 0 deletions mpf/tests/machine_files/scoring/modes/mode3/config/mode3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#config_version=4
mode:
start_events: start_mode3
stop_events: stop_mode3
priority: 400

scoring:
score_player1:
score:
score: 42
player: 1
score_player2:
score:
score: 23
player: 2
reset_player2:
score:
score: 10
player: 2
action: set
Loading

0 comments on commit ff1b852

Please sign in to comment.