Add highscores saves and level selection. The levels are not played sequentially, but the level played can be choosen at start.
This commit is contained in:
parent
0d6809d1fb
commit
fc707b23f7
36
main.gd
36
main.gd
|
@ -1,22 +1,31 @@
|
|||
extends Node
|
||||
|
||||
var StartScreen = preload("res://screens/start_screen.tscn")
|
||||
var LevelSelectScreen = preload("res://screens/level_select_screen.tscn")
|
||||
var GameScreen = preload("res://screens/game_screen.tscn")
|
||||
var GameOverScreen = preload("res://screens/game_over_screen.tscn")
|
||||
var PauseScreen = preload("res://screens/pause_screen.tscn")
|
||||
var HighScores = preload("res://tools/highscores.gd")
|
||||
var Levels = preload("res://tools/levels.gd")
|
||||
|
||||
# Variables
|
||||
var current_screen = null # Current displayed screen
|
||||
var current_state = "" # Current state
|
||||
var game_screen = null # To store the game screen while in pause
|
||||
|
||||
var highscores = HighScores.new() # Object to keep the scores
|
||||
var levels = Levels.new() # Object to keep the levels
|
||||
|
||||
func _ready():
|
||||
# Read the high scores
|
||||
highscores.load_scores()
|
||||
# Go to the start screen
|
||||
goToStartScreen()
|
||||
|
||||
func _on_next_screen(next):
|
||||
if next == "start":
|
||||
goToStartScreen()
|
||||
if next == "select":
|
||||
goToLevelSelectScreen()
|
||||
elif next == "game":
|
||||
goToGameScreen()
|
||||
elif next == "game_won":
|
||||
|
@ -38,8 +47,20 @@ func goToStartScreen():
|
|||
current_screen.connect("next_screen", self, "_on_next_screen")
|
||||
add_child(current_screen)
|
||||
|
||||
func goToLevelSelectScreen():
|
||||
# Clean up current scene
|
||||
if current_screen != null:
|
||||
current_screen.queue_free()
|
||||
if game_screen != null:
|
||||
game_screen.queue_free()
|
||||
game_screen = null
|
||||
current_state = "select"
|
||||
current_screen = LevelSelectScreen.instance()
|
||||
current_screen.init(levels, highscores)
|
||||
current_screen.connect("next_screen", self, "_on_next_screen")
|
||||
add_child(current_screen)
|
||||
|
||||
func goToGameScreen():
|
||||
var start_level = 0
|
||||
if current_screen != null:
|
||||
current_screen.queue_free()
|
||||
current_state = "game"
|
||||
|
@ -49,27 +70,34 @@ func goToGameScreen():
|
|||
game_screen = null
|
||||
else:
|
||||
current_screen = GameScreen.instance()
|
||||
current_screen.init(start_level)
|
||||
current_screen.init(levels.get_current())
|
||||
current_screen.connect("next_screen", self, "_on_next_screen")
|
||||
add_child(current_screen)
|
||||
|
||||
func goToGameOverScreen(hasWon):
|
||||
# Retrieve score
|
||||
var score = 0
|
||||
var level_name = ""
|
||||
if current_state == "game":
|
||||
score = current_screen.get_score()
|
||||
level_name = current_screen.get_node("LevelName").text
|
||||
elif game_screen != null:
|
||||
score = game_screen.get_score()
|
||||
level_name = game_screen.get_node("LevelName").text
|
||||
# Clean up current scene
|
||||
if current_screen != null:
|
||||
current_screen.queue_free()
|
||||
if game_screen != null:
|
||||
game_screen.queue_free()
|
||||
game_screen = null
|
||||
# Highscores are updated only when a level is won (terminated)
|
||||
var is_new_high = false
|
||||
if hasWon:
|
||||
is_new_high = highscores.set_score(level_name, score)
|
||||
# Prepare screen
|
||||
current_state = "game_over"
|
||||
current_screen = GameOverScreen.instance()
|
||||
current_screen.init(hasWon, score)
|
||||
current_screen.init(hasWon, score, is_new_high)
|
||||
current_screen.connect("next_screen", self, "_on_next_screen")
|
||||
add_child(current_screen)
|
||||
|
||||
|
|
|
@ -8,8 +8,10 @@ signal next_screen(screen_name)
|
|||
func _ready():
|
||||
pass
|
||||
|
||||
func init(hasWon, score):
|
||||
if hasWon:
|
||||
func init(hasWon, score, isNewHigh):
|
||||
if hasWon && isNewHigh:
|
||||
$Status.text = "New High Score !"
|
||||
elif hasWon:
|
||||
$Status.text = "Victory !"
|
||||
else:
|
||||
$Status.text = "Game Over !"
|
||||
|
|
|
@ -3,31 +3,9 @@ extends Node
|
|||
signal next_screen(screen_name)
|
||||
|
||||
var Ball = preload("res://ball/ball.tscn")
|
||||
# List of levels
|
||||
var Levels = [
|
||||
# - Classic - #
|
||||
preload("res://levels/level_01_01.tscn"), # "Breakout" - Disposition from the 1st breakout game
|
||||
preload("res://levels/level_01_02.tscn"), # "Behind the wall" - Wall block
|
||||
preload("res://levels/level_01_03.tscn"), # "Building" - Hard blocks
|
||||
preload("res://levels/level_01_04.tscn"), # "Heart" - Half blocks
|
||||
preload("res://levels/level_01_05.tscn"), # "Paw" - (no new block)
|
||||
# - Hex - #
|
||||
preload("res://levels/level_02_01.tscn"), # "Hive" - Hexagonal blocks
|
||||
preload("res://levels/level_02_02.tscn"), # "Flowers" - Non-regular hexagons
|
||||
preload("res://levels/level_02_03.tscn"), # "Leaves" - (no new block)
|
||||
preload("res://levels/level_02_04.tscn"), # "Aquarium" - Fish blocks (+ Leaves)
|
||||
preload("res://levels/level_02_05.tscn"), # "Stars" - Star3 blocks
|
||||
# - Moving/Timing blocks - #
|
||||
# "Invasion" - Left-right movement
|
||||
# "Ghosts" - Blinking blocks : block appear / disappear
|
||||
# "Gems" - Gravity blocks - cf. Boulder Dash
|
||||
# "Many-Eyed Angel" - Blinking blocks 2 : wall / non-wall
|
||||
# "Tris" - Falling blocks / appearing at random on the top
|
||||
]
|
||||
|
||||
# Declare member variables here.
|
||||
var ball = null # Ball on the field
|
||||
var current_level = 0 # Index of the current levels
|
||||
var level = null # Current level
|
||||
var total_score = 0 # Score
|
||||
var paused = false
|
||||
|
@ -36,11 +14,10 @@ var paused = false
|
|||
func _ready():
|
||||
pass
|
||||
|
||||
func init(level_index):
|
||||
current_level = level_index
|
||||
func init(level_type):
|
||||
total_score = 0
|
||||
update_score()
|
||||
new_level()
|
||||
new_level(level_type)
|
||||
new_ball()
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
|
@ -63,8 +40,8 @@ func unpause():
|
|||
$BGM.play()
|
||||
paused = false
|
||||
|
||||
func new_level():
|
||||
level = Levels[current_level].instance()
|
||||
func new_level(level_type):
|
||||
level = level_type.instance()
|
||||
level.connect("level_ended", self, "_on_level_ended")
|
||||
level.connect("score_increased", self, "_on_score_increased")
|
||||
$LevelName.text = level.level_name
|
||||
|
@ -105,9 +82,4 @@ func _on_EndLevelTimer_timeout():
|
|||
ball.disconnect("ball_lost", self, "_on_ball_lost")
|
||||
level.queue_free()
|
||||
ball.queue_free()
|
||||
current_level = current_level + 1
|
||||
if current_level >= Levels.size():
|
||||
emit_signal("next_screen", "game_won")
|
||||
else:
|
||||
new_level()
|
||||
new_ball()
|
||||
emit_signal("next_screen", "game_won")
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
extends Node
|
||||
|
||||
signal next_screen(screen_name)
|
||||
|
||||
var level_list = null
|
||||
var highscore_list = null
|
||||
var shown_level = null
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
# Init with the level list
|
||||
func init(levels, highscores):
|
||||
level_list = levels
|
||||
highscore_list = highscores
|
||||
display_level()
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(_delta):
|
||||
if Input.is_action_just_pressed('ui_accept'):
|
||||
emit_signal("next_screen", "game")
|
||||
if Input.is_action_just_pressed("ui_menu"):
|
||||
emit_signal("next_screen", "start")
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
level_list.go_to_prev()
|
||||
display_level()
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
level_list.go_to_next()
|
||||
display_level()
|
||||
|
||||
# Display the level
|
||||
func display_level():
|
||||
if shown_level != null:
|
||||
shown_level.queue_free()
|
||||
shown_level = level_list.get_current().instance()
|
||||
$LevelName.text = shown_level.level_name
|
||||
var high = highscore_list.get_score(shown_level.level_name)
|
||||
if high == null:
|
||||
$Score.text = "-no highscore-"
|
||||
else:
|
||||
$Score.text = str(high)
|
||||
$Level.call_deferred("add_child", shown_level)
|
|
@ -0,0 +1,69 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://screens/NovaOval.ttf" type="DynamicFontData" id=1]
|
||||
[ext_resource path="res://empty_level/empty_level.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://screens/level_select_screen.gd" type="Script" id=3]
|
||||
|
||||
[sub_resource type="DynamicFont" id=1]
|
||||
size = 24
|
||||
font_data = ExtResource( 1 )
|
||||
|
||||
[sub_resource type="DynamicFont" id=2]
|
||||
size = 24
|
||||
font_data = ExtResource( 1 )
|
||||
|
||||
[sub_resource type="DynamicFont" id=3]
|
||||
size = 50
|
||||
font_data = ExtResource( 1 )
|
||||
|
||||
[node name="LevelSelectScreen" type="Node"]
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="EmptyLevel" parent="." instance=ExtResource( 2 )]
|
||||
|
||||
[node name="Score" type="Label" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
margin_left = -440.0
|
||||
margin_right = -72.0
|
||||
margin_bottom = 32.0
|
||||
custom_fonts/font = SubResource( 1 )
|
||||
custom_colors/font_color = Color( 0.552941, 0.960784, 0.505882, 1 )
|
||||
text = "SCORE"
|
||||
align = 2
|
||||
valign = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="LevelName" type="Label" parent="."]
|
||||
margin_left = 72.0
|
||||
margin_right = 440.0
|
||||
margin_bottom = 32.0
|
||||
custom_fonts/font = SubResource( 2 )
|
||||
custom_colors/font_color = Color( 0.552941, 0.960784, 0.505882, 1 )
|
||||
text = "NAME"
|
||||
valign = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Level" type="Node" parent="."]
|
||||
|
||||
[node name="Instructions" type="Label" parent="."]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
margin_left = -512.0
|
||||
margin_top = -125.0
|
||||
margin_right = 512.0
|
||||
custom_fonts/font = SubResource( 3 )
|
||||
text = "Select level with next and right,
|
||||
then press space"
|
||||
align = 1
|
||||
valign = 1
|
||||
autowrap = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
|
@ -15,7 +15,7 @@ func init():
|
|||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(_delta):
|
||||
if Input.is_action_just_pressed('ui_accept'):
|
||||
emit_signal("next_screen", "game")
|
||||
emit_signal("next_screen", "select")
|
||||
elif Input.is_action_just_pressed("ui_menu"):
|
||||
get_tree().notification(MainLoop.NOTIFICATION_WM_QUIT_REQUEST)
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
extends Reference
|
||||
|
||||
# Score list. A map level_name -> value
|
||||
var scores = {}
|
||||
# Path to score file
|
||||
const score_file = "user://highscores.json"
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
# Return the score or null if unset
|
||||
func get_score(level_name: String):
|
||||
return scores.get(level_name, null)
|
||||
|
||||
# Set score, return true if a new high score was set, false otherwise
|
||||
func set_score(level_name: String, new_score: int):
|
||||
var old_score = get_score(level_name)
|
||||
if (old_score == null) or (new_score > old_score):
|
||||
scores[level_name] = new_score
|
||||
save_scores()
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func save_scores():
|
||||
# Serialize the scores in JSON
|
||||
var serialized = JSON.print(scores)
|
||||
# Write them to score file
|
||||
var file = File.new()
|
||||
var err = file.open(score_file, File.WRITE)
|
||||
if err == OK:
|
||||
file.store_string(serialized)
|
||||
file.close()
|
||||
else:
|
||||
print("Error opening file " + score_file + " - Unable to save scores")
|
||||
|
||||
func load_scores():
|
||||
# Clear scores
|
||||
scores.clear()
|
||||
# Read score file, if it exists
|
||||
var file = File.new()
|
||||
if file.file_exists(score_file):
|
||||
var err = file.open(score_file, File.READ)
|
||||
if err == OK:
|
||||
var serialized = file.get_as_text()
|
||||
file.close()
|
||||
# Parse JSON
|
||||
var parsed = JSON.parse(serialized)
|
||||
# Check if OK
|
||||
if (parsed.error == OK) && (typeof(parsed.result) == TYPE_DICTIONARY):
|
||||
# Numeric values read from JSON are float type : must be converted to int
|
||||
for k in parsed.result.keys():
|
||||
scores[k] = int(parsed.result[k])
|
||||
else:
|
||||
print("Error reading file " + score_file + " - Unexpected format")
|
||||
else:
|
||||
print("Error opening existing file " + score_file)
|
|
@ -0,0 +1,54 @@
|
|||
extends Reference
|
||||
|
||||
# List of levels
|
||||
var list = [
|
||||
# - Classic - #
|
||||
preload("res://levels/level_01_01.tscn"), # "Breakout" - Disposition from the 1st breakout game
|
||||
preload("res://levels/level_01_02.tscn"), # "Behind the wall" - Wall block
|
||||
preload("res://levels/level_01_03.tscn"), # "Building" - Hard blocks
|
||||
preload("res://levels/level_01_04.tscn"), # "Heart" - Half blocks
|
||||
preload("res://levels/level_01_05.tscn"), # "Paw" - (no new block)
|
||||
# - Hex - #
|
||||
preload("res://levels/level_02_01.tscn"), # "Hive" - Hexagonal blocks
|
||||
preload("res://levels/level_02_02.tscn"), # "Flowers" - Non-regular hexagons
|
||||
preload("res://levels/level_02_03.tscn"), # "Leaves" - (no new block)
|
||||
preload("res://levels/level_02_04.tscn"), # "Aquarium" - Fish blocks (+ Leaves)
|
||||
preload("res://levels/level_02_05.tscn"), # "Stars" - Star3 blocks
|
||||
# - Moving/Timing blocks - #
|
||||
# "Invasion" - Left-right movement
|
||||
# "Ghosts" - Blinking blocks : block appear / disappear
|
||||
# "Gems" - Gravity blocks - cf. Boulder Dash
|
||||
# "Many-Eyed Angel" - Blinking blocks 2 : wall / non-wall
|
||||
# "Tris" - Falling blocks / appearing at random on the top
|
||||
]
|
||||
|
||||
# Index for moving in the list
|
||||
var current_level = 0
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
# Get current level
|
||||
func get_current():
|
||||
return list[current_level]
|
||||
|
||||
# Go to next level (list is circulary)
|
||||
func go_to_next():
|
||||
current_level = current_level + 1
|
||||
if current_level >= list.size():
|
||||
current_level = 0
|
||||
|
||||
# Go to previous level (list is circulary)
|
||||
func go_to_prev():
|
||||
current_level = current_level - 1
|
||||
if current_level < 0:
|
||||
current_level = list.size() - 1
|
||||
|
||||
# Check if current level is last
|
||||
func current_is_last():
|
||||
return current_level == list.size() - 1
|
||||
|
||||
# Check if current level is first
|
||||
func current_is_first():
|
||||
return current_level == 0
|
Loading…
Reference in New Issue