diff --git a/controllers/game/modes/8_ball.gd b/controllers/game/modes/8_ball.gd index 498b98b..84c0c3c 100644 --- a/controllers/game/modes/8_ball.gd +++ b/controllers/game/modes/8_ball.gd @@ -1,54 +1,5 @@ -class_name Ball8 extends BaseMode +class_name Ball8 extends LastBallMode -func _init() -> void: - pass - -func reap_ball(ball: Node3D) -> void: - super(ball) - - if ball.name == "08_ball": - # TODO: 8 ball must not be be destroyed. Handle rule... - ball_states[ball.name]["is_foul"] = true - -func process_ball(ball: Node3D) -> void: - super(ball) - - if ball.name == "08_ball": - # TODO: 8 ball must be sunk last. Handle rule... - ball_states[ball.name]["is_foul"] = true - -func handle_switch_user() -> void: - push_warning("Balls reaped: ", balls_reaped.size() ) - push_warning("Balls sunk: ", balls_sunk.size() ) - var is_player_1: bool = "Player 1" == get_player() - var switch_player: bool = true - - if not balls_reaped.is_empty(): - for ball_name in balls_reaped: - if is_player_1: - player_1_points -= ball_states[ball_name]["points"] - else: - player_2_points -= ball_states[ball_name]["points"] - - balls_reaped.clear() - - if not balls_sunk.is_empty(): - # TODO: Handle points, etc, insure 8-ball not sunk unless last - # Insure we process stripped vs solid ball and apropriate points accordingly - # ball_states[ball.name]["is_foul"] = true - - switch_player = false - balls_sunk.clear() - - if ball_states_finalized(): - process_win_condition() - return - - if switch_player: - Globals.game_state.switch_active_player() - - turn_count += 1 - -func process_win_condition() -> void: - pass +func _init() -> void: + final_ball_name = "08_ball" diff --git a/controllers/game/modes/9_ball.gd b/controllers/game/modes/9_ball.gd index 5a176b6..23c1651 100644 --- a/controllers/game/modes/9_ball.gd +++ b/controllers/game/modes/9_ball.gd @@ -1,54 +1,5 @@ -class_name Ball9 extends BaseMode +class_name Ball9 extends LastBallMode -func _init() -> void: - pass - -func reap_ball(ball: Node3D) -> void: - super(ball) - - if ball.name == "09_ball": - # TODO: 9 ball must not be be destroyed. Handle rule... - ball_states[ball.name]["is_foul"] = true - -func process_ball(ball: Node3D) -> void: - super(ball) - - if ball.name == "09_ball": - # TODO: 9 ball must be sunk last. Handle rule... - ball_states[ball.name]["is_foul"] = true - -func handle_switch_user() -> void: - push_warning("Balls reaped: ", balls_reaped.size() ) - push_warning("Balls sunk: ", balls_sunk.size() ) - var is_player_1: bool = "Player 1" == get_player() - var switch_player: bool = true - - if not balls_reaped.is_empty(): - for ball_name in balls_reaped: - if is_player_1: - player_1_points -= ball_states[ball_name]["points"] - else: - player_2_points -= ball_states[ball_name]["points"] - - balls_reaped.clear() - - if not balls_sunk.is_empty(): - # TODO: Handle points, etc, insure 9-ball not sunk unless last. - # Insure we process stripped vs solid ball and apropriate points accordingly. - # ball_states[ball.name]["is_foul"] = true - - switch_player = false - balls_sunk.clear() - - if ball_states_finalized(): - process_win_condition() - return - - if switch_player: - Globals.game_state.switch_active_player() - - turn_count += 1 - -func process_win_condition() -> void: - pass +func _init() -> void: + final_ball_name = "09_ball" diff --git a/controllers/game/modes/base_mode.gd b/controllers/game/modes/base_mode.gd index b22457d..8222a61 100644 --- a/controllers/game/modes/base_mode.gd +++ b/controllers/game/modes/base_mode.gd @@ -28,11 +28,74 @@ var BALL_STATE: Dictionary = { } -func _init() -> void: +func _process_reaped_balls(player: String) -> void: + for ball_name in balls_reaped: + _add_points(player, -ball_states[ball_name]["points"]) + + balls_reaped.clear() + +func _process_sunk_balls(player: String) -> void: + for ball_name in balls_sunk: + _add_points(player, ball_states[ball_name]["points"]) + + balls_sunk.clear() + +func _record_ball( + ball: Node3D, + state_key: String, + tracker: Array +) -> void: + tracker.append(ball.name) + + ball_states[ball.name]["turn"] = turn_count + ball_states[ball.name]["player"] = get_player() + ball_states[ball.name][state_key] = true + +func _add_points(player: String, amount: int) -> void: + if player == "Player 1": + player_1_points += amount + else: + player_2_points += amount + + +func get_player() -> String: + return ( + "Player 1" + if Globals.game_data.client1 == + Globals.multiplayer_data.current_turn_player_id + else + "Player 2" + ) + +# NOTE: Override when a mode doesn't awards points for sunk balls. +func score_sunk_balls() -> bool: + return true + +# NOTE: Override when a mode shouldn't keep the player's turn after a legal sink. +func keep_turn_after_sink() -> bool: + return true + +# NOTE: Override when a modehas extra processing such as +# checking if solid or striped ball. +func on_ball_reaped(_ball: Node3D) -> void: pass +# NOTE: Override when a modehas extra processing such as +# checking if solid or striped ball. +func on_ball_sunk(_ball: Node3D) -> void: + pass + + +func reap_ball(ball: Node3D) -> void: + _record_ball(ball, "reaped", balls_reaped) + on_ball_reaped(ball) + +func process_ball(ball: Node3D) -> void: + _record_ball(ball, "sunk", balls_sunk) + on_ball_sunk(ball) + func generate_ball_states() -> Dictionary: - var state := {} + var state: Dictionary = {} for i: int in range(1, 16): var ball_name = "%02d_ball" % i @@ -41,6 +104,26 @@ func generate_ball_states() -> Dictionary: return state +func handle_switch_user() -> void: + push_warning("Balls reaped: ", balls_reaped.size() ) + push_warning("Balls sunk: ", balls_sunk.size() ) + + var player: String = get_player() + var no_sunk_balls: bool = balls_sunk.is_empty() + + _process_reaped_balls(player) + if score_sunk_balls(): + _process_sunk_balls(player) + + if ball_states_finalized(): + process_win_condition() + return + + if no_sunk_balls or not keep_turn_after_sink(): + Globals.game_state.switch_active_player() + + turn_count += 1 + func ball_states_finalized() -> bool: for ball_state in ball_states.values(): if ball_state["reaped"] or ball_state["sunk"]: @@ -52,28 +135,3 @@ func ball_states_finalized() -> bool: func process_win_condition() -> void: assert(false, "This method needs to be overridden...") - -func get_player() -> String: - return \ - "Player 1" \ - if \ - Globals.game_data.client1 == Globals.multiplayer_data.current_turn_player_id \ - else \ - "Player 2" - -func reap_ball(ball: Node3D) -> void: - balls_reaped.append(ball.name) - - ball_states[ball.name]["turn"] = turn_count - ball_states[ball.name]["player"] = get_player() - ball_states[ball.name]["reaped"] = true - -func process_ball(ball: Node3D) -> void: - balls_sunk.append(ball.name) - - ball_states[ball.name]["turn"] = turn_count - ball_states[ball.name]["player"] = get_player() - ball_states[ball.name]["sunk"] = true - -func handle_switch_user() -> void: - assert(false, "This method needs to be overridden...") diff --git a/controllers/game/modes/free_ball.gd b/controllers/game/modes/free_ball.gd index 540b613..550a460 100644 --- a/controllers/game/modes/free_ball.gd +++ b/controllers/game/modes/free_ball.gd @@ -4,45 +4,6 @@ class_name FreeBall extends BaseMode func _init() -> void: pass -func reap_ball(ball: Node3D) -> void: - super(ball) - -func process_ball(ball: Node3D) -> void: - super(ball) - -func handle_switch_user() -> void: - push_warning("Balls reaped: ", balls_reaped.size() ) - push_warning("Balls sunk: ", balls_sunk.size() ) - var is_player_1: bool = "Player 1" == get_player() - var switch_player: bool = true - - if not balls_reaped.is_empty(): - for ball_name in balls_reaped: - if is_player_1: - player_1_points -= ball_states[ball_name]["points"] - else: - player_2_points -= ball_states[ball_name]["points"] - - balls_reaped.clear() - - if not balls_sunk.is_empty(): - for ball_name in balls_sunk: - if is_player_1: - player_1_points += ball_states[ball_name]["points"] - else: - player_2_points += ball_states[ball_name]["points"] - - switch_player = false - balls_sunk.clear() - - if ball_states_finalized(): - process_win_condition() - return - - if switch_player: - Globals.game_state.switch_active_player() - - turn_count += 1 func process_win_condition() -> void: pass diff --git a/controllers/game/modes/game_manager_base.gd b/controllers/game/modes/game_manager_base.gd index d139bd3..c1108ef 100644 --- a/controllers/game/modes/game_manager_base.gd +++ b/controllers/game/modes/game_manager_base.gd @@ -21,5 +21,5 @@ func _balls_stopped_moving() -> void: mode.handle_switch_user() func set_mode(game_mode) -> void: - mode = Globals.game_data.GameMode.get(game_mode).new() + mode = Globals.game_data.GameMode.get(game_mode).new() mode.ball_states = mode.generate_ball_states() diff --git a/controllers/game/modes/last_ball_mode.gd b/controllers/game/modes/last_ball_mode.gd new file mode 100644 index 0000000..3022368 --- /dev/null +++ b/controllers/game/modes/last_ball_mode.gd @@ -0,0 +1,16 @@ +class_name LastBallMode extends BaseMode + + +@export var final_ball_name: String = "" + + +func on_ball_reaped(ball: Node3D) -> void: + if ball.name == final_ball_name: + ball_states[ball.name]["is_foul"] = true + +func on_ball_sunk(ball: Node3D) -> void: + if ball.name == final_ball_name: + ball_states[ball.name]["is_foul"] = true + +func process_win_condition() -> void: + pass diff --git a/controllers/game/modes/last_ball_mode.gd.uid b/controllers/game/modes/last_ball_mode.gd.uid new file mode 100644 index 0000000..35408cc --- /dev/null +++ b/controllers/game/modes/last_ball_mode.gd.uid @@ -0,0 +1 @@ +uid://rkwfbkek6ypo diff --git a/controllers/game/modes/snooker.gd b/controllers/game/modes/snooker.gd index c90dad4..b2712b9 100644 --- a/controllers/game/modes/snooker.gd +++ b/controllers/game/modes/snooker.gd @@ -4,93 +4,41 @@ class_name Snooker extends BaseMode # yellow (2 points), green (3), brown (4), blue (5), pink (6), black (7). -var COLORED_BALLS: Array[String] = [ - "yellow_ball", "green_ball", "brown_ball", - "blue_ball", "pink_ball", "black_ball" -] - -var COLORED_BALLS_POINTS: Array[int] = [ - 2, 3, 4, - 5, 6, 7 -] +const COLORED_BALLS := { + "yellow_ball": 2, "green_ball": 3, "brown_ball": 4, + "blue_ball": 5, "pink_ball": 6, "black_ball": 7, +} -func _init() -> void: +func _init() -> void: pass + func generate_ball_states() -> Dictionary: var state: Dictionary = {} - for i in COLORED_BALLS.size(): - var ball_name = COLORED_BALLS[i] - var points = COLORED_BALLS_POINTS[i] - + for ball_name in COLORED_BALLS: state[ball_name] = BALL_STATE.duplicate_deep() - state[ball_name]["points"] = points + state[ball_name]["points"] = COLORED_BALLS[ball_name] - # NOTE: Red balls for i: int in range(1, 16): - var ball_name = "%02d_ball" % i + var ball_name := "%02d_ball" % i state[ball_name] = BALL_STATE.duplicate_deep() state[ball_name]["points"] = 1 - ball_states = state return state -func is_colored_ball(ball_name: String) -> bool: - return ball_name in COLORED_BALLS - -func reap_ball(ball: Node3D) -> void: - super(ball) - - if is_colored_ball(ball.name): - # TODO: Colored ball must not be be destroyed. Handle rule... +func on_ball_reaped(ball: Node3D) -> void: + if ball.name in COLORED_BALLS: ball_states[ball.name]["is_foul"] = true -func process_ball(ball: Node3D) -> void: - super(ball) +func on_ball_sunk(ball: Node3D) -> void: + if ball.name in COLORED_BALLS: + # TODO: Check whether a red ball has been sunk first. + ball_states[ball.name]["is_foul"] = true - if is_colored_ball(ball.name): - # TODO: Colored ball must not be be processed before red ball sunk. - # Handle rule... - var red_ball_sunk = true - if not red_ball_sunk: - ball_states[ball.name]["is_foul"] = true - -func handle_switch_user() -> void: - push_warning("Balls reaped: ", balls_reaped.size() ) - push_warning("Balls sunk: ", balls_sunk.size() ) - var is_player_1: bool = "Player 1" == get_player() - var switch_player: bool = true - - if not balls_reaped.is_empty(): - for ball_name in balls_reaped: - if is_player_1: - player_1_points -= ball_states[ball_name]["points"] - else: - player_2_points -= ball_states[ball_name]["points"] - - balls_reaped.clear() - - if not balls_sunk.is_empty(): - # NOTE: Handle points, etc, insure color ball not sunk before red ball - for ball_name in balls_sunk: - if is_player_1: - player_1_points += ball_states[ball_name]["points"] - else: - player_2_points += ball_states[ball_name]["points"] - - switch_player = false - balls_sunk.clear() - - if ball_states_finalized(): - process_win_condition() - return - - if switch_player: - Globals.game_state.switch_active_player() - - turn_count += 1 +func score_sunk_balls() -> bool: + return true func process_win_condition() -> void: pass