365 lines
9.7 KiB
GDScript
365 lines
9.7 KiB
GDScript
extends Node
|
|
|
|
|
|
## difficulty levels
|
|
enum Difficulty {
|
|
SWEET, # slower enemies, infinite lives
|
|
SALTY, # normal enemies, infinite lives
|
|
SPICY, # normal enemies, 3 lives, DEFAULT
|
|
PUNGENT, # faster enemies, 3 lives
|
|
}
|
|
|
|
# score multiplier for easy mode
|
|
const EASY_DIFFICULTY_MODIFIER: float = 0.75
|
|
|
|
|
|
## resolution the game renders at
|
|
const RESOLUTION := Vector2(256,192)
|
|
const DIFFICULTY_NAMES := ["Sweet","Salty","Spicy","Pungent"]
|
|
|
|
|
|
#== collectibles ==#
|
|
var keys: int = 0 # collected keys
|
|
## which stars are collected
|
|
var stars_collected := [false,false,false,false,false]
|
|
var stars : int setget , _get_stars # total stars
|
|
## which stars are collected
|
|
var shards_collected := [false,false,false,false,false,false,false,false]
|
|
## how many shards collected
|
|
var shards: int setget , _get_shards # total shards
|
|
var arrows: int = 0 # current arrows
|
|
#== stats ==#
|
|
var lives: int = 2
|
|
var deaths: int = 0
|
|
var time: float = 0.0
|
|
#== score ==#
|
|
var score: int = 0
|
|
var arrows_bonus: int = 0
|
|
var collection_bonus: int = 0
|
|
var time_bonus: int = 0
|
|
var life_bonus: int = 0
|
|
var perfect_bonus: int = 0
|
|
var final_score: int = 0
|
|
var old_high_score: int = 0
|
|
#== state ==#
|
|
var current_sector := Vector2.ZERO
|
|
var respawn_point := Vector2(32,166)
|
|
var current_level: int = 0
|
|
var difficulty: int = Difficulty.SPICY setget _set_difficulty
|
|
var enemy_speed_factor: float = 1.0 # multiplier of enemy speed
|
|
var is_easy_mode: bool = false # whether to do easy-specific behaviors
|
|
var use_lives: bool = false
|
|
var can_pause: bool = true
|
|
var can_restart: bool = true
|
|
var current_palette: String = "default"
|
|
var still_playing: bool = false
|
|
var last_mm_button = null
|
|
var alternate_death = null
|
|
var can_use_shield: bool = false
|
|
#== marathon mode ==#
|
|
var marathon_mode: bool = false
|
|
var marathon_score: int = 0
|
|
var marathon_lives: int = 0
|
|
var marathon_shards: int = 0
|
|
var marathon_deaths: int = 0
|
|
|
|
|
|
func _get_stars() -> int:
|
|
return stars_collected.count(true)
|
|
|
|
|
|
func _get_shards() -> int:
|
|
return shards_collected.count(true)
|
|
|
|
|
|
# stuff to change when setting difficulty
|
|
func _set_difficulty(value: int) -> void:
|
|
difficulty = value
|
|
match difficulty:
|
|
Difficulty.SWEET:
|
|
is_easy_mode = true
|
|
enemy_speed_factor = 0.75
|
|
use_lives = false
|
|
Difficulty.SALTY:
|
|
is_easy_mode = false
|
|
enemy_speed_factor = 1.0
|
|
use_lives = false
|
|
Difficulty.SPICY:
|
|
is_easy_mode = false
|
|
enemy_speed_factor = 1.0
|
|
use_lives = true
|
|
Difficulty.PUNGENT:
|
|
is_easy_mode = false
|
|
enemy_speed_factor = 1.25
|
|
use_lives = true
|
|
|
|
#Instances a node
|
|
func instance_node(node:PackedScene,x:float,y:float,parent):
|
|
var Instance = node.instance()
|
|
Instance.global_position = Vector2(x,y)
|
|
parent.call_deferred("add_child", Instance)
|
|
|
|
#Get position in sectors
|
|
func get_sector(pos):
|
|
return (pos / RESOLUTION).floor()
|
|
|
|
#Return the current Map
|
|
func get_map():
|
|
return get_tree().get_nodes_in_group("map").front()
|
|
|
|
|
|
## tally up scores
|
|
func tally_scores() -> void:
|
|
var map = get_map()
|
|
# arrows bonus
|
|
arrows_bonus = arrows * 100
|
|
# collection bonus
|
|
if keys >= 50:
|
|
collection_bonus += 500
|
|
if _get_shards() >= 5:
|
|
collection_bonus += 500
|
|
# 100% collection
|
|
if keys >= 50 and _get_shards() >= 5:
|
|
collection_bonus += 250
|
|
shards_collected[5] = true
|
|
# 100% time bonus
|
|
if time < map.target_time_100:
|
|
time_bonus = max(2500 - int(2500.0 * time / map.target_time_100), 0) as int
|
|
shards_collected[6] = true
|
|
else:
|
|
# any% time bonus
|
|
if time < map.target_time_any:
|
|
time_bonus = max(2500 - int(2500.0 * time / map.target_time_any), 0) as int
|
|
shards_collected[6] = true
|
|
# life bonus
|
|
if deaths == 0:
|
|
life_bonus = 1500
|
|
shards_collected[7] = true
|
|
elif deaths == 1:
|
|
life_bonus = 500
|
|
# perfect bonus
|
|
if shards_collected[5] and shards_collected[6] and shards_collected[7]:
|
|
perfect_bonus += 1000
|
|
# final score
|
|
final_score = score + arrows_bonus + collection_bonus + time_bonus + life_bonus + perfect_bonus
|
|
# easy mode modifier
|
|
if difficulty == Difficulty.SWEET:
|
|
final_score *= EASY_DIFFICULTY_MODIFIER
|
|
if marathon_mode:
|
|
marathon_shards += shards_collected.count(true)
|
|
marathon_score += final_score
|
|
else:
|
|
Game.save()
|
|
|
|
|
|
#Go to new map
|
|
func change_map(map: PackedScene) -> void:
|
|
get_tree().paused = true
|
|
can_pause = false
|
|
Fade.fade_out(Options.transition_speed_secs)
|
|
yield(Fade, "fade_finished")
|
|
can_pause = true
|
|
# save deaths
|
|
if not marathon_mode:
|
|
var save_id = LevelData.levels[current_level].save_id
|
|
var save_data: Save.LevelSaveData = Save.current_file.levels[save_id]
|
|
save_data.deaths += deaths
|
|
Save.current_file.play_time += time
|
|
Save.current_file.save_to_file()
|
|
# clear
|
|
clear_collectibles()
|
|
StainLayer.reset()
|
|
#Loop is so no more than one level is loaded at a time
|
|
SceneManager.current_scene = map.instance()
|
|
|
|
#Clear data
|
|
func clear_collectibles() -> void:
|
|
# collectibles
|
|
keys = 0
|
|
stars_collected.fill(false)
|
|
shards_collected.fill(false)
|
|
arrows = 0
|
|
if not marathon_mode:
|
|
lives = 2
|
|
deaths = 0
|
|
# score
|
|
score = 0
|
|
collection_bonus = 0
|
|
time_bonus = 0
|
|
life_bonus = 0
|
|
perfect_bonus = 0
|
|
final_score = 0
|
|
|
|
#Save
|
|
func save():
|
|
if marathon_mode:
|
|
return
|
|
|
|
var new_super := Save.current_file.is_super_player()
|
|
|
|
# get level's save data object
|
|
var save_id = LevelData.levels[current_level].save_id
|
|
var save_data: Save.LevelSaveData = Save.current_file.levels[save_id]
|
|
|
|
old_high_score = max(save_data.score_100, save_data.score_any)
|
|
|
|
# save score and time depending on completion
|
|
if _get_shards() >= 5 && keys >= 50:
|
|
save_data.score_100 = max(save_data.score_100, final_score) as int
|
|
save_data.time_100 = min(save_data.time_100, time)
|
|
else:
|
|
save_data.score_any = max(save_data.score_any, final_score) as int
|
|
save_data.time_any = min(save_data.time_any, time)
|
|
|
|
# set shards
|
|
for i in 8:
|
|
if shards_collected[i]:
|
|
save_data.shards_collected[i] = true
|
|
|
|
# set keys
|
|
save_data.keys_collected = max(save_data.keys_collected, keys) as int
|
|
|
|
# mark as completed
|
|
save_data.completed = true
|
|
|
|
# add to playtime
|
|
Save.current_file.play_time += time
|
|
|
|
if new_super and Save.current_file.is_super_player():
|
|
Save.current_file.palette = "super_player"
|
|
|
|
# save the file
|
|
Save.current_file.save_to_file()
|
|
|
|
|
|
# smaller save function for bosses
|
|
func save_boss() -> void:
|
|
if marathon_mode:
|
|
return
|
|
|
|
var new_super := not Save.current_file.is_super_player()
|
|
|
|
var save_id = LevelData.levels[current_level].save_id
|
|
var save_data: Save.LevelSaveData = Save.current_file.levels[save_id]
|
|
|
|
save_data.time_any = min(save_data.time_any, time)
|
|
save_data.completed = true
|
|
|
|
Save.current_file.play_time += time
|
|
|
|
if new_super and Save.current_file.is_super_player():
|
|
Save.current_file.palette = "super_player"
|
|
|
|
Save.current_file.save_to_file()
|
|
|
|
|
|
# !!DEPRECATED!! convert milliseconds into M:SS:MS
|
|
func timeify(input):
|
|
if input <= 5999099:
|
|
var seconds = fmod(Game.time / 1000,60)
|
|
var minutes = floor((Game.time / 1000) / 60)
|
|
var centiseconds = fmod(floor(seconds * 100),100)
|
|
return ("%02d" % minutes) + ":" + ("%02d" % seconds) + "." + ("%02d" % centiseconds)
|
|
elif input == INF:
|
|
return "-:--.--"
|
|
else:
|
|
return "99:59.99"
|
|
|
|
# convert seconds into M:SS.MS
|
|
func format_time(seconds: float) -> String:
|
|
if is_inf(seconds) or is_nan(seconds): # infinite
|
|
return "--:--.--"
|
|
elif seconds >= 6000.0: # 10 minutes or greater
|
|
return "99:99.99"
|
|
else:
|
|
var minutes = floor(seconds / 60.0)
|
|
var centiseconds = fmod(floor(seconds * 100.0), 100.0)
|
|
return "%02d:%02d.%02d" % [minutes, fmod(seconds, 60.0), centiseconds]
|
|
|
|
#Restart level
|
|
func restart_level():
|
|
if can_restart:
|
|
Save.current_file.play_time += time
|
|
Save.current_file.levels[LevelData.levels[current_level].save_id].deaths += deaths
|
|
Save.current_file.save_to_file()
|
|
clear_collectibles()
|
|
Audio.ac_climb.stop()
|
|
Audio.ac_die.stop()
|
|
Engine.time_scale = 1.0
|
|
for tween in get_tree().get_processed_tweens():
|
|
tween.kill()
|
|
change_map(load(get_map().filename))
|
|
Audio.ac_music.stream_paused = false
|
|
|
|
#Freeze frame
|
|
func freeze_frame(freeze_time):
|
|
get_tree().paused = true
|
|
var timer = get_tree().create_timer(freeze_time, true)
|
|
timer.connect("timeout", get_tree(), "set_pause", [false])
|
|
return timer
|
|
|
|
#Check if 100%ed
|
|
func has_collection_bonus():
|
|
return _get_shards() == 5 && keys == 50
|
|
|
|
# called when player dies
|
|
func _on_player_died() -> void:
|
|
deaths += 1
|
|
if marathon_mode:
|
|
marathon_deaths += 1
|
|
if use_lives and lives <= 0:
|
|
still_playing = false
|
|
can_restart = false
|
|
can_pause = false
|
|
if not marathon_mode:
|
|
Save.current_file.play_time += time
|
|
Save.current_file.levels[LevelData.levels[current_level].save_id].deaths += deaths
|
|
Save.current_file.save_to_file()
|
|
clear_collectibles()
|
|
Audio.play_sound(Audio.a_game_over, Audio.ac_die)
|
|
get_tree().get_nodes_in_group("player")[0].queue_free()
|
|
var time_tween = create_tween()
|
|
time_tween.tween_property(Engine, "time_scale", 0.1, 0.3)
|
|
Audio.ac_music.stream_paused = true
|
|
yield(time_tween, "finished")
|
|
yield(get_tree().create_timer(1.0 * 0.1), "timeout")
|
|
Engine.time_scale = 1.0
|
|
Fade.fade_out(Options.transition_speed_secs)
|
|
yield(Fade, "fade_finished")
|
|
if marathon_mode:
|
|
SceneManager.current_scene = load("res://menus/main_menu.tscn").instance()
|
|
else:
|
|
var map = get_map()
|
|
var gover = load("res://menus/game_over.tscn").instance()
|
|
map.add_child(gover)
|
|
can_restart = true
|
|
can_pause = true
|
|
else:
|
|
# count death
|
|
lives -= 1
|
|
# play death sound
|
|
if alternate_death:
|
|
Audio.play_sound(alternate_death, Audio.ac_die)
|
|
else:
|
|
Audio.play_sound(Audio.a_die, Audio.ac_die)
|
|
alternate_death = null
|
|
# death score penalty
|
|
if use_lives == false && lives < 0:
|
|
score = max(0, score - 500) as int
|
|
# freezeframe
|
|
Game.freeze_frame(0.3)
|
|
|
|
func check_time_bonus():
|
|
var map = get_map()
|
|
if keys >= 50 and _get_shards() >= 5:
|
|
# 100% time bonus
|
|
if time < map.target_time_100:
|
|
return true
|
|
else:
|
|
return false
|
|
else:
|
|
# any% time bonus
|
|
if time < map.target_time_any:
|
|
return true
|
|
else:
|
|
return false
|