674 lines
22 KiB
GDScript3
674 lines
22 KiB
GDScript3
extends Container
|
|
class_name Game
|
|
|
|
# Declare member variables here. Examples:
|
|
# var a = 2
|
|
# var b = "text"
|
|
|
|
var pf_scale
|
|
var turn = 1
|
|
var is_player_turn = true
|
|
var selected_piece = null
|
|
# Holds Vector2(x, y) => { piece, }
|
|
var board_squares = {}
|
|
|
|
var height
|
|
var width
|
|
var landing_piece = null
|
|
var landing_pos = null
|
|
|
|
var current_state = 0
|
|
var states = {
|
|
0: {
|
|
"description": "Player turn",
|
|
"directive": "Make a move",
|
|
},
|
|
1: {
|
|
"description": "Opponent's turn",
|
|
"directive": "Wait for the opponent to make a move",
|
|
}
|
|
}
|
|
var flash_help = null
|
|
var rng = null
|
|
var ai_target = null
|
|
var ai_piece = null
|
|
var reinforcements = null
|
|
var reinforcements_size = 0
|
|
var reinforcements_coords = []
|
|
var reinforcement_buff = null
|
|
|
|
const on_ai_lose_piece = {
|
|
50: "/root/Game/Huh",
|
|
75: "/root/Game/StopThat",
|
|
90: "/root/Game/Yammering",
|
|
}
|
|
const on_player_lose_piece = {
|
|
75: "/root/Game/Hehe",
|
|
90: "/root/Game/Dust",
|
|
}
|
|
const piece_types = {
|
|
"pawn": "res://src/pieces/Pawn.tscn",
|
|
"rook": "res://src/pieces/Rook.tscn",
|
|
"bishop": "res://src/pieces/Bishop.tscn",
|
|
"knight": "res://src/pieces/Knight.tscn",
|
|
"king": "res://src/pieces/King.tscn",
|
|
"queen": "res://src/pieces/Queen.tscn",
|
|
}
|
|
|
|
func new_piece(piece_type, group, position = null):
|
|
if not piece_types.has(piece_type):
|
|
return null
|
|
var piece = ResourceLoader.load(piece_types[piece_type]).instance()
|
|
if position != null:
|
|
if !set_piece_position(piece, position, true):
|
|
print("Failed to place piece ", piece, " at positiion ", position)
|
|
piece.queue_free()
|
|
return null
|
|
piece.set_position(Vector2(
|
|
position.x * 128,
|
|
position.y * 128
|
|
))
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
piece.add_to_group("pieces")
|
|
piece.add_to_group(group)
|
|
if group == "opponent":
|
|
piece.get_node("Body").set_modulate(
|
|
Color(0.8, 0.01, 0.01, 1.0)
|
|
)
|
|
pf.add_child(piece)
|
|
piece.connect("click", self, "_on_piece_click")
|
|
piece.connect("hold_start", self, "_on_hold_start")
|
|
piece.connect("hold_stop", self, "_on_hold_stop")
|
|
#print("Created piece ", piece, " of type ", piece_type, " at ", position, " for group ", group)
|
|
return piece
|
|
|
|
func _on_hold_start(piece, event):
|
|
if piece.is_in_group("player"):
|
|
if self.current_state != 0:
|
|
print("Can't move a piece, it's not your turn")
|
|
get_node("BottomBar/Help").set_text("Cannot move a piece, it's not your turn")
|
|
self.flash_help = 2
|
|
piece.cancel_hold()
|
|
return
|
|
self._on_piece_click(piece, event)
|
|
else:
|
|
print("Can't move opponent's piece: ", piece)
|
|
get_node("BottomBar/Help").set_text("Cannot move an opponent's piece")
|
|
self.flash_help = 3
|
|
piece.cancel_hold()
|
|
|
|
func _on_hold_stop(piece, event):
|
|
if self.selected_piece != null:
|
|
# deselect
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").set_visible(false)
|
|
var p = self.selected_piece
|
|
if p.is_in_group("player"):
|
|
p.get_node("Body").set_modulate(Color(1, 1, 1, 1))
|
|
else:
|
|
p.get_node("Body").set_modulate(Color(0.8, .01, .01, 1))
|
|
var moves = get_valid_piece_moves(p)
|
|
clear_square_hilights_for_moves(moves)
|
|
self.selected_piece = null
|
|
# Try to land the piece on the next physics frame so we
|
|
# can use raycasts
|
|
self.landing_piece = piece
|
|
self.landing_pos = event.position
|
|
|
|
func get_valid_piece_moves(piece, verbose = false):
|
|
var square = square_of_piece(piece)
|
|
var possible_moves = piece.get_possible_moves(Vector2(square.x, square.y))
|
|
# @TODO Filter based on game state
|
|
var moves = []
|
|
for d in possible_moves:
|
|
for m in d:
|
|
var target_square = null
|
|
if self.board_squares.has(m['pos']):
|
|
target_square = self.board_squares[m['pos']]
|
|
if target_square == null:
|
|
# Move it off the board
|
|
if verbose:
|
|
print("Move to ", m['pos'], " is not valid due to not being on the board")
|
|
break
|
|
if target_square['piece']:
|
|
# something here
|
|
if pieces_hostile(piece, target_square['piece']):
|
|
if not m['attack']:
|
|
if verbose:
|
|
print("Move to ", m['pos'], " is not valid due to not being an attack move")
|
|
break
|
|
else:
|
|
m["target"] = target_square['piece']
|
|
moves.append(m)
|
|
if not m['jump']:
|
|
break
|
|
else:
|
|
if verbose:
|
|
print("Move to ", m['pos'], " is not valid due to same-team piece is spot")
|
|
if not m['jump']:
|
|
break
|
|
else:
|
|
# empty
|
|
if not m['attack_only']:
|
|
moves.append(m)
|
|
else:
|
|
if verbose:
|
|
print("Move to ", m['pos'], " is not valid due to missing target")
|
|
break
|
|
return moves
|
|
|
|
func pieces_hostile(p1, p2):
|
|
var p1_player = p1.is_in_group("player")
|
|
var p2_player = p2.is_in_group("player")
|
|
if p1_player == p2_player:
|
|
return false
|
|
return true
|
|
|
|
func _on_piece_click(piece, event):
|
|
if self.selected_piece != null:
|
|
# deselect
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").set_visible(false)
|
|
var p = self.selected_piece
|
|
if p.is_in_group("player"):
|
|
p.get_node("Body").set_modulate(Color(1, 1, 1, 1))
|
|
else:
|
|
p.get_node("Body").set_modulate(Color(0.8, .01, .01, 1))
|
|
var moves = get_valid_piece_moves(p)
|
|
clear_square_hilights_for_moves(moves)
|
|
if self.selected_piece == piece:
|
|
self.selected_piece = null
|
|
return
|
|
if piece == null:
|
|
self.selected_piece = null
|
|
return
|
|
#print("Selected piece: ", piece)
|
|
if piece.is_in_group("player"):
|
|
piece.get_node("Body").set_modulate(Color(0, 1, 0, 1))
|
|
else:
|
|
piece.get_node("Body").set_modulate(Color(1.0, 0, 0, 1))
|
|
self.selected_piece = piece
|
|
var square = square_of_piece(piece)
|
|
var moves = get_valid_piece_moves(piece)
|
|
set_square_hilights_for_moves(moves)
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").set_piece_info(piece)
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").set_visible(true)
|
|
|
|
func clear_square_hilights_for_moves(moves):
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
for m in moves:
|
|
if pf.squares.has(m["pos"]):
|
|
var edge = pf.squares[m["pos"]].get_ref().get_node("Body")
|
|
edge.set_modulate(Color(1, 1, 1, 1.0))
|
|
|
|
func set_square_hilights_for_moves(moves):
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
for m in moves:
|
|
if pf.squares.has(m["pos"]):
|
|
var edge = pf.squares[m["pos"]].get_ref().get_node("Body")
|
|
if not m["attack"]:
|
|
edge.set_modulate(Color(0, 1, 0, 1))
|
|
else:
|
|
edge.set_modulate(Color(1, 0, 0, 1))
|
|
|
|
static func position_in_playfield(x, y, width, height):
|
|
return x >= 0 and x < width and y >= 0 and y < height
|
|
|
|
func square_of_piece(piece):
|
|
for k in self.board_squares.keys():
|
|
if self.board_squares[k]["piece"] == piece:
|
|
return self.board_squares[k]
|
|
return null
|
|
|
|
func set_piece_position(piece, position, destroy = false):
|
|
var square = self.board_squares[position]
|
|
if square["piece"] != null:
|
|
if destroy:
|
|
square["piece"].queue_free()
|
|
square["piece"] = null
|
|
else:
|
|
print("Warning, piece collision during set_piece_position at ", position)
|
|
return false
|
|
square["piece"] = piece
|
|
return true
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready():
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
var start_height = 8
|
|
var start_width = 8
|
|
pf.initialize(start_width, start_height)
|
|
self.height = start_height
|
|
self.width = start_width
|
|
# Translate the pf to it sits on top left
|
|
# We want to scale it to fit within the margin container
|
|
var pf_size = Vector2(width * 128.0, height * 128.0)
|
|
var larger = pf_size.x
|
|
if pf_size.y > larger:
|
|
larger = pf_size.y
|
|
# Container is 768x768
|
|
var scale = 768 / larger
|
|
print("Scaling playfield by ", scale)
|
|
pf.apply_scale(Vector2(scale, scale))
|
|
pf.translate(Vector2(
|
|
64 * scale,
|
|
64 * scale
|
|
))
|
|
self.pf_scale = scale
|
|
self.rng = RandomNumberGenerator.new()
|
|
self.rng.randomize()
|
|
|
|
get_node("/root/Game/EndMenu/VBoxContainer/New").connect("pressed", self, "_on_new_game_pressed")
|
|
get_node("/root/Game/EndMenu/VBoxContainer/Quit").connect("pressed", self, "_on_quit_game_pressed")
|
|
get_node("/root/Game/EndMenu/VBoxContainer/Fail Game").connect("pressed", self, "_on_fail_game")
|
|
get_node("/root/Game/EndMenu/VBoxContainer/Win Game").connect("pressed", self, "_on_win_game")
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").connect("stat_change_requested", self, "_on_piece_stat_change_requested")
|
|
reset_game_state()
|
|
|
|
func _on_piece_stat_change_requested(piece, attribute, value):
|
|
if piece.get(attribute) != null:
|
|
piece.set(attribute, value)
|
|
get_node("/root/Game/PanelRight/VBox/PieceInfo").set_piece_info(piece)
|
|
|
|
func _input(ev):
|
|
if Input.is_action_just_pressed("ui_cancel"):
|
|
print(ev)
|
|
self._on_escape(false, "Paused")
|
|
|
|
func _on_escape(force_visible = false, message = null):
|
|
var n = get_node("/root/Game/EndMenu")
|
|
if message != null:
|
|
n.get_node("VBoxContainer/Label").set_text(message)
|
|
if n.is_visible() and not force_visible:
|
|
n.set_visible(false)
|
|
else:
|
|
n.popup_centered()
|
|
|
|
func _on_new_game_pressed():
|
|
get_node("/root/Game/EndMenu").set_visible(false)
|
|
self.reset_game_state()
|
|
|
|
func _on_quit_game_pressed():
|
|
get_node("/root/Game/EndMenu").set_visible(false)
|
|
get_tree().quit()
|
|
|
|
func reset_game_state():
|
|
get_node("/root/Game/EndSong").stop()
|
|
self.is_player_turn = true
|
|
self.turn = 0
|
|
for p in get_tree().get_nodes_in_group("pieces"):
|
|
p.queue_free()
|
|
|
|
self.board_squares = {}
|
|
var x = 0;
|
|
var y = 0;
|
|
while x < self.width:
|
|
y = 0
|
|
while y < self.height:
|
|
self.board_squares[Vector2(x, y)] = {
|
|
"x": x,
|
|
"y": y,
|
|
"piece": null,
|
|
"reinforcement": null,
|
|
}
|
|
y += 1
|
|
x += 1
|
|
# Create starting pieces
|
|
# 8 pawns per side, starting at col 0 + (width-8)/2
|
|
# on rows 2, height - 2
|
|
var start_x = (self.width - 8) / 2
|
|
var y_opponent = 1
|
|
var y_player = self.height - 2
|
|
var i = 0
|
|
while i < 8:
|
|
x = start_x + i
|
|
new_piece("pawn", "player", Vector2(x, y_player))
|
|
new_piece("pawn", "opponent", Vector2(x, y_opponent))
|
|
i += 1
|
|
new_piece("rook", "player", Vector2(start_x, self.height - 1))
|
|
new_piece("rook", "player", Vector2(start_x + 7, self.height - 1))
|
|
new_piece("rook", "opponent", Vector2(start_x, 0))
|
|
new_piece("rook", "opponent", Vector2(start_x + 7, 0))
|
|
new_piece("bishop", "player", Vector2(start_x + 2, self.height -1))
|
|
new_piece("bishop", "player", Vector2(start_x + 5, self.height -1))
|
|
new_piece("bishop", "opponent", Vector2(start_x + 2, 0))
|
|
new_piece("bishop", "opponent", Vector2(start_x + 5, 0))
|
|
new_piece("knight", "player", Vector2(start_x + 1, self.height -1))
|
|
new_piece("knight", "player", Vector2(start_x + 6, self.height -1))
|
|
new_piece("knight", "opponent", Vector2(start_x + 1, 0))
|
|
new_piece("knight", "opponent", Vector2(start_x + 6, 0))
|
|
new_piece("king", "player", Vector2(start_x + 4, self.height - 1))
|
|
new_piece("king", "opponent", Vector2(start_x + 4, 0))
|
|
new_piece("queen", "player", Vector2(start_x + 3, self.height - 1))
|
|
new_piece("queen", "opponent", Vector2(start_x + 3, 0))
|
|
|
|
# Visual updates
|
|
self.current_state = 3
|
|
self.turn = 0
|
|
self._on_phase_end()
|
|
self._reset_help()
|
|
|
|
func _on_phase_end():
|
|
self.current_state += 1;
|
|
if not self.current_state in self.states.keys():
|
|
self.current_state = 0
|
|
self._on_new_turn()
|
|
if self.current_state == 0:
|
|
get_node("PanelRight/VBox/HBox/VBox/HBox/SkipTurnButton").set_disabled(false)
|
|
else:
|
|
get_node("PanelRight/VBox/HBox/VBox/HBox/SkipTurnButton").set_disabled(true)
|
|
get_node("TopBar/Bottom/Instruction").set_text(
|
|
self.states[self.current_state]["description"] + " - " + self.states[self.current_state]["directive"]
|
|
)
|
|
|
|
func choose_reinforcements(size, opponent: bool = true, at_spawn = true):
|
|
var min_square = 0
|
|
var max_square = (self.height * self.width) - 1
|
|
if at_spawn:
|
|
max_square = (self.width)*2 - 1
|
|
var range_size = max_square - min_square
|
|
var arrival_coords = []
|
|
var n = 0
|
|
while n < size:
|
|
var try = 0
|
|
var square_index = 0
|
|
var square = null
|
|
while try < 10:
|
|
# Try to get an unoccupied square
|
|
square_index = self.rng.randi() % range_size
|
|
var square_coord = Vector2(
|
|
floor(square_index % self.width), floor(square_index / self.width)
|
|
)
|
|
if at_spawn and not opponent:
|
|
square_coord.y += self.height - 2
|
|
square = self.board_squares[square_coord]
|
|
if square['piece'] == null and square['reinforcement'] == null:
|
|
# unoccupied, we'll take it
|
|
arrival_coords.append(square_coord)
|
|
var types = self.piece_types.keys()
|
|
square['reinforcement'] = types[self.rng.randi()%types.size()]
|
|
break
|
|
# occupied, try again
|
|
square = null
|
|
try += 1
|
|
if square == null:
|
|
print("Failed to find a spot to spawn reinforcement")
|
|
n += 1
|
|
return arrival_coords
|
|
|
|
func _on_new_turn():
|
|
self.turn += 1
|
|
var just_spawned = false
|
|
if self.reinforcements != null:
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
self.reinforcements -= 1
|
|
if self.reinforcements == 1:
|
|
# decide on what will arrive
|
|
self.reinforcements_coords = choose_reinforcements(self.reinforcements_size)
|
|
# show the player where they will arrive
|
|
for coord in self.reinforcements_coords:
|
|
pf.squares[coord].get_ref().set_reinforcement(self.board_squares[coord]['reinforcement'])
|
|
print("Reinforcement at coord ", coord, ": ", self.board_squares[coord]['reinforcement'])
|
|
get_node("BottomBar/Help").set_text("Reinforcement arrival locations analyzed")
|
|
self.flash_help = 3
|
|
elif self.reinforcements == 0:
|
|
# spawn them
|
|
for coord in self.reinforcements_coords:
|
|
pf.squares[coord].get_ref().set_reinforcement(null)
|
|
var square = self.board_squares[coord]
|
|
if square['piece'] == null:
|
|
new_piece(square['reinforcement'], "opponent", coord)
|
|
square = self.board_squares[coord]
|
|
if self.reinforcement_buff != null:
|
|
square['piece'].speed += self.reinforcement_buff
|
|
square['piece'].damage += self.reinforcement_buff
|
|
square['piece'].health += self.reinforcement_buff
|
|
if self.reinforcement_buff >= 5:
|
|
square['piece'].health = true
|
|
else:
|
|
print("Reinforcement arrival at ", coord, " blocked by piece!")
|
|
get_node("BottomBar/Help").set_text("Reinforcement at " + str(coord) + " telefragged.")
|
|
self.flash_help = 2
|
|
square['reinforcement'] = null
|
|
just_spawned = true
|
|
self.reinforcements = null
|
|
self.reinforcements_size = 0
|
|
self.reinforcements_coords = []
|
|
|
|
# Check for reinforcements
|
|
var opponent_pieces = get_tree().get_nodes_in_group("opponent")
|
|
# @TODO hardcoded value for number of pieces a side should have on the board
|
|
# If they are less than 1/4 strength ensure that reinforcements are queued
|
|
if opponent_pieces.size() <= 3 and self.reinforcements == null:
|
|
self.reinforcements = 2 # 2 turns away
|
|
self.reinforcements_size = 4
|
|
get_node("BottomBar/Help").set_text("Multiple opponent reinforcements detected inbound")
|
|
get_node("/root/Game/ThinkYouCan").play()
|
|
self.flash_help = 3
|
|
if self.reinforcement_buff != null:
|
|
self.reinforcement_buff += 1
|
|
if self.reinforcement_buff == null:
|
|
self.reinforcement_buff = 0
|
|
if self.reinforcements == null and not just_spawned:
|
|
var chance = lerp(0, 50, 1 - float(opponent_pieces.size())/16.0)
|
|
var i = self.rng.randi() % 100
|
|
print("Roll for reinforcements ", i, " < ", chance)
|
|
if i < chance:
|
|
self.reinforcements = 2
|
|
self.reinforcements_size = 1
|
|
get_node("BottomBar/Help").set_text("Inbound opponent reinforcements detected")
|
|
self.flash_help = 3
|
|
|
|
get_node("TopBar/Top/HBoxContainer/Turn").set_text(str(self.turn))
|
|
|
|
func _reset_help():
|
|
get_node("BottomBar/Help").set_text("Delay losing as long as possible. Your will lose.\nClick on piece to see it's possible moves and stats.\nDrag and drop a piece to make a move.")
|
|
|
|
func _on_game_end(force_condition = null):
|
|
get_node("/root/Game/TopBar/Bottom/Instruction").set_text("Game over")
|
|
self.current_state = 99
|
|
var player_victory = false
|
|
if force_condition == null:
|
|
if get_tree().get_nodes_in_group("opponent").empty():
|
|
player_victory = true
|
|
else:
|
|
player_victory = force_condition
|
|
if not player_victory:
|
|
get_node("/root/Game/EndSong").play()
|
|
get_node("/root/Game/BottomBar/Help").set_text("Unsurprisingly, the result was known before-hand.")
|
|
else:
|
|
get_node("/root/Game/BottomBar/Help").set_text("Well, I'll be damned. I didn't think this would happen!")
|
|
get_node("/root/Game/Impossible").play()
|
|
# Show a popup for new one, or quit
|
|
self._on_escape(true, "Game over!")
|
|
|
|
func _on_fail_game():
|
|
_on_game_end(false)
|
|
|
|
func _on_win_game():
|
|
_on_game_end(true)
|
|
|
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
func _process(delta):
|
|
if self.flash_help != null:
|
|
if self.flash_help > 0:
|
|
self.flash_help -= delta
|
|
else:
|
|
self._reset_help()
|
|
self.flash_help = null
|
|
var opponent_pieces = get_tree().get_nodes_in_group("opponent")
|
|
if opponent_pieces.empty() or get_tree().get_nodes_in_group("player").empty() and self.current_state != 99:
|
|
# The game is over
|
|
self._on_game_end()
|
|
if self.current_state == 1:
|
|
if self.ai_target != null:
|
|
var target_square = self.board_squares[self.ai_target]
|
|
var target = get_node("/root/Game/MarginContainer/Playfield").squares[self.ai_target].get_ref()
|
|
if self.ai_piece.get_global_position().distance_to(target.get_global_position()) >= 5.0:
|
|
self.ai_piece.set_global_position(
|
|
self.ai_piece.get_global_position().move_toward(
|
|
target.get_global_position(),
|
|
5.0
|
|
)
|
|
)
|
|
else:
|
|
# End movement
|
|
var square = square_of_piece(self.ai_piece)
|
|
if target_square['piece'] != null:
|
|
if square['piece'].damage >= target_square['piece'].health:
|
|
target_square['piece'].queue_free()
|
|
square['piece'].kills += 1
|
|
get_node("PanelRight/VBox/PieceInfo").set_piece_info(square['piece'])
|
|
var c = self.rng.randi() % 100
|
|
var index_to_play = null
|
|
for idx in self.on_player_lose_piece.keys():
|
|
if c < idx:
|
|
break
|
|
index_to_play = idx
|
|
if index_to_play != null:
|
|
print("ai loss Chance to play: ", c, " got index ", index_to_play)
|
|
get_node(self.on_player_lose_piece[index_to_play]).play()
|
|
else:
|
|
# Deal damage
|
|
target_square['piece'].health -= square['piece'].damage
|
|
# @TODO Sound effect
|
|
# @TODO Visual indication of damage dealt
|
|
# Bounce piece back
|
|
target_square = square
|
|
square['piece'] = null
|
|
target_square['piece'] = self.ai_piece
|
|
self.ai_piece.set_position(Vector2(target_square['x']*128, target_square['y']*128))
|
|
self.ai_piece.at_spawn = false
|
|
self.ai_target = null
|
|
self.ai_piece = null
|
|
self._on_phase_end()
|
|
else:
|
|
# @TODO Find a way to run in a BG thread or split workload across frames
|
|
# if it takes too long to narrow down a move.
|
|
var moves = []
|
|
for piece in get_tree().get_nodes_in_group("opponent"):
|
|
moves += get_valid_piece_moves(piece)
|
|
# Our highest priority moves are to take another piece
|
|
var priority_moves = []
|
|
for m in moves:
|
|
if m.has("target"):
|
|
priority_moves.append(m)
|
|
if not priority_moves.empty():
|
|
# @TODO Check for the most "valuable" piece to take
|
|
var i = self.rng.randi() % (priority_moves.size())
|
|
self.ai_target = priority_moves[i]['pos']
|
|
self.ai_piece = priority_moves[i]['source']
|
|
print("Opponent moving ", self.ai_piece, " to ", self.ai_target, " from ", square_of_piece(self.ai_piece))
|
|
elif not moves.empty():
|
|
# @TODO Sort our moves to try and get the furthest forward
|
|
# possible
|
|
var i = self.rng.randi() % (moves.size())
|
|
self.ai_target = moves[i]['pos']
|
|
self.ai_piece = moves[i]['source']
|
|
print("Opponent moving ", self.ai_piece, " to ", self.ai_target, " from ", square_of_piece(self.ai_piece))
|
|
else:
|
|
# @TODO Would be a good time to spawn a new piece for the opponent
|
|
self._on_phase_end()
|
|
print("No possible moves")
|
|
|
|
|
|
func _physics_process(delta):
|
|
if self.landing_piece != null:
|
|
var piece = self.landing_piece
|
|
var square = square_of_piece(piece)
|
|
var moves = get_valid_piece_moves(piece)
|
|
# Try Land the piece
|
|
var matches = get_world_2d().direct_space_state.intersect_point(
|
|
self.landing_pos, 32, [], 2147483647, true, true)
|
|
var s = null
|
|
for m in matches:
|
|
if m["collider"].get_parent() is Square:
|
|
print(m["collider"].get_parent())
|
|
s = m["collider"].get_parent()
|
|
if s == null:
|
|
# No valid position, we'll return the piece to where it should be
|
|
piece.set_position(Vector2(
|
|
square["x"] * 128, square["y"] * 128
|
|
))
|
|
else:
|
|
var pf = get_node("/root/Game/MarginContainer/Playfield")
|
|
var dest = null
|
|
for k in pf.squares.keys():
|
|
if pf.squares[k].get_ref() == s:
|
|
dest = k
|
|
if dest != null:
|
|
print(dest)
|
|
var dest_valid = false;
|
|
for m in moves:
|
|
if m['pos'] == dest:
|
|
dest_valid = true
|
|
if dest_valid:
|
|
var dest_square = self.board_squares[dest]
|
|
if dest_square['piece'] != null:
|
|
# @TODO If the target doesn't die, we need to bounce back
|
|
if square['piece'].damage >= dest_square['piece'].health:
|
|
dest_square['piece'].queue_free()
|
|
square['piece'].kills += 1
|
|
get_node("PanelRight/VBox/PieceInfo").set_piece_info(square['piece'])
|
|
var c = self.rng.randi() % 100
|
|
var index_to_play = null
|
|
for idx in self.on_ai_lose_piece.keys():
|
|
if c < idx:
|
|
break
|
|
index_to_play = idx;
|
|
if index_to_play != null:
|
|
print("ai loss Chance to play: ", c, " got index ", index_to_play)
|
|
get_node(self.on_ai_lose_piece[index_to_play]).play()
|
|
else:
|
|
# @TODO Play a sound effect
|
|
# @TODO Visual indication of damage dealt
|
|
dest_square['piece'].health -= square['piece'].damage
|
|
dest_square = square
|
|
square['piece'] = null
|
|
dest_square['piece'] = piece
|
|
piece.set_position(Vector2(dest_square['x']*128, dest_square['y']*128))
|
|
piece.at_spawn = false
|
|
self._on_phase_end()
|
|
else:
|
|
# invalid destination bounce back
|
|
piece.set_position(Vector2(square['x']*128, square['y']*128))
|
|
else:
|
|
piece.set_position(Vector2(square['x']*128, square['y']*128))
|
|
|
|
self.landing_piece = null
|
|
|
|
|
|
|
|
func _on_EndSong_finished():
|
|
get_node("/root/Game/EndSong").stop()
|
|
|
|
|
|
func _on_Yammering_finished():
|
|
get_node("/root/Game/Yammering").stop()
|
|
|
|
|
|
func _on_Dust_finished():
|
|
get_node("/root/Game/Dust").stop()
|
|
|
|
|
|
func _on_ThinkYouCan_finished():
|
|
get_node("/root/Game/ThinkYouCan").stop()
|
|
|
|
|
|
func _on_Impossible_finished():
|
|
get_node("/root/Game/Impossible").stop()
|
|
|
|
|
|
func _on_Hehe_finished():
|
|
get_node("/root/Game/Hehe").stop()
|
|
|
|
|
|
func _on_Huh_finished():
|
|
get_node("/root/Game/Huh").stop()
|
|
|
|
|
|
func _on_StopThat_finished():
|
|
get_node("/root/Game/StopThat").stop()
|
|
|
|
|
|
func _on_SkipTurnButton_pressed():
|
|
self._on_phase_end()
|