Adds graphics for snake that updates according to movement (mostly)
|
@ -49,5 +49,4 @@ offset_top = 173.0
|
|||
offset_right = 812.0
|
||||
offset_bottom = 475.0
|
||||
|
||||
[connection signal="area_exited" from="Level" to="SnakePart" method="_on_level_area_exited"]
|
||||
[connection signal="timeout" from="RestartTimer" to="Timer" method="start" binds= [0.3]]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extends VBoxContainer
|
||||
|
||||
var level : PackedScene = load("res://Scenes/main.tscn")
|
||||
#var level : PackedScene = load("res://Scenes/main.tscn")
|
||||
var main_menu : PackedScene = load("res://Scenes/main_menu.tscn")
|
||||
@onready
|
||||
var score_label : Label = $FinalScore
|
||||
|
@ -10,7 +10,7 @@ func update_score() -> void:
|
|||
score_label.text = "Final Score\n{score}".format(GameManager)
|
||||
|
||||
func restart():
|
||||
get_tree().change_scene_to_packed(level)
|
||||
get_tree().change_scene_to_packed(load("res://Scenes/main.tscn"))
|
||||
|
||||
func back():
|
||||
get_tree().change_scene_to_packed(main_menu)
|
||||
|
|
|
@ -8,13 +8,15 @@ enum States {ALIVE, DEAD, OUROBOROS, OLD_OUROBOROS}
|
|||
var part_type : PartTypes = PartTypes.HEAD
|
||||
var state : States = States.ALIVE
|
||||
var current_direction : Vector2 = Vector2.RIGHT
|
||||
var old_direction : Vector2 = Vector2.RIGHT
|
||||
|
||||
var inputs : Dictionary[String, Vector2] = {"right": Vector2.RIGHT,
|
||||
"left": Vector2.LEFT,
|
||||
"up": Vector2.UP,
|
||||
"down": Vector2.DOWN}
|
||||
|
||||
|
||||
var possible_x : Array
|
||||
var possible_y : Array
|
||||
|
||||
@onready
|
||||
var raycast_right : RayCast2D = $RightRayCast2D
|
||||
|
@ -34,11 +36,23 @@ var timer_ref : Timer = $"../Timer"
|
|||
@onready
|
||||
var game_over_menu : VBoxContainer = get_tree().get_first_node_in_group("GameOverMenu")
|
||||
|
||||
@onready
|
||||
var sprite : Sprite2D = $Sprite2D
|
||||
|
||||
var image_head : CompressedTexture2D = preload("res://Sprites/snake_head.png")
|
||||
var image_head_dead : CompressedTexture2D = preload("res://Sprites/snake_head_dead.png")
|
||||
var image_head_ouroboros : CompressedTexture2D = preload("res://Sprites/snake_head_ouroboros.png")
|
||||
var image_left : CompressedTexture2D = preload("res://Sprites/snake_left.png")
|
||||
var image_right : CompressedTexture2D = preload("res://Sprites/snake_right.png")
|
||||
var image_straight : CompressedTexture2D = preload("res://Sprites/snake_straight.png")
|
||||
var image_tail : CompressedTexture2D = preload("res://Sprites/snake_tail.png")
|
||||
|
||||
var next_part : SnakePart = null
|
||||
var commanding_head : SnakePart = null
|
||||
var skip_next_move_propagation : bool = false
|
||||
var queued_growth : int = 0
|
||||
|
||||
signal on_movement(new_dir)
|
||||
signal on_movement(new_dir, prevent_move)
|
||||
signal on_death
|
||||
signal on_ouroboros
|
||||
|
||||
|
@ -48,10 +62,21 @@ func _ready() -> void:
|
|||
# Add to group for easy access from other nodes
|
||||
add_to_group("Head")
|
||||
|
||||
# Work out allowed positions
|
||||
|
||||
# Determine the level limits
|
||||
var level : Area2D = get_tree().get_first_node_in_group("Level")
|
||||
var x_bounds : Vector2 = level.position.x * Vector2.ONE + Vector2(-TILE_SIZE, TILE_SIZE) * level.scale.x / 2
|
||||
var y_bounds : Vector2 = level.position.y * Vector2.ONE + Vector2(-TILE_SIZE, TILE_SIZE) * level.scale.y / 2
|
||||
|
||||
# Determine the possible x and y coordinates
|
||||
possible_x = range(x_bounds.x, x_bounds.y, TILE_SIZE)
|
||||
possible_y = range(y_bounds.x, y_bounds.y, TILE_SIZE)
|
||||
|
||||
# Attach clock
|
||||
var timer : Node = get_tree().get_first_node_in_group("GameClock")
|
||||
if timer is Timer:
|
||||
timer.timeout.connect(process_movement.bind(Vector2.ZERO))
|
||||
timer.timeout.connect(process_movement.bind(Vector2.ZERO, false))
|
||||
|
||||
# Set up a body part
|
||||
var body = snake_part_obj.instantiate()
|
||||
|
@ -72,6 +97,7 @@ func _ready() -> void:
|
|||
body.on_movement.connect(tail.process_movement)
|
||||
on_death.connect(tail.lose_game)
|
||||
on_ouroboros.connect(tail.ouroboros)
|
||||
tail.commanding_head = self
|
||||
else:
|
||||
|
||||
# Free raycasts if we don't need them (consider only loading them in for the head for performance if needed)
|
||||
|
@ -133,7 +159,7 @@ func check_position_for_snake(query_position : Vector2) -> bool:
|
|||
return true
|
||||
return false
|
||||
|
||||
func process_movement(new_direction : Vector2) -> void:
|
||||
func process_movement(new_direction : Vector2, prevent_movment : bool = false) -> void:
|
||||
|
||||
# Only alive snakes can move
|
||||
if state != States.ALIVE:
|
||||
|
@ -142,27 +168,75 @@ func process_movement(new_direction : Vector2) -> void:
|
|||
# The head needs to check if we are about to collide with something
|
||||
if part_type == PartTypes.HEAD:
|
||||
check_movement()
|
||||
if queued_growth:
|
||||
if queued_growth and state == States.ALIVE:
|
||||
extend()
|
||||
|
||||
# Only alive snakes can move
|
||||
update_sprite()
|
||||
if state != States.ALIVE:
|
||||
return
|
||||
|
||||
# Update the position of this part
|
||||
position = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
|
||||
if !prevent_movment:
|
||||
position = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
|
||||
|
||||
# Tell the next part to move and give it the direction we are moving
|
||||
if !skip_next_move_propagation:
|
||||
on_movement.emit(current_direction)
|
||||
on_movement.emit(current_direction, prevent_movment or skip_next_move_propagation)
|
||||
skip_next_move_propagation = false
|
||||
|
||||
# Unless we are the head, change our direction to the direction given
|
||||
if new_direction != Vector2.ZERO:
|
||||
if !prevent_movment:
|
||||
old_direction = current_direction
|
||||
if new_direction != Vector2.ZERO and !prevent_movment:
|
||||
current_direction = new_direction
|
||||
|
||||
# Update the sprite
|
||||
update_sprite()
|
||||
|
||||
func update_sprite() -> void:
|
||||
|
||||
# Set the sprite based on state and part type
|
||||
match part_type:
|
||||
PartTypes.HEAD:
|
||||
match state:
|
||||
States.ALIVE:
|
||||
sprite.texture = image_head
|
||||
States.DEAD:
|
||||
sprite.texture = image_head_dead
|
||||
_:
|
||||
sprite.texture = image_head_ouroboros
|
||||
PartTypes.TAIL:
|
||||
if state == States.OUROBOROS or state == States.OLD_OUROBOROS:
|
||||
var tail_rel_direction : float = current_direction.angle_to(position - commanding_head.position)
|
||||
if abs(tail_rel_direction) < 0.1:
|
||||
sprite.texture = image_straight
|
||||
elif tail_rel_direction > 0:
|
||||
sprite.texture = image_left
|
||||
elif tail_rel_direction < 0:
|
||||
sprite.texture = image_right
|
||||
else:
|
||||
sprite.texture = image_tail
|
||||
PartTypes.BODY:
|
||||
var tail_rel_direction : float = current_direction.angle_to(old_direction)
|
||||
if abs(tail_rel_direction) < 0.1:
|
||||
sprite.texture = image_straight
|
||||
elif tail_rel_direction > 0:
|
||||
sprite.texture = image_left
|
||||
elif tail_rel_direction < 0:
|
||||
sprite.texture = image_right
|
||||
|
||||
if current_direction != Vector2.ZERO:
|
||||
sprite.rotation = current_direction.angle() + PI / 2
|
||||
else:
|
||||
sprite.rotation = old_direction.angle() + PI / 2
|
||||
|
||||
func check_movement() -> void:
|
||||
|
||||
# Make sure we are going to stay in the arena
|
||||
var new_position : Vector2 = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
|
||||
if not int(new_position.x) in possible_x or not int(new_position.y) in possible_y:
|
||||
lose_game()
|
||||
|
||||
# Get the correct raycast for the direction we are moving
|
||||
var raycast_to_use : RayCast2D
|
||||
if current_direction == Vector2.RIGHT:
|
||||
|
@ -213,6 +287,7 @@ func extend() -> void:
|
|||
|
||||
# Set direction of new part
|
||||
new_body.current_direction = current_direction
|
||||
new_body.old_direction = old_direction
|
||||
|
||||
# Update reference to next part
|
||||
next_part = new_body
|
||||
|
@ -224,20 +299,22 @@ func extend() -> void:
|
|||
queued_growth -= 1
|
||||
|
||||
|
||||
func lose_game() -> void:
|
||||
func lose_game(from_lack_of_space : bool = false) -> void:
|
||||
|
||||
# You lose!
|
||||
print("Game lost")
|
||||
state = States.DEAD
|
||||
on_death.emit()
|
||||
if !from_lack_of_space:
|
||||
state = States.DEAD
|
||||
on_death.emit()
|
||||
game_over_menu.visible = true
|
||||
|
||||
func ouroboros() -> void:
|
||||
print("You have achieved the ouroboros!")
|
||||
if state == States.OUROBOROS:
|
||||
state = States.OLD_OUROBOROS
|
||||
elif state == States.ALIVE:
|
||||
state = States.OUROBOROS
|
||||
if part_type == PartTypes.TAIL:
|
||||
update_sprite()
|
||||
on_ouroboros.emit()
|
||||
|
||||
# The head can spawn a new snake
|
||||
|
@ -282,7 +359,7 @@ func spawn_new_snake() -> void:
|
|||
|
||||
# If there is no room to spawn a snake, we lose
|
||||
if spawn_positions.is_empty():
|
||||
lose_game()
|
||||
lose_game(true)
|
||||
return
|
||||
|
||||
# Pick a position
|
||||
|
@ -304,5 +381,5 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
|
||||
|
||||
# If we leave the arena
|
||||
func _on_level_area_exited(_area: Area2D) -> void:
|
||||
lose_game()
|
||||
#func _on_level_area_exited(_area: Area2D) -> void:
|
||||
#lose_game()
|
||||
|
|
After Width: | Height: | Size: 480 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://djqg2n4n81gqj"
|
||||
path="res://.godot/imported/snake_head.png-d9b790ba6d57ab17f6536c547f091446.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_head.png"
|
||||
dest_files=["res://.godot/imported/snake_head.png-d9b790ba6d57ab17f6536c547f091446.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
|
After Width: | Height: | Size: 482 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bm1cbd5uf6tlq"
|
||||
path="res://.godot/imported/snake_head_dead.png-e3c3b818ce7ed644b9a37e714866ea73.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_head_dead.png"
|
||||
dest_files=["res://.godot/imported/snake_head_dead.png-e3c3b818ce7ed644b9a37e714866ea73.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
|
After Width: | Height: | Size: 497 B |
After Width: | Height: | Size: 606 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://f31d1vxf6kdv"
|
||||
path="res://.godot/imported/snake_head_ouroboros.png-880dd7bcf916adb92d621a92cede4944.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_head_ouroboros.png"
|
||||
dest_files=["res://.godot/imported/snake_head_ouroboros.png-880dd7bcf916adb92d621a92cede4944.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
|
After Width: | Height: | Size: 485 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b5lwghfk2spid"
|
||||
path="res://.godot/imported/snake_left.png-badd0667030f5f0cf0ee9e32fe23f909.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_left.png"
|
||||
dest_files=["res://.godot/imported/snake_left.png-badd0667030f5f0cf0ee9e32fe23f909.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
|
After Width: | Height: | Size: 496 B |
After Width: | Height: | Size: 503 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bvg583s86j3ca"
|
||||
path="res://.godot/imported/snake_right.png-06790ccfafebd89ea6cb77e5510b3ad6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_right.png"
|
||||
dest_files=["res://.godot/imported/snake_right.png-06790ccfafebd89ea6cb77e5510b3ad6.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
|
After Width: | Height: | Size: 489 B |
After Width: | Height: | Size: 346 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b0can5myhke8s"
|
||||
path="res://.godot/imported/snake_straight.png-afe959d90748ce7a63c1d2cc542b0007.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_straight.png"
|
||||
dest_files=["res://.godot/imported/snake_straight.png-afe959d90748ce7a63c1d2cc542b0007.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
|
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 510 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dkbr22xkxksaf"
|
||||
path="res://.godot/imported/snake_tail.png-86597c4ec7b604cdaa9ee2880ddc84b9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/snake_tail.png"
|
||||
dest_files=["res://.godot/imported/snake_tail.png-86597c4ec7b604cdaa9ee2880ddc84b9.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
|