Add enemy respawns

This commit is contained in:
Kienan Stewart 2022-04-03 10:24:33 -04:00
parent 911bceb750
commit d22224a818
7 changed files with 143 additions and 17 deletions

14
TODO.md
View File

@ -1,22 +1,18 @@
1. Enemy respawns 1. Power ups
* every now and again, the opponents has new units that spawn
* indicator to show respawn is nearing
* is it all the missing units, or a chance for each?
2. Power ups
* a chance of having power ups spawn on an random unoccupied square when a unit dies * a chance of having power ups spawn on an random unoccupied square when a unit dies
* +health, +attack, +speed, +jump, (pawn only) remove "attack_only", spawn a new (random?) piece, change movement type * +health, +attack, +speed, +jump, (pawn only) remove "attack_only", spawn a new (random?) piece, change movement type
* then the enemy respawns units, their new units are stronger * then the enemy respawns units, their new units are stronger
* support for pieces with multiple hit points * support for pieces with multiple hit points
* unit info panel * unit info panel
3. Visual polish 2. Visual polish
* clean up tile borders * clean up tile borders
* multiple square tiles to add variation * multiple square tiles to add variation
* make the help text indicate (flash, etc.) * make the help text indicate (flash, etc.)
4. Sound effects 3. Sound effects
* on hit * on hit
* on piece lost * on piece lost
* on piece kill * on piece kill
* on opponent victory * on opponent victory
* if possible, a small bit of background music * if possible, a small bit of background music
5. Further visual polish 4. Further visual polish
6. New units 5. New units

Binary file not shown.

Before

Width:  |  Height:  |  Size: 892 B

After

Width:  |  Height:  |  Size: 891 B

View File

@ -32,6 +32,9 @@ var flash_help = null
var rng = null var rng = null
var ai_target = null var ai_target = null
var ai_piece = null var ai_piece = null
var reinforcements = null
var reinforcements_size = 0
var reinforcements_coords = []
const piece_types = { const piece_types = {
"pawn": "res://src/pieces/Pawn.tscn", "pawn": "res://src/pieces/Pawn.tscn",
@ -165,7 +168,7 @@ func _on_piece_click(piece, event):
if piece == null: if piece == null:
self.selected_piece = null self.selected_piece = null
return return
print("Selected piece: ", piece) #print("Selected piece: ", piece)
if piece.is_in_group("player"): if piece.is_in_group("player"):
piece.get_node("Body").set_modulate(Color(0, 1, 0, 1)) piece.get_node("Body").set_modulate(Color(0, 1, 0, 1))
else: else:
@ -278,7 +281,8 @@ func reset_game_state():
self.board_squares[Vector2(x, y)] = { self.board_squares[Vector2(x, y)] = {
"x": x, "x": x,
"y": y, "y": y,
"piece": null "piece": null,
"reinforcement": null,
} }
y += 1 y += 1
x += 1 x += 1
@ -321,13 +325,97 @@ func _on_phase_end():
self.current_state += 1; self.current_state += 1;
if not self.current_state in self.states.keys(): if not self.current_state in self.states.keys():
self.current_state = 0 self.current_state = 0
self.turn += 1
self._on_new_turn() self._on_new_turn()
get_node("TopBar/Bottom/Instruction").set_text( get_node("TopBar/Bottom/Instruction").set_text(
self.states[self.current_state]["description"] + " - " + self.states[self.current_state]["directive"] 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(): 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)
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")
self.flash_help = 3
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)) get_node("TopBar/Top/HBoxContainer/Turn").set_text(str(self.turn))
func _reset_help(): func _reset_help():
@ -341,7 +429,8 @@ func _process(delta):
else: else:
self._reset_help() self._reset_help()
self.flash_help = null self.flash_help = null
if get_tree().get_nodes_in_group("opponent").empty() or get_tree().get_nodes_in_group("player").empty(): var opponent_pieces = get_tree().get_nodes_in_group("opponent")
if opponent_pieces.empty() or get_tree().get_nodes_in_group("player").empty():
# The game is over # The game is over
self.current_state = 99 self.current_state = 99
var player_victory = false var player_victory = false

View File

@ -40,7 +40,7 @@ func _input(event):
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
else: else:
if self.last_click != null and self.last_click <= CLICK_THRESHOLD: if self.last_click != null and self.last_click <= CLICK_THRESHOLD:
print("Click: ", self, event) #print("Click: ", self, event)
emit_signal("click", self, event) emit_signal("click", self, event)
# Work-around bug where only the last signal is connected # Work-around bug where only the last signal is connected
# to the Game # to the Game
@ -52,7 +52,7 @@ func _input(event):
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
if not event.pressed and self.hold_started: if not event.pressed and self.hold_started:
emit_signal("hold_stop", self, event) emit_signal("hold_stop", self, event)
print("Hold stop", self, " ", event) #print("Hold stop", self, " ", event)
self.cancel_hold() self.cancel_hold()
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
@ -62,7 +62,7 @@ func _process(delta):
self.last_click += delta self.last_click += delta
if not self.hold_started and self.last_click >= self.CLICK_THRESHOLD: if not self.hold_started and self.last_click >= self.CLICK_THRESHOLD:
self.hold_started = true; self.hold_started = true;
print("Hold start", self, " ", null) #print("Hold start", self, " ", null)
emit_signal("hold_start", self, null) emit_signal("hold_start", self, null)
if self.hold_started: if self.hold_started:
self.set_global_position(get_viewport().get_mouse_position()) self.set_global_position(get_viewport().get_mouse_position())

View File

@ -29,6 +29,7 @@ func initialize(width: int = 8, height: int = 8):
# @TODO any tweaks to the node by calling custom function initialize() # @TODO any tweaks to the node by calling custom function initialize()
instance.translate(Vector2(128*i, 128*j)) instance.translate(Vector2(128*i, 128*j))
self.squares[Vector2(i, j)] = weakref(instance) self.squares[Vector2(i, j)] = weakref(instance)
instance.get_node("Control").set_tooltip(str(Vector2(i, j)))
add_child(instance) add_child(instance)
j += 1 j += 1
i += 1 i += 1

View File

@ -5,11 +5,41 @@ class_name Square
# var a = 2 # var a = 2
# var b = "text" # var b = "text"
var reinforcement = null
var timer = 0
const FLASH_FREQUENCY = 2 # seconds
const FLASH_MIN = Color(1, 0.75, 0.75, 1)
const FLASH_MAX = Color(1, 0.5, 0.5, 1)
const DEFAULT_BODY = Color(1, 1, 1, 1)
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
pass # Replace with function body. pass # Replace with function body.
func set_reinforcement(reinforcement):
self.reinforcement = reinforcement
self.timer = 0
print("Set reinforcement on ", self, " : ", reinforcement)
if reinforcement == null:
get_node("Body").set_modulate(DEFAULT_BODY)
get_node("Control").set_tooltip("")
else:
get_node("Control").set_tooltip("Arriving unit: " + reinforcement)
func _process(delta):
if self.reinforcement != null:
self.timer += delta
if self.timer >= FLASH_FREQUENCY*2:
self.timer = 0
if self.timer >= FLASH_FREQUENCY:
# Going from dark to light
get_node("Body").set_modulate(
lerp(FLASH_MIN, FLASH_MAX, 1-((self.timer-FLASH_FREQUENCY)/FLASH_FREQUENCY))
)
else:
# Going from light to dark
get_node("Body").set_modulate(
lerp(FLASH_MIN, FLASH_MAX, self.timer / FLASH_FREQUENCY)
)
# Called every frame. 'delta' is the elapsed time since the previous frame. # Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta): #func _process(delta):

View File

@ -19,6 +19,16 @@ modulate = Color( 0.12549, 0.0980392, 0.0980392, 1 )
texture = ExtResource( 1 ) texture = ExtResource( 1 )
[node name="Area2D" type="Area2D" parent="."] [node name="Area2D" type="Area2D" parent="."]
visible = false
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
visible = false
shape = SubResource( 1 ) shape = SubResource( 1 )
[node name="Control" type="Control" parent="."]
margin_right = 40.0
margin_bottom = 40.0
hint_tooltip = "Square"
__meta__ = {
"_edit_use_anchors_": false
}