Added win/lose conditions, lose animation. Clicking on revealed tile with correct number of flags reveals the surrounding unflagged tiles. Added completion timer. Probably other things I'm forgetting

This commit is contained in:
Nolan Casey 2025-05-14 11:32:36 -04:00
parent 34a254b12c
commit 32150473c6
7 changed files with 112 additions and 14 deletions

View File

@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://b8ewc3373b0t2"]
[ext_resource type="Script" uid="uid://du63nf81skbpt" path="res://scripts/Minefield.gd" id="1_rm6t6"]
[node name="Minefield" type="Node2D"]
script = ExtResource("1_rm6t6")
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(4, 4)

View File

@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://b8ewc3373b0t2"]
[ext_resource type="Script" uid="uid://du63nf81skbpt" path="res://scripts/Minefield.gd" id="1_rm6t6"]
[node name="Minefield" type="Node2D"]
script = ExtResource("1_rm6t6")
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(4, 4)

View File

@ -11,8 +11,14 @@ static var instance: Minefield
@export var startingSafeZone: int = 2 @export var startingSafeZone: int = 2
var tiles: Array[Tile] var tiles: Array[Tile]
var mines: Array[Tile]
var falseFlags: Array[Tile]
var spriteWidth: int = 16 var spriteWidth: int = 16
var fieldInitialized: bool = false var fieldInitialized: bool = false
var gameRunning: bool = true
var revealed: int = 0
var winCondition: int
var timer: int
func _enter_tree() -> void: func _enter_tree() -> void:
if (instance == null || is_instance_valid(instance)): instance = self if (instance == null || is_instance_valid(instance)): instance = self
@ -20,6 +26,7 @@ func _enter_tree() -> void:
func _ready() -> void: func _ready() -> void:
var scale: float = 1 var scale: float = 1
winCondition = (width * height) - mineCount
for x: int in range(width): for x: int in range(width):
for y: int in range(height): for y: int in range(height):
var tile: Tile = tilePrefab.instantiate() var tile: Tile = tilePrefab.instantiate()
@ -38,10 +45,29 @@ func initField(coord: Vector2i) -> void:
pickPool.erase(pick) pickPool.erase(pick)
if (abs(coord - pick.coord) >= Vector2i(startingSafeZone, startingSafeZone)): if (abs(coord - pick.coord) >= Vector2i(startingSafeZone, startingSafeZone)):
pick.type = Tile.TileTypes.MINE pick.type = Tile.TileTypes.MINE
mines.append(pick)
break break
pick = pickPool.pick_random() pick = pickPool.pick_random()
for tile: Tile in tiles: for tile: Tile in tiles:
tile.evaluateType() tile.evaluateType()
timer = Time.get_ticks_msec()
func lose() -> void:
gameRunning = false
for mine: Tile in mines:
await get_tree().create_timer(randf_range(0.01, 0.25)).timeout
mine.reveal()
for flag: Tile in falseFlags:
await get_tree().create_timer(randf_range(0.01, 0.1)).timeout
flag.breakFlag()
func tilesRevealed(count: int) -> void:
revealed += count
if (revealed == winCondition):
gameRunning = false
var completionTime: int = Time.get_ticks_msec() - timer
var humanTime: float = float(completionTime) / 1000
print("You win.\nCompleted in " + str(humanTime) + " seconds")
func getTile(coord: Vector2i) -> Tile: func getTile(coord: Vector2i) -> Tile:
if (coord.x < 0 || coord.y < 0 || coord.x >= width || coord.y >= width): return null if (coord.x < 0 || coord.y < 0 || coord.x >= width || coord.y >= width): return null

View File

@ -4,6 +4,7 @@ class_name Tile extends Node2D
@onready var cover: CompressedTexture2D = preload("res://sprites/cover.png") @onready var cover: CompressedTexture2D = preload("res://sprites/cover.png")
@onready var mine: CompressedTexture2D = preload("res://sprites/mine.png") @onready var mine: CompressedTexture2D = preload("res://sprites/mine.png")
@onready var flag: CompressedTexture2D = preload("res://sprites/flag.png") @onready var flag: CompressedTexture2D = preload("res://sprites/flag.png")
@onready var brokenFlag: CompressedTexture2D = preload("res://sprites/brokenFlag.png")
@onready var nums: Array[CompressedTexture2D] = [ @onready var nums: Array[CompressedTexture2D] = [
preload("res://sprites/1.png"), preload("res://sprites/1.png"),
preload("res://sprites/2.png"), preload("res://sprites/2.png"),
@ -36,44 +37,63 @@ var coord: Vector2i
var revealed: bool = false var revealed: bool = false
var flagged: bool = false var flagged: bool = false
func evaluateType() -> void: func getNeighbors() -> Array[Tile]:
if (type == TileTypes.MINE): return var results: Array[Tile]
var count: int = 0
for x: int in range(-1, 2): for x: int in range(-1, 2):
for y: int in range(-1, 2): for y: int in range(-1, 2):
if (x == 0 && y == 0): continue if (x == 0 && y == 0): continue
var tile: Tile = Minefield.instance.getTile(coord + Vector2i(x, y)) var tile: Tile = Minefield.instance.getTile(coord + Vector2i(x, y))
if (tile == null): continue if (tile == null): continue
if (tile.type == TileTypes.MINE): count += 1 results.append(tile)
return results
func evaluateType() -> void:
if (type == TileTypes.MINE): return
var count: int = 0
for tile: Tile in getNeighbors():
if (tile.type == TileTypes.MINE): count += 1
type = count as TileTypes type = count as TileTypes
func reveal() -> void: func reveal() -> int:
if (revealed): return if (revealed || Minefield.instance.gameRunning && flagged): return 0
revealed = true revealed = true
var count: int = 1
layer1.texture = backplate layer1.texture = backplate
match type: match type:
TileTypes.EMPTY: TileTypes.EMPTY:
layer2.texture = null layer2.texture = null
for x: int in range(-1, 2): for tile: Tile in getNeighbors(): count += tile.reveal()
for y: int in range(-1, 2):
if (x == 0 && y == 0): continue
var tile: Tile = Minefield.instance.getTile(coord + Vector2i(x, y))
if (tile == null): continue
tile.reveal()
TileTypes.MINE: TileTypes.MINE:
layer2.texture = mine layer2.texture = mine
if (Minefield.instance.gameRunning): Minefield.instance.lose()
_: _:
layer2.texture = nums[type as int - 1] layer2.texture = nums[type as int - 1]
return count
func breakFlag() -> void:
layer2.texture = brokenFlag
func _on_area_2d_input_event(viewport: Node, event: InputEvent, shape_idx: int) -> void: func _on_area_2d_input_event(viewport: Node, event: InputEvent, shape_idx: int) -> void:
if (!Minefield.instance.gameRunning): return
if (event.is_released()): if (event.is_released()):
if (event.is_action_released("reveal") && !flagged): if (event.is_action_released("reveal") && revealed):
var count: int = 0
var neighbors: Array[Tile] = getNeighbors()
for tile: Tile in neighbors:
if (tile.flagged): count += 1
if (count == type as int):
count = 0
for tile: Tile in neighbors: count += tile.reveal()
Minefield.instance.tilesRevealed(count)
elif (event.is_action_released("reveal") && !flagged):
if (!Minefield.instance.fieldInitialized): if (!Minefield.instance.fieldInitialized):
Minefield.instance.initField(coord) Minefield.instance.initField(coord)
reveal() Minefield.instance.tilesRevealed(reveal())
elif (event.is_action_released("flag") && !revealed): elif (event.is_action_released("flag") && !revealed):
flagged = !flagged flagged = !flagged
if (flagged): if (flagged):
layer2.texture = flag layer2.texture = flag
if (type != TileTypes.MINE): Minefield.instance.falseFlags.append(self)
else: else:
layer2.texture = null layer2.texture = null
if (type != TileTypes.MINE): Minefield.instance.falseFlags.erase(self)

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d353xpjsitdct"
path="res://.godot/imported/brokenFlag.png-785bad89b513c2a3f4b8d504b0f83440.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sprites/brokenFlag.png"
dest_files=["res://.godot/imported/brokenFlag.png-785bad89b513c2a3f4b8d504b0f83440.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1