feat: Flying Tutorial Prompts, Tutorial State, Cam fix

A small frog fix, for jump height.
Better screen size detection for the camera
This commit is contained in:
Jakob Feldmann 2023-06-26 23:14:07 +02:00
parent 495e6ed2ed
commit 93578d4896
23 changed files with 1023 additions and 776 deletions

View File

@ -0,0 +1,45 @@
shader_type canvas_item;
uniform sampler2D noise_tex_normal;
uniform sampler2D noise_tex;
uniform float progress : hint_range(0.0, 1.0);
uniform float scale : hint_range(0.0, 100.0);
uniform float strength = 1.0;
// If your sprite doesn't have enough space and the explosion gets clipped,
// you can uncomment this and adjust the scale
void vertex() {
VERTEX *= scale;
UV *= scale;
UV -= (scale - 1.0) / 2.0;
}
void fragment() {
vec2 direction = texture(noise_tex_normal, UV).xy; // We're using normal map as direction
direction -= 0.5; // Since our normal map is a texture, it ranges from 0.0 to 1.0...
direction *= 2.0; // ...so we're going to make it range from -1.0 to 1.0.
direction = direction * strength * progress;
// UV for exploded texture
vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; // Real texture size in pixels
vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); // Pixelate UV to snap pixels
uv = uv - direction; // Distort UV
// Texture with exploded UV
vec4 tex = texture(TEXTURE, uv);
// Dissolve alpha
float dissolve = texture(noise_tex, UV).x;
dissolve = step(progress, dissolve);
tex.a *= dissolve;
// Border (in case the edge of your sprite stretches, otherwise you can remove this block)
vec2 border_uv = uv * 2.0 - 1.0;
border_uv = clamp(abs(border_uv), 0.0, 1.0);
float border = max(border_uv.x, border_uv.y);
border = ceil(1.0 - border);
tex.a *= border;
COLOR = tex;
}

View File

@ -0,0 +1,13 @@
[gd_resource type="ShaderMaterial" load_steps=3 format=2]
[ext_resource path="res://assets/effects/pixelDissolve.gdshader" type="Shader" id=1]
[ext_resource path="res://assets/effects/noise.png" type="Texture" id=2]
[resource]
resource_local_to_scene = true
shader = ExtResource( 1 )
shader_param/progress = 0.0
shader_param/scale = 1.0
shader_param/strength = 1.0
shader_param/noise_tex_normal = ExtResource( 2 )
shader_param/noise_tex = ExtResource( 2 )

View File

@ -261,6 +261,7 @@ ui_click={
2d_physics/layer_7="finnemajig" 2d_physics/layer_7="finnemajig"
2d_physics/layer_8="dropThrough" 2d_physics/layer_8="dropThrough"
2d_physics/layer_9="food" 2d_physics/layer_9="food"
2d_physics/layer_10="dynamicUI"
[physics] [physics]
@ -275,6 +276,7 @@ quality/intended_usage/framebuffer_allocation=0
quality/intended_usage/framebuffer_allocation.mobile=0 quality/intended_usage/framebuffer_allocation.mobile=0
2d/snapping/use_gpu_pixel_snap=true 2d/snapping/use_gpu_pixel_snap=true
threads/thread_model=2 threads/thread_model=2
quality/depth/hdr=false
environment/default_environment="res://default_env.tres" environment/default_environment="res://default_env.tres"
quality/2d/use_pixel_snap=true quality/2d/use_pixel_snap=true
environment/2d/use_nvidia_rect_flicker_workaround=true environment/2d/use_nvidia_rect_flicker_workaround=true

View File

@ -3201,6 +3201,7 @@ tracks/10/keys = {
[sub_resource type="Animation" id=9] [sub_resource type="Animation" id=9]
length = 0.5 length = 0.5
loop = true loop = true
step = 0.05
tracks/0/type = "value" tracks/0/type = "value"
tracks/0/path = NodePath(".:frame") tracks/0/path = NodePath(".:frame")
tracks/0/interp = 1 tracks/0/interp = 1
@ -3328,10 +3329,10 @@ tracks/10/loop_wrap = true
tracks/10/imported = false tracks/10/imported = false
tracks/10/enabled = true tracks/10/enabled = true
tracks/10/keys = { tracks/10/keys = {
"times": PoolRealArray( 0 ), "times": PoolRealArray( 0, 0.25, 0.5 ),
"transitions": PoolRealArray( 1 ), "transitions": PoolRealArray( 1, 1, 1 ),
"update": 0, "update": 0,
"values": [ Vector2( 1, 1 ) ] "values": [ Vector2( 1, 1 ), Vector2( 1, 1 ), Vector2( 1, 1 ) ]
} }
[sub_resource type="Animation" id=99] [sub_resource type="Animation" id=99]
@ -3474,6 +3475,7 @@ tracks/10/keys = {
[sub_resource type="Animation" id=73] [sub_resource type="Animation" id=73]
length = 0.15 length = 0.15
loop = true loop = true
step = 0.01
tracks/0/type = "value" tracks/0/type = "value"
tracks/0/path = NodePath(".:frame") tracks/0/path = NodePath(".:frame")
tracks/0/interp = 1 tracks/0/interp = 1
@ -3601,16 +3603,17 @@ tracks/10/loop_wrap = true
tracks/10/imported = false tracks/10/imported = false
tracks/10/enabled = true tracks/10/enabled = true
tracks/10/keys = { tracks/10/keys = {
"times": PoolRealArray( 0 ), "times": PoolRealArray( 0, 0.07 ),
"transitions": PoolRealArray( 1 ), "transitions": PoolRealArray( 1, 1 ),
"update": 0, "update": 0,
"values": [ Vector2( 1, 1 ) ] "values": [ Vector2( 1, 1 ), Vector2( 1.3, 1 ) ]
} }
[sub_resource type="Animation" id=101] [sub_resource type="Animation" id=101]
resource_name = "wallslideToJumpRight" resource_name = "wallslideToJumpRight"
length = 0.15 length = 0.15
loop = true loop = true
step = 0.01
tracks/0/type = "value" tracks/0/type = "value"
tracks/0/path = NodePath(".:frame") tracks/0/path = NodePath(".:frame")
tracks/0/interp = 1 tracks/0/interp = 1
@ -3738,10 +3741,10 @@ tracks/10/loop_wrap = true
tracks/10/imported = false tracks/10/imported = false
tracks/10/enabled = true tracks/10/enabled = true
tracks/10/keys = { tracks/10/keys = {
"times": PoolRealArray( 0 ), "times": PoolRealArray( 0, 0.08 ),
"transitions": PoolRealArray( 1 ), "transitions": PoolRealArray( 1, 1 ),
"update": 0, "update": 0,
"values": [ Vector2( -1, 1 ) ] "values": [ Vector2( -1, 1 ), Vector2( -1.3, 1 ) ]
} }
[sub_resource type="Animation" id=74] [sub_resource type="Animation" id=74]

View File

@ -17,7 +17,7 @@ var target_offset: Vector2 = Vector2(0,0)
export var camera_horizontal_shift = 60 export var camera_horizontal_shift = 60
export var offset_reset_seconds := 1 export var offset_reset_seconds := 1
export var offset_adapt_seconds := 1 export var offset_adapt_seconds := 1
export var offset_input_seconds := 0.618 export var offset_input_seconds := 0.618 * 2
onready var levelState := $"%LevelState" onready var levelState := $"%LevelState"
onready var signalManager := $"%SignalManager" onready var signalManager := $"%SignalManager"
@ -35,7 +35,8 @@ var texture = ImageTexture.new()
var prev_pos: Vector2 var prev_pos: Vector2
var camera_state := "centered" var camera_state := "centered"
var screen_rect = Vector2(ProjectSettings.get_setting("display/window/size/width") * zoom.x, ProjectSettings.get_setting("display/window/size/height") * zoom.y ) var screen_rect = Vector2()
var old_screen_rect = Vector2(ProjectSettings.get_setting("display/window/size/width") * zoom.x, ProjectSettings.get_setting("display/window/size/height") * zoom.y )
var screen_center = Vector2() var screen_center = Vector2()
var screen_bottom = Vector2() var screen_bottom = Vector2()
var screen_top = Vector2() var screen_top = Vector2()
@ -45,206 +46,215 @@ var screen_right = Vector2()
# Gets the camera limits from the tilemap of the level # Gets the camera limits from the tilemap of the level
# Requires "TileMap" to be a sibling of blobby # Requires "TileMap" to be a sibling of blobby
func _ready(): func _ready():
_set_boundaries() _set_boundaries()
self.position = blobby.global_position get_tree().get_root().connect("size_changed", self, "_set_boundaries")
image.create(128, 2, false, Image.FORMAT_RGBAH) self.position = blobby.global_position
# TODO Test Performance image.create(128, 2, false, Image.FORMAT_RGBAH)
_update_lighting_shader() # TODO Test Performance
# TODO Trigger when needed _update_lighting_shader()
signalManager.connect("terminal_activated", self, "_on_SignalManager_terminal_activated") # TODO Trigger when needed
signalManager.connect("player_died", self, "_death_cam") signalManager.connect("terminal_activated", self, "_on_SignalManager_terminal_activated")
signalManager.connect("player_died", self, "_death_cam")
func _on_SignalManager_terminal_activated(animation_number: int = 0): func _on_SignalManager_terminal_activated(animation_number: int = 0):
get_node("LightAnimationPlayer").play("Pulsing") get_node("LightAnimationPlayer").play("Pulsing")
#func _draw(): #func _draw():
# draw_line(Vector2((limit_left - position.x), screen_center.y), screen_left, Color(255, 0, 0), 1) # draw_line(Vector2((limit_left - position.x), screen_center.y), screen_left, Color(255, 0, 0), 1)
func _process(delta: float) -> void: func _physics_process(delta: float) -> void:
# update() # update()
screen_center = (get_camera_screen_center() - position) screen_center = (get_camera_screen_center() - position)
screen_bottom = screen_center + Vector2(0, screen_rect.y/2) screen_bottom = screen_center + Vector2(0, screen_rect.y/2)
screen_top = screen_center - Vector2(0, screen_rect.y/2) screen_top = screen_center - Vector2(0, screen_rect.y/2)
screen_left = screen_center - Vector2(screen_rect.x/2, 0) screen_left = screen_center - Vector2(screen_rect.x/2, 0)
screen_right = screen_center + Vector2(screen_rect.x/2, 0) screen_right = screen_center + Vector2(screen_rect.x/2, 0)
var was_adjusted := false var was_adjusted := false
if(!levelState.is_dead): if(!levelState.is_dead):
was_adjusted = _adjust_offset(delta) was_adjusted = _adjust_offset(delta)
if(anim_player.is_playing() || was_adjusted): if(anim_player.is_playing() || was_adjusted):
position = blobby.position position = blobby.position
prev_pos = position prev_pos = position
_update_lighting_shader() _update_lighting_shader()
return return
var player_vel = (blobby.position - prev_pos)/delta var player_vel = (blobby.position - prev_pos)/delta
# TODO Take average of velocity here # TODO Take average of velocity here
if(abs(player_vel.x) >= blobby.max_velocity["walk"] * 0.97 if(abs(player_vel.x) >= blobby.max_velocity["walk"] * 0.97
&& (sign(player_vel.x) == sign(target_offset.x) || target_offset.x == 0)): && (sign(player_vel.x) == sign(target_offset.x) || target_offset.x == 0)):
if(player_vel.x > 0): if(player_vel.x > 0):
right_move_time += delta right_move_time += delta
left_move_time = max(0, left_move_time - delta) left_move_time = max(0, left_move_time - delta)
slow_time = max(0, slow_time - delta) slow_time = max(0, slow_time - delta)
else: else:
left_move_time += delta left_move_time += delta
right_move_time = max(0, right_move_time - delta) right_move_time = max(0, right_move_time - delta)
slow_time = max(0, slow_time - delta) slow_time = max(0, slow_time - delta)
elif(abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9 elif(abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9
|| sign(player_vel.x) != sign(target_offset.x) || target_offset.x == 0): || sign(player_vel.x) != sign(target_offset.x) || target_offset.x == 0):
slow_time += delta slow_time += delta
left_move_time = max(0, left_move_time - delta) left_move_time = max(0, left_move_time - delta)
right_move_time = max(0, right_move_time - delta) right_move_time = max(0, right_move_time - delta)
_adapt_to_movement(player_vel) _adapt_to_movement(player_vel)
if abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9: if abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9:
_adapt_to_input(player_vel, delta) _adapt_to_input(player_vel, delta)
position = blobby.position position = blobby.position
prev_pos = position prev_pos = position
_update_lighting_shader() _update_lighting_shader()
# TODO This has to be redone when the screen is resized in any way
# Otherwise the boundaries will not be correct anymore
func _set_boundaries(): func _set_boundaries():
# This is ok, because it only happens on initialization screen_rect = get_viewport_rect().size
# But it is also quite fickle screen_rect.x *= zoom.x
var tilemap = get_node("./%TileMap") screen_rect.y *= zoom.y
# TODO: This goes wrong when overwriting old tiles with new sprites original_x_zoom = zoom.x
# New pngs -> completely new tiles and rebuild map original_y_zoom = zoom.y
var rect = tilemap.get_used_rect() # This is ok, because it only happens on initialization
var cell_size = tilemap.cell_size # But it is also quite fickle
# TODO +2 is fixed for camera issue in adjust horizontal var tilemap = get_node("./%TileMap")
limit_right = rect.end.x * cell_size.x - 8 # TODO: This goes wrong when overwriting old tiles with new sprites
limit_left = rect.position.x * cell_size.x + 8 # New pngs -> completely new tiles and rebuild map
limit_top = rect.position.y * cell_size.y + 8 var rect = tilemap.get_used_rect()
limit_bottom = rect.end.y * cell_size.y - 8 var cell_size = tilemap.cell_size
original_limit_left = limit_left # TODO is fixed for camera issue in adjust horizontal
original_limit_right = limit_right limit_right = rect.end.x * cell_size.x - 6
original_limit_top = limit_top limit_left = rect.position.x * cell_size.x + 6
original_limit_bottom = limit_bottom limit_top = rect.position.y * cell_size.y + 6
var screen_size = get_viewport_rect() limit_bottom = rect.end.y * cell_size.y - 6
var h_pixels = limit_right - limit_left original_limit_left = limit_left
var v_pixels = limit_bottom - limit_top original_limit_right = limit_right
# TODO: Fix that it can zoom both? original_limit_top = limit_top
if screen_size.end.x * original_x_zoom - h_pixels > 0: original_limit_bottom = limit_bottom
zoom.x = h_pixels / screen_size.end.x var screen_size = get_viewport_rect()
zoom.y = zoom.x var h_pixels = limit_right - limit_left
if screen_size.end.y * original_y_zoom - v_pixels > 0: var v_pixels = limit_bottom - limit_top
zoom.y = v_pixels / screen_size.end.y # TODO: Fix that it can zoom both?
zoom.x = zoom.y if screen_size.end.x * original_x_zoom - h_pixels > 0:
zoom.x = h_pixels / screen_size.end.x
zoom.y = zoom.x
if screen_size.end.y * original_y_zoom - v_pixels > 0:
zoom.y = v_pixels / screen_size.end.y
zoom.x = zoom.y
# Smoothing the camera limits in godot ruins something # Smoothing the camera limits in godot ruins something
func _adapt_to_movement(velocity: Vector2) -> void: func _adapt_to_movement(velocity: Vector2) -> void:
var offset_track var offset_track
var center = get_camera_screen_center() var center = get_camera_screen_center()
var left_edge_pos = center.x - screen_rect.x/2 + camera_horizontal_shift var left_edge_pos = center.x - screen_rect.x/2 + camera_horizontal_shift
var right_edge_pos = center.x + screen_rect.x/2 - camera_horizontal_shift var right_edge_pos = center.x + screen_rect.x/2 - camera_horizontal_shift
if(left_move_time >= offset_adapt_seconds && !anim_player.is_playing()): if(left_move_time >= offset_adapt_seconds && !anim_player.is_playing()):
left_move_time = 0 left_move_time = 0
target_offset.x = -camera_horizontal_shift target_offset.x = -camera_horizontal_shift
if(offset == target_offset): if(offset == target_offset):
return return
offset_track = shiftLeft.find_track(".:offset") offset_track = shiftLeft.find_track(".:offset")
shiftLeft.track_set_key_value(offset_track, 0, offset) shiftLeft.track_set_key_value(offset_track, 0, offset)
shiftLeft.track_set_key_value(offset_track, 1, target_offset) shiftLeft.track_set_key_value(offset_track, 1, target_offset)
camera_state = "shiftedLeft" camera_state = "shiftedLeft"
anim_player.play("shiftingLeft") anim_player.play("shiftingLeft")
elif(right_move_time >= offset_adapt_seconds && !anim_player.is_playing()): elif(right_move_time >= offset_adapt_seconds && !anim_player.is_playing()):
right_move_time = 0 right_move_time = 0
target_offset.x = camera_horizontal_shift target_offset.x = camera_horizontal_shift
if(offset == target_offset): if(offset == target_offset):
return return
offset_track = shiftRight.find_track(".:offset") offset_track = shiftRight.find_track(".:offset")
shiftRight.track_set_key_value(offset_track, 0, offset) shiftRight.track_set_key_value(offset_track, 0, offset)
shiftRight.track_set_key_value(offset_track, 1, target_offset) shiftRight.track_set_key_value(offset_track, 1, target_offset)
camera_state = "shiftedRight" camera_state = "shiftedRight"
anim_player.play("shiftingRight") anim_player.play("shiftingRight")
elif(slow_time >= offset_reset_seconds && elif(slow_time >= offset_reset_seconds &&
!(Input.is_action_pressed("up") || Input.is_action_pressed("duck"))): !(Input.is_action_pressed("up") || Input.is_action_pressed("duck"))):
slow_time = 0 slow_time = 0
target_offset.x = 0 target_offset.x = 0
if(offset == target_offset): if(offset == target_offset):
return return
if(left_edge_pos > limit_left && limit_right > right_edge_pos): if(left_edge_pos > limit_left && limit_right > right_edge_pos):
offset_track = shiftCenter.find_track(".:offset") offset_track = shiftCenter.find_track(".:offset")
shiftCenter.track_set_key_value(offset_track, 0, offset) shiftCenter.track_set_key_value(offset_track, 0, offset)
shiftCenter.track_set_key_value(offset_track, 1, target_offset) shiftCenter.track_set_key_value(offset_track, 1, target_offset)
camera_state = "centered" camera_state = "centered"
anim_player.play("shiftingCenter") anim_player.play("shiftingCenter")
return return
func _adapt_to_input(velocity: Vector2, delta: float) -> void: func _adapt_to_input(velocity: Vector2, delta: float) -> void:
# TODO Den bug dass man damit durch die map gucken kann wenn man sich weiter bewegt # TODO Den bug dass man damit durch die map gucken kann wenn man sich weiter bewegt
# lasse ich erstmal drin # lasse ich erstmal drin
if(velocity.length() > 20.0): if(velocity.length() > 20.0):
input_time = 0 input_time = 0
return return
if(input_time < offset_input_seconds): if(input_time < offset_input_seconds):
input_time += delta input_time += delta
return return
if Input.is_action_pressed("duck"): if Input.is_action_pressed("duck"):
if(original_limit_bottom - position.y - 2 > screen_bottom.y && offset.y < 48): if(original_limit_bottom - position.y - 2 > screen_bottom.y && offset.y < 48):
offset.y += 1 offset.y += 0.5
elif Input.is_action_pressed("up"): elif Input.is_action_pressed("up"):
if(original_limit_top - position.y + 2 < screen_top.y && offset.y > -48): if(original_limit_top - position.y + 2 < screen_top.y && offset.y > -48):
offset.y -= 1 offset.y -= 0.5
# TODO This is a regulatory problem, it doesn't adapt fast enough # TODO This is a regulatory problem, it doesn't adapt fast enough
# TODO Maybe just make background black and dont bother # TODO Maybe just make background black and dont bother
func _adjust_offset(delta: float) -> bool: func _adjust_offset(delta: float) -> bool:
var new_offset = offset var new_offset = offset
if (limit_left - position.x - screen_left.x > 0.1): if (limit_left - position.x - screen_left.x > 0.1):
if (anim_player.is_playing()): if (anim_player.is_playing()):
anim_player.stop(true) anim_player.stop(true)
new_offset.x += (limit_left - position.x - screen_left.x)/2 new_offset.x += (limit_left - position.x - screen_left.x)/1.5
if (limit_right - position.x - screen_right.x < 0.1): if (limit_right - position.x - screen_right.x < 0.1):
if (anim_player.is_playing()): if (anim_player.is_playing()):
anim_player.stop(true) anim_player.stop(true)
new_offset.x += (limit_right - position.x - screen_right.x)/2 new_offset.x += (limit_right - position.x - screen_right.x)/1.5
if (limit_top - position.y - screen_top.y > 0.001): if (limit_top - position.y - screen_top.y > 0.001):
new_offset.y += (limit_top - position.y - screen_top.y)/2 new_offset.y += (limit_top - position.y - screen_top.y)/1.5
if (limit_bottom - position.y - screen_bottom.y < 0.001): if (limit_bottom - position.y - screen_bottom.y < 0.001):
new_offset.y += (limit_bottom - position.y - screen_bottom.y)/2 new_offset.y += (limit_bottom - position.y - screen_bottom.y)/1.5
if(abs(offset.x) >= abs(new_offset.x) + 1): #print(abs(offset.x) - abs(new_offset.x))
offset = new_offset if(abs(offset.x) > abs(new_offset.x) || abs(offset.y) > abs(new_offset.y)):
return true offset = new_offset
else: return true
return false else:
return false
func reset_limits() -> void: func reset_limits() -> void:
limit_left = original_limit_left limit_left = original_limit_left
limit_right = original_limit_right limit_right = original_limit_right
limit_bottom = original_limit_bottom limit_bottom = original_limit_bottom
limit_top = original_limit_top limit_top = original_limit_top
func _death_cam(animation_number: int = 0) -> void: func _death_cam(animation_number: int = 0) -> void:
if(animation_number < 1): if(animation_number < 1):
$CameraAnimationPlayer.play("deathCamJustZoom") $CameraAnimationPlayer.play("deathCamJustZoom")
if(animation_number == 1): if(animation_number == 1):
$CameraAnimationPlayer.play("deathCamLateRotation") $CameraAnimationPlayer.play("deathCamLateRotation")
func _update_lighting_shader() -> void: func _update_lighting_shader() -> void:
# Props to gameendaevour # Props to gameendaevour
# TODO get this into a central world update management system # TODO get this into a central world update management system
var lights = get_tree().get_nodes_in_group("light") var lights = get_tree().get_nodes_in_group("light")
image.lock() image.lock()
for i in lights.size(): for i in lights.size():
var light = lights[i] var light = lights[i]
# TODO To make the lighting affect all layers properly # TODO To make the lighting affect all layers properly
# I would have the access the global positions of nodes in different Z layers # I would have the access the global positions of nodes in different Z layers
# without the projection to the global center layer. # without the projection to the global center layer.
# var vtrans = get_canvas_transform() # var vtrans = get_canvas_transform()
# var top_left = -vtrans.origin / vtrans.get_scale() # var top_left = -vtrans.origin / vtrans.get_scale()
# var vsize = get_viewport_rect().size # var vsize = get_viewport_rect().size
# var t = Transform2D(0, (top_left + 0.5*vsize/vtrans.get_scale()).rotated(rotation)) # var t = Transform2D(0, (top_left + 0.5*vsize/vtrans.get_scale()).rotated(rotation))
image.set_pixel(i, 0, Color( image.set_pixel(i, 0, Color(
light.position.x, light.position.y, light.position.x, light.position.y,
light.strength, light.radius light.strength, light.radius
)) ))
image.set_pixel(i, 1, light.color) image.set_pixel(i, 1, light.color)
image.unlock() image.unlock()
texture.create_from_image(image) texture.create_from_image(image)
material.set_shader_param("n_lights", lights.size()) material.set_shader_param("n_lights", lights.size())
material.set_shader_param("light_data", texture) material.set_shader_param("light_data", texture)
material.set_shader_param("global_transform", get_global_transform()) material.set_shader_param("global_transform", get_global_transform())
material.set_shader_param("viewport_transform", get_viewport_transform()) material.set_shader_param("viewport_transform", get_viewport_transform())

View File

@ -868,6 +868,7 @@ z_index = 3
rotating = true rotating = true
current = true current = true
zoom = Vector2( 0.75, 0.75 ) zoom = Vector2( 0.75, 0.75 )
process_mode = 0
drag_margin_h_enabled = true drag_margin_h_enabled = true
drag_margin_v_enabled = true drag_margin_v_enabled = true
drag_margin_left = 0.08 drag_margin_left = 0.08
@ -971,10 +972,10 @@ texture = ExtResource( 8 )
[node name="AnimatedSprite" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"] [node name="AnimatedSprite" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"]
frames = SubResource( 7 ) frames = SubResource( 7 )
frame = 3 frame = 4
playing = true playing = true
[node name="AnimatedSprite2" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"] [node name="AnimatedSprite2" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"]
frames = SubResource( 8 ) frames = SubResource( 8 )
frame = 8 frame = 9
playing = true playing = true

View File

@ -53,466 +53,466 @@ var reversing_possible_searching := true
func _ready(): func _ready():
default_jump_distance = default_jump_distance * tilemap.cell_size.x default_jump_distance = default_jump_distance * tilemap.cell_size.x
jump_timer = Timer.new() jump_timer = Timer.new()
jump_timer.set_one_shot(true) jump_timer.set_one_shot(true)
jump_timer.connect("timeout", self, "jump") jump_timer.connect("timeout", self, "jump")
target_lost_timer = Timer.new() target_lost_timer = Timer.new()
target_lost_timer.set_one_shot(true) target_lost_timer.set_one_shot(true)
target_lost_timer.connect("timeout", self, "loose_target") target_lost_timer.connect("timeout", self, "loose_target")
add_child(jump_timer) add_child(jump_timer)
add_child(target_lost_timer) add_child(target_lost_timer)
# TODO Stays harmless for now # TODO Stays harmless for now
#if(is_bound): add_to_group("harmful") #if(is_bound): add_to_group("harmful")
$LeashAnchor.visible = is_bound $LeashAnchor.visible = is_bound
func bind_to_anchor(anchor_node: Node2D, radius: float ) -> void: func bind_to_anchor(anchor_node: Node2D, radius: float ) -> void:
anchor = anchor_node anchor = anchor_node
movement_radius = radius * block_size movement_radius = radius * block_size
is_bound = true is_bound = true
$LeashAnchor.visible = true $LeashAnchor.visible = true
func _on_StompDetector_body_entered(body: Node) -> void: func _on_StompDetector_body_entered(body: Node) -> void:
if !body.is_in_group("player") || is_hurt: if !body.is_in_group("player") || is_hurt:
return return
var incoming_vel_vector: Vector2 = body.velocity.normalized() var incoming_vel_vector: Vector2 = body.velocity.normalized()
# TODO This is not the right angle somehow # TODO This is not the right angle somehow
# print(rad2deg(abs(incoming_vel_vector.angle_to(Vector2.DOWN.rotated(rotation))))) # print(rad2deg(abs(incoming_vel_vector.angle_to(Vector2.DOWN.rotated(rotation)))))
# if abs(incoming_vel_vector.angle_to(\Vector2.DOWN.rotated(rotation))) > deg2rad(60): # if abs(incoming_vel_vector.angle_to(\Vector2.DOWN.rotated(rotation))) > deg2rad(60):
# print("too shallow entry") # print("too shallow entry")
# return # return
signalManager.emit_signal("got_stomped") signalManager.emit_signal("got_stomped")
remove_from_group("harmful") remove_from_group("harmful")
# TODO Weakpoint group is not needed per se # TODO Weakpoint group is not needed per se
$StompDetector.remove_from_group("weakpoint") $StompDetector.remove_from_group("weakpoint")
get_node("EnemyBody").disabled = true get_node("EnemyBody").disabled = true
is_hurt = true is_hurt = true
$FrogSprite.material = invincible_shader $FrogSprite.material = invincible_shader
$HurtTimer.start() $HurtTimer.start()
func execute_movement(delta: float) -> void: func execute_movement(delta: float) -> void:
# Navigation2DServer.map_get_path() # Navigation2DServer.map_get_path()
current_delta = delta current_delta = delta
detect_timer += delta detect_timer += delta
velocity.y += _gravity * delta velocity.y += _gravity * delta
if(is_bound): if(is_bound):
var next_position = global_position + velocity * current_delta var next_position = global_position + velocity * current_delta
var current_distance = global_position.distance_to(anchor.global_position) var current_distance = global_position.distance_to(anchor.global_position)
var new_distance = next_position.distance_to(anchor.global_position) var new_distance = next_position.distance_to(anchor.global_position)
# TODO Fix this in respects to x and y distances and movement dampening # TODO Fix this in respects to x and y distances and movement dampening
# Maybe use mathemathematics or something idfc # Maybe use mathemathematics or something idfc
if(current_distance >= movement_radius && new_distance > current_distance): if(current_distance >= movement_radius && new_distance > current_distance):
velocity.x = velocity.x * 0.8 velocity.x = velocity.x * 0.8
velocity.y = velocity.y * 0.8 velocity.y = velocity.y * 0.8
was_restricted = true was_restricted = true
velocity = move_and_slide(velocity, FLOOR_NORMAL, false, 4, 0.785398,false) velocity = move_and_slide(velocity, FLOOR_NORMAL, false, 4, 0.785398,false)
if(is_on_floor()): if(is_on_floor()):
velocity = Vector2(0,0) velocity = Vector2(0,0)
# Reverse direction when hitting limit # Reverse direction when hitting limit
func die() -> void: func die() -> void:
levelState.kills += 1 levelState.kills += 1
queue_free() queue_free()
func _on_EnemySkin_area_entered(area:Area2D) -> void: func _on_EnemySkin_area_entered(area:Area2D) -> void:
if area.is_in_group("harmful"): if area.is_in_group("harmful"):
get_node("EnemyBody").disabled = true get_node("EnemyBody").disabled = true
die() die()
func _on_EnemySkin_body_entered(body: Node) -> void: func _on_EnemySkin_body_entered(body: Node) -> void:
if body.is_in_group("frogfood"): if body.is_in_group("frogfood"):
loose_target() loose_target()
body.die() body.die()
func searching() -> Vector2: func searching() -> Vector2:
if(detect_timer > 0.333): if(detect_timer > 0.333):
search_next_target() search_next_target()
detect_timer = 0.0 detect_timer = 0.0
if(is_on_floor()): if(is_on_floor()):
if(jump_timer.is_stopped()): if(jump_timer.is_stopped()):
jump_timer.start(rng.randfn(jump_time_search, jump_time_standard_deviation)) jump_timer.start(rng.randfn(jump_time_search, jump_time_standard_deviation))
if(in_air): if(in_air):
in_air = false in_air = false
else: else:
if(!in_air): if(!in_air):
start_x = global_position.x start_x = global_position.x
reversing_possible_searching = true reversing_possible_searching = true
jump_timer.stop() jump_timer.stop()
in_air = true in_air = true
return velocity return velocity
func search_next_target(): func search_next_target():
if(target != null && !weakref(target).get_ref()): if(target != null && !weakref(target).get_ref()):
return return
detect_food() detect_food()
if(food_target == null): if(food_target == null):
detect_player() detect_player()
func hunting() -> Vector2: func hunting() -> Vector2:
var was_target_freed = !weakref(target).get_ref() var was_target_freed = !weakref(target).get_ref()
if(detect_timer > 0.333): if(detect_timer > 0.333):
search_next_target() search_next_target()
detect_timer = 0.0 detect_timer = 0.0
#TODO Dependent on block size #TODO Dependent on block size
elif(is_on_floor() && food_target != null && !was_target_freed && elif(is_on_floor() && food_target != null && !was_target_freed &&
global_position.distance_to(food_target.global_position) <= attack_jump_range * block_size): global_position.distance_to(food_target.global_position) <= attack_jump_range * block_size):
var collider = check_feeler(food_target.global_position - global_position) var collider = check_feeler(food_target.global_position - global_position)
if(!was_restricted && collider != null && collider.is_in_group("frogfood")): if(!was_restricted && collider != null && collider.is_in_group("frogfood")):
jump_timer.stop() jump_timer.stop()
return attack_jump(food_target.global_position) return attack_jump(food_target.global_position)
if(is_on_floor()): if(is_on_floor()):
if(jump_timer.is_stopped()): if(jump_timer.is_stopped()):
jump_timer.start(rng.randfn(jump_time_hunt, jump_time_standard_deviation)) jump_timer.start(rng.randfn(jump_time_hunt, jump_time_standard_deviation))
if(in_air): if(in_air):
in_air = false in_air = false
else: else:
if(!in_air): if(!in_air):
start_x = global_position.x start_x = global_position.x
reversing_possible_searching = true reversing_possible_searching = true
jump_timer.stop() jump_timer.stop()
in_air = true in_air = true
if(barely_held_back_counter > 1): if(barely_held_back_counter > 1):
barely_held_back_counter = 0 barely_held_back_counter = 0
loose_target() loose_target()
if(target != null && !was_target_freed && if(target != null && !was_target_freed &&
sign((target.global_position - global_position).x) != get_facing_direction()): sign((target.global_position - global_position).x) != get_facing_direction()):
# TODO Waits in front of too small tunnels if it sees the target on the other side # TODO Waits in front of too small tunnels if it sees the target on the other side
# It's ok behavior for now # It's ok behavior for now
reverse_facing_direction() reverse_facing_direction()
return velocity return velocity
func detect_food() -> void: func detect_food() -> void:
# TODO What if food spawns in # TODO What if food spawns in
food_sources = get_tree().get_nodes_in_group("frogfood") food_sources = get_tree().get_nodes_in_group("frogfood")
if(food_sources.empty()): if(food_sources.empty()):
return return
var i = 0 var i = 0
var min_dist_f_index = 0 var min_dist_f_index = 0
var min_dist = (food_sources[0].global_position - global_position).length() var min_dist = (food_sources[0].global_position - global_position).length()
var food_node = null var food_node = null
for f in food_sources: for f in food_sources:
var new_dist = (food_sources[i].global_position - global_position).length() var new_dist = (food_sources[i].global_position - global_position).length()
min_dist = new_dist if new_dist < min_dist else min_dist min_dist = new_dist if new_dist < min_dist else min_dist
min_dist_f_index = i if new_dist < min_dist else min_dist_f_index min_dist_f_index = i if new_dist < min_dist else min_dist_f_index
i += 1 i += 1
food_node = food_sources[min_dist_f_index] food_node = food_sources[min_dist_f_index]
#TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0
vision_raycast.cast_to = (food_node.global_position - global_position).normalized() * block_size * vision_distance vision_raycast.cast_to = (food_node.global_position - global_position).normalized() * block_size * vision_distance
var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to)
vision_raycast.force_raycast_update() vision_raycast.force_raycast_update()
var collider = vision_raycast.get_collider() var collider = vision_raycast.get_collider()
if(abs(ray_angle_to_facing) < PI/3 && collider != null && collider.is_in_group("frogfood")): if(abs(ray_angle_to_facing) < PI/3 && collider != null && collider.is_in_group("frogfood")):
target_lost_timer.stop() target_lost_timer.stop()
target = collider target = collider
food_target = collider food_target = collider
elif(target != null && target_lost_timer.is_stopped()): elif(target != null && target_lost_timer.is_stopped()):
target_lost_timer.start(loose_target_seconds) target_lost_timer.start(loose_target_seconds)
func detect_player() -> void: func detect_player() -> void:
var player var player
if(players.empty()): if(players.empty()):
# print("no player found") # print("no player found")
return return
player = players[0] player = players[0]
#TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0
vision_raycast.cast_to = (player.global_position - global_position - Vector2(0, 9)).normalized() * block_size * vision_distance vision_raycast.cast_to = (player.global_position - global_position - Vector2(0, 9)).normalized() * block_size * vision_distance
var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to)
vision_raycast.force_raycast_update() vision_raycast.force_raycast_update()
var collider = vision_raycast.get_collider() var collider = vision_raycast.get_collider()
if(abs(ray_angle_to_facing) < PI/4 && collider != null && collider.is_in_group("player")): if(abs(ray_angle_to_facing) < PI/4 && collider != null && collider.is_in_group("player")):
target_lost_timer.stop() target_lost_timer.stop()
target = collider target = collider
elif(target != null && target_lost_timer.is_stopped()): elif(target != null && target_lost_timer.is_stopped()):
target_lost_timer.start(loose_target_seconds) target_lost_timer.start(loose_target_seconds)
func sleeping() -> Vector2: func sleeping() -> Vector2:
jump_timer.stop() jump_timer.stop()
detect_player() detect_player()
return velocity return velocity
func loose_target() -> void: func loose_target() -> void:
# print("frog target lost") # print("frog target lost")
target = null target = null
food_target = null food_target = null
func jump(): func jump():
# print("jump calculation initiated") # print("jump calculation initiated")
# Can only reverse once per jump calculation # Can only reverse once per jump calculation
has_reversed = false has_reversed = false
var zero_vector = Vector2(0,0) var zero_vector = Vector2(0,0)
var v: Vector2 = velocity_for_jump_distance(default_jump_distance, deg2rad(default_jump_angle)) var v: Vector2 = velocity_for_jump_distance(default_jump_distance, deg2rad(default_jump_angle))
v = correct_jump_direction(v) v = correct_jump_direction(v)
if(is_bound): if(is_bound):
var next_position = global_position + v * current_delta var next_position = global_position + v * current_delta
var current_distance = global_position.distance_to(anchor.global_position) var current_distance = global_position.distance_to(anchor.global_position)
var new_distance = next_position.distance_to(anchor.global_position) var new_distance = next_position.distance_to(anchor.global_position)
# print(current_distance) # print(current_distance)
# print(new_distance) # print(new_distance)
# Would go out of distance # Would go out of distance
if((new_distance >= movement_radius && new_distance > current_distance) || (new_distance > current_distance && was_restricted)): if((new_distance >= movement_radius && new_distance > current_distance) || (new_distance > current_distance && was_restricted)):
if(state_machine.state == "hunting"): if(state_machine.state == "hunting"):
barely_held_back_counter += 1 barely_held_back_counter += 1
if can_reverse_facing_direction() && (barely_held_back_counter == 0 || barely_held_back_counter > 1): if can_reverse_facing_direction() && (barely_held_back_counter == 0 || barely_held_back_counter > 1):
reverse_facing_direction() reverse_facing_direction()
was_restricted = false was_restricted = false
if ($Right_Wallcast.is_colliding() && $Left_Wallcast.is_colliding()): if ($Right_Wallcast.is_colliding() && $Left_Wallcast.is_colliding()):
# TODO No idea what it might do in these situations # TODO No idea what it might do in these situations
print("help this is a really tight space :(") print("help this is a really tight space :(")
elif (get_facing_direction() < 0 && $Left_Wallcast.is_colliding()): elif (get_facing_direction() < 0 && $Left_Wallcast.is_colliding()):
v = zero_vector v = zero_vector
elif (get_facing_direction() > 0 && $Right_Wallcast.is_colliding()): elif (get_facing_direction() > 0 && $Right_Wallcast.is_colliding()):
v = zero_vector v = zero_vector
v = correct_jump_direction(v) v = correct_jump_direction(v)
if(v != zero_vector): if(v != zero_vector):
v = consider_jump_headspace(v) v = consider_jump_headspace(v)
if(v != zero_vector): if(v != zero_vector):
v = consider_jump_landing_space(v) v = consider_jump_landing_space(v)
if(v == zero_vector): if(v == zero_vector):
# TODO fix that you could call jump from jumping on top # TODO fix that you could call jump from jumping on top
# and let it fail if the top is dangerous for jump height or not safe # and let it fail if the top is dangerous for jump height or not safe
v = consider_jumping_on_top() v = consider_jumping_on_top()
if(v == zero_vector && can_reverse_facing_direction()): if(v == zero_vector && can_reverse_facing_direction()):
reverse_facing_direction() reverse_facing_direction()
velocity = v velocity = v
func correct_jump_direction(v: Vector2) -> Vector2: func correct_jump_direction(v: Vector2) -> Vector2:
if sign(v.x) != get_facing_direction(): if sign(v.x) != get_facing_direction():
v.x *= -1 v.x *= -1
return v return v
# Cast a ray to the highest point of the jump # Cast a ray to the highest point of the jump
# Check the highest point for collision # Check the highest point for collision
# Calculate safe jump height and then a safe jump velocity # Calculate safe jump height and then a safe jump velocity
func consider_jump_headspace(v: Vector2) -> Vector2: func consider_jump_headspace(v: Vector2) -> Vector2:
var height = calculate_jump_height(v) var height = calculate_jump_height(v)
var distance = calculate_jump_distance(v) var distance = calculate_jump_distance(v)
var angle = (v * get_facing_direction()).angle() var angle = (v * get_facing_direction()).angle()
# Half distance is an estimate of the jumps apex() # Half distance is an estimate of the jumps apex()
#TODO Consider sprite size for height #TODO Consider sprite size for height
var height_collider = check_feeler(Vector2(get_facing_direction()*(distance/2), -(height+23))) var height_collider = check_feeler(Vector2(get_facing_direction()*(distance/2), -(height+23)))
if(height_collider != null): if(height_collider != null):
var collision_point = feeler_raycast.get_collision_point() var collision_point = feeler_raycast.get_collision_point()
var target_height = collision_point.y - (feeler_raycast.global_position.y - 23) var target_height = collision_point.y - (feeler_raycast.global_position.y - 23)
# print(feeler_raycast.global_position) # print(feeler_raycast.global_position)
var new_angle = angle * (0.75 if target_height > -26 else 0.95) var new_angle = angle * (0.75 if target_height > -26 else 0.95)
var new_distance = default_jump_distance * (0.66 if target_height > -26 else 0.75) var new_distance = default_jump_distance * (0.66 if target_height < -26 else 0.75)
v = velocity_for_jump_distance(new_distance, abs(new_angle)) v = velocity_for_jump_distance(new_distance, abs(new_angle))
v = correct_jump_direction(v) v = correct_jump_direction(v)
height = calculate_jump_height(v) * -1 height = calculate_jump_height(v) * -1
distance = calculate_jump_distance(v) * get_facing_direction() distance = calculate_jump_distance(v) * get_facing_direction()
if(height < target_height && can_reverse_facing_direction()): if(height < target_height && can_reverse_facing_direction()):
print("no safe height for frog jump") print("no safe height for frog jump")
return Vector2(0,0) return Vector2(0,0)
return v return v
# Check the block in jump distance for danger or height # Check the block in jump distance for danger or height
# If danger check neighboring blocks: if still danger, then jump closer (or jump over) # If danger check neighboring blocks: if still danger, then jump closer (or jump over)
# If height move to distance which allows 1 block high jump # If height move to distance which allows 1 block high jump
func consider_jump_landing_space(v: Vector2) -> Vector2: func consider_jump_landing_space(v: Vector2) -> Vector2:
var jump_distance = calculate_jump_distance(v) var jump_distance = calculate_jump_distance(v)
var jump_height = calculate_jump_height(v) var jump_height = calculate_jump_height(v)
var collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) var collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2))
# TODO Unpacked loop, make function or something? # TODO Unpacked loop, make function or something?
# Shortens the jump in steps to make it more safe # Shortens the jump in steps to make it more safe
if(!is_jump_path_safe(v, global_position) || collider != null): if(!is_jump_path_safe(v, global_position) || collider != null):
jump_distance = calculate_jump_distance(v) - block_size/1.5 jump_distance = calculate_jump_distance(v) - block_size/1.5
v = change_jump_distance(jump_distance, v) v = change_jump_distance(jump_distance, v)
jump_height = calculate_jump_height(v) jump_height = calculate_jump_height(v)
v = correct_jump_direction(v) v = correct_jump_direction(v)
collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2))
if(!is_jump_path_safe(v, global_position) || collider != null): if(!is_jump_path_safe(v, global_position) || collider != null):
jump_distance = calculate_jump_distance(v) - block_size/2.0 jump_distance = calculate_jump_distance(v) - block_size/2.0
v = change_jump_distance(jump_distance, v) v = change_jump_distance(jump_distance, v)
jump_height = calculate_jump_height(v) jump_height = calculate_jump_height(v)
v = correct_jump_direction(v) v = correct_jump_direction(v)
collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2))
if((!is_jump_path_safe(v, global_position) || collider != null) && can_reverse_facing_direction()): if((!is_jump_path_safe(v, global_position) || collider != null) && can_reverse_facing_direction()):
# Can be printed when frog would jump into a wall too # Can be printed when frog would jump into a wall too
print("no safe landing space found") print("no safe landing space found")
return Vector2(0,0) return Vector2(0,0)
return v return v
func consider_jumping_on_top() -> Vector2: func consider_jumping_on_top() -> Vector2:
var collider = check_feeler(Vector2(42 * get_facing_direction(),0)) var collider = check_feeler(Vector2(42 * get_facing_direction(),0))
var facing = 0 if get_facing_direction() >= 0 else - 1 var facing = 0 if get_facing_direction() >= 0 else - 1
if (collider == null): if (collider == null):
return Vector2(0,0) return Vector2(0,0)
var local_position = tilemap.to_local(feeler_raycast.get_collision_point()) var local_position = tilemap.to_local(feeler_raycast.get_collision_point())
var map_position = tilemap.world_to_map(local_position) var map_position = tilemap.world_to_map(local_position)
var tile_position = Vector2(map_position.x + facing, map_position.y) var tile_position = Vector2(map_position.x + facing, map_position.y)
# TODO Here the climb height of frog is limited to one constantly # TODO Here the climb height of frog is limited to one constantly
var cell_id = tilemap.get_cell(tile_position.x, tile_position.y - 2) var cell_id = tilemap.get_cell(tile_position.x, tile_position.y - 2)
if (cell_id != -1 && if (cell_id != -1 &&
#TODO 9 is the navigation tile, but thats subject to change! #TODO 9 is the navigation tile, but thats subject to change!
cell_id != 8): cell_id != 8):
return Vector2(0,0) return Vector2(0,0)
var tile_upper_left_corner = tilemap.to_global(tilemap.map_to_world(tile_position)) var tile_upper_left_corner = tilemap.to_global(tilemap.map_to_world(tile_position))
var tile_upper_right_corner = Vector2(tile_upper_left_corner.x + tilemap.cell_size.x, tile_upper_left_corner.y) var tile_upper_right_corner = Vector2(tile_upper_left_corner.x + tilemap.cell_size.x, tile_upper_left_corner.y)
var jump_angle = 0 var jump_angle = 0
if(facing < 0): if(facing < 0):
var frog_bottom_left_corner = Vector2($EnemyBody.global_position.x - $EnemyBody.shape.extents.x, var frog_bottom_left_corner = Vector2($EnemyBody.global_position.x - $EnemyBody.shape.extents.x,
$EnemyBody.global_position.y + $EnemyBody.shape.extents.y) $EnemyBody.global_position.y + $EnemyBody.shape.extents.y)
jump_angle = frog_bottom_left_corner.angle_to_point(tile_upper_right_corner) jump_angle = frog_bottom_left_corner.angle_to_point(tile_upper_right_corner)
else: else:
var frog_bottom_right_corner = Vector2($EnemyBody.global_position.x + $EnemyBody.shape.extents.x, var frog_bottom_right_corner = Vector2($EnemyBody.global_position.x + $EnemyBody.shape.extents.x,
$EnemyBody.global_position.y + $EnemyBody.shape.extents.y) $EnemyBody.global_position.y + $EnemyBody.shape.extents.y)
jump_angle = frog_bottom_right_corner.angle_to_point(tile_upper_left_corner) - PI jump_angle = frog_bottom_right_corner.angle_to_point(tile_upper_left_corner) - PI
if(abs(rad2deg(jump_angle)) < 78): if(abs(rad2deg(jump_angle)) < 78):
return correct_jump_direction(velocity_for_jump_distance(default_jump_distance/2, abs(deg2rad(80)))) return correct_jump_direction(velocity_for_jump_distance(default_jump_distance/2, abs(deg2rad(80))))
else: else:
return velocity_for_jump_distance(10, abs(deg2rad(45))) * -1 * facing return velocity_for_jump_distance(10, abs(deg2rad(45))) * -1 * facing
# Tries to shorten the jump, so that it lands in a tiles center # Tries to shorten the jump, so that it lands in a tiles center
func jump_to_tile_center(v: Vector2) -> Vector2: func jump_to_tile_center(v: Vector2) -> Vector2:
var distance = stepify(calculate_jump_distance(v), 0.01) var distance = stepify(calculate_jump_distance(v), 0.01)
if !is_equal_approx(fmod(abs(global_position.x + distance * get_facing_direction()), block_size), (block_size/2.0)): if !is_equal_approx(fmod(abs(global_position.x + distance * get_facing_direction()), block_size), (block_size/2.0)):
# print(distance) # print(distance)
# print(global_position.x + distance) # print(global_position.x + distance)
# print(fmod((global_position.x + distance), block_size)) # print(fmod((global_position.x + distance), block_size))
var new_distance = distance var new_distance = distance
if(get_facing_direction() < 0): if(get_facing_direction() < 0):
new_distance = fmod((global_position.x + distance), block_size) - (block_size/2.0) + distance new_distance = fmod((global_position.x + distance), block_size) - (block_size/2.0) + distance
else: else:
new_distance = distance + block_size/2.0 - fmod((global_position.x + distance), block_size) new_distance = distance + block_size/2.0 - fmod((global_position.x + distance), block_size)
# print("centering distance") # print("centering distance")
# print(new_distance) # print(new_distance)
v = change_jump_distance(abs(new_distance), v) v = change_jump_distance(abs(new_distance), v)
v = correct_jump_direction(v) v = correct_jump_direction(v)
return v return v
# TODO Depends on Frog Shape and Tile Shape # TODO Depends on Frog Shape and Tile Shape
func is_jump_path_safe(v: Vector2, pos: Vector2) -> bool: func is_jump_path_safe(v: Vector2, pos: Vector2) -> bool:
var v0 = v.length() var v0 = v.length()
var angle = v.angle() var angle = v.angle()
var jump_distance = calculate_jump_distance(v) var jump_distance = calculate_jump_distance(v)
var harmful_nodes = get_tree().get_nodes_in_group("harmful") var harmful_nodes = get_tree().get_nodes_in_group("harmful")
harmful_nodes.append_array(get_tree().get_nodes_in_group("pit")) harmful_nodes.append_array(get_tree().get_nodes_in_group("pit"))
for node in harmful_nodes: for node in harmful_nodes:
var node_pos = node.global_position var node_pos = node.global_position
# TODO Ignores spikes more than 4 blocks below and 3 jumps away # TODO Ignores spikes more than 4 blocks below and 3 jumps away
# Also when its too near to one # Also when its too near to one
if (abs(node_pos.x - pos.x) > abs(jump_distance) * 3 if (abs(node_pos.x - pos.x) > abs(jump_distance) * 3
||abs(node_pos.y - pos.y) > block_size * 4 ||abs(node_pos.y - pos.y) > block_size * 4
|| abs(node_pos.x - pos.x) < 1): || abs(node_pos.x - pos.x) < 1):
continue continue
var node_y = node_pos.y - block_size/2.0 var node_y = node_pos.y - block_size/2.0
var initial_throw_height = node_y - (global_position.y + 9) var initial_throw_height = node_y - (global_position.y + 9)
var term1 = (pow(v0, 2) * sin(2 * angle)) / (2 * _gravity) var term1 = (pow(v0, 2) * sin(2 * angle)) / (2 * _gravity)
var term2 = ((v0 * cos(angle))/_gravity) * sqrt(pow(v0, 2) * pow(sin(angle), 2) + 2 * _gravity * initial_throw_height) var term2 = ((v0 * cos(angle))/_gravity) * sqrt(pow(v0, 2) * pow(sin(angle), 2) + 2 * _gravity * initial_throw_height)
var distance = abs(term1) + abs(term2) var distance = abs(term1) + abs(term2)
# print("distance to next spike") # print("distance to next spike")
# print(pos.x + sign(v.x) * distance - node_pos.x) # print(pos.x + sign(v.x) * distance - node_pos.x)
var safe_distance = block_size/2.0 var safe_distance = block_size/2.0
if (sign(initial_throw_height) < 0): if (sign(initial_throw_height) < 0):
safe_distance = block_size safe_distance = block_size
if(abs(pos.x + sign(v.x) * distance - node_pos.x) < safe_distance): if(abs(pos.x + sign(v.x) * distance - node_pos.x) < safe_distance):
return false return false
return true return true
func calculate_jump_height(v: Vector2) -> float: func calculate_jump_height(v: Vector2) -> float:
return abs((pow(v.length(), 2) * pow(sin(v.angle()), 2))/(2*_gravity)) return abs((pow(v.length(), 2) * pow(sin(v.angle()), 2))/(2*_gravity))
# Only works for jumps on straight ground # Only works for jumps on straight ground
func calculate_jump_distance(v: Vector2) -> float: func calculate_jump_distance(v: Vector2) -> float:
return abs((pow(v.length(), 2) * sin(-1 * 2 * v.angle()))/(_gravity)) return abs((pow(v.length(), 2) * sin(-1 * 2 * v.angle()))/(_gravity))
func jump_height_to_velocity(target_height: float, v: Vector2) -> Vector2: func jump_height_to_velocity(target_height: float, v: Vector2) -> Vector2:
var initial_height = calculate_jump_height(v) var initial_height = calculate_jump_height(v)
return v.normalized() * sqrt(pow(v.length(),2)/(initial_height/target_height)) return v.normalized() * sqrt(pow(v.length(),2)/(initial_height/target_height))
# Changes a Vector for a jump to the targeted distance, keeping the angle # Changes a Vector for a jump to the targeted distance, keeping the angle
func change_jump_distance(target_distance: float, v: Vector2) -> Vector2: func change_jump_distance(target_distance: float, v: Vector2) -> Vector2:
var initial_distance = calculate_jump_distance(v) var initial_distance = calculate_jump_distance(v)
return v.normalized() * sqrt(pow(v.length(),2)/(initial_distance/target_distance)) return v.normalized() * sqrt(pow(v.length(),2)/(initial_distance/target_distance))
# Takes an angle and a distance to calculate a jump launching at that angle and covering the distance # Takes an angle and a distance to calculate a jump launching at that angle and covering the distance
func velocity_for_jump_distance(distance: float = default_jump_distance*block_size, angle: float = deg2rad(default_jump_angle)) -> Vector2: func velocity_for_jump_distance(distance: float = default_jump_distance*block_size, angle: float = deg2rad(default_jump_angle)) -> Vector2:
var abs_velocity = sqrt((distance * _gravity)/sin(2*angle)) var abs_velocity = sqrt((distance * _gravity)/sin(2*angle))
return Vector2(abs_velocity,0).rotated(-1*angle) return Vector2(abs_velocity,0).rotated(-1*angle)
func can_reverse_facing_direction() -> bool: func can_reverse_facing_direction() -> bool:
if(is_on_floor() && !has_reversed): if(is_on_floor() && !has_reversed):
return true return true
return false return false
# Returns a jump velocity that has the target_position in it's path # Returns a jump velocity that has the target_position in it's path
func attack_jump(target_position: Vector2) -> Vector2: func attack_jump(target_position: Vector2) -> Vector2:
var target_vector = target_position - global_position var target_vector = target_position - global_position
target_vector = Vector2(abs(target_vector.x), target_vector.y) target_vector = Vector2(abs(target_vector.x), target_vector.y)
var jump_angle = target_vector.angle() var jump_angle = target_vector.angle()
var v = Vector2() var v = Vector2()
# TODO Tunable parameters # TODO Tunable parameters
if jump_angle < deg2rad(-30): if jump_angle < deg2rad(-30):
v = velocity_for_jump_distance(target_vector.x, deg2rad(default_jump_angle)) v = velocity_for_jump_distance(target_vector.x, deg2rad(default_jump_angle))
v = jump_height_to_velocity(abs(target_vector.y), v) v = jump_height_to_velocity(abs(target_vector.y), v)
else: else:
v = velocity_for_jump_distance(target_vector.x * 1.5,deg2rad(45)) v = velocity_for_jump_distance(target_vector.x * 1.5,deg2rad(45))
v = correct_jump_direction(v) v = correct_jump_direction(v)
return v return v
# Checks the feeler ray for collisions and returns collider or null # Checks the feeler ray for collisions and returns collider or null
func check_feeler(v: Vector2, _offset = Vector2(0,0)) -> Object: func check_feeler(v: Vector2, _offset = Vector2(0,0)) -> Object:
var prev_position = feeler_raycast.position var prev_position = feeler_raycast.position
feeler_raycast.position += _offset feeler_raycast.position += _offset
feeler_raycast.cast_to = v feeler_raycast.cast_to = v
feeler_raycast.force_raycast_update() feeler_raycast.force_raycast_update()
var collider = feeler_raycast.get_collider() var collider = feeler_raycast.get_collider()
feeler_raycast.position = prev_position feeler_raycast.position = prev_position
return collider return collider
func reverse_facing_direction() -> void: func reverse_facing_direction() -> void:
has_reversed = true has_reversed = true
# print("reversing direction") # print("reversing direction")
orientation.cast_to.x *= -1 orientation.cast_to.x *= -1
func get_facing_direction() -> float: func get_facing_direction() -> float:
return orientation.cast_to.x return orientation.cast_to.x
func _on_HurtTimer_timeout() -> void: func _on_HurtTimer_timeout() -> void:
is_hurt = false is_hurt = false
#if(is_bound): add_to_group("harmful") #if(is_bound): add_to_group("harmful")
$FrogSprite.material = null $FrogSprite.material = null

View File

@ -7,42 +7,45 @@ extends Node
export(Resource) var gsr export(Resource) var gsr
func set_progress(value) -> void: func set_progress(value) -> void:
gsr.progress_dict = value gsr.progress_dict = value
SaveManager.save_default() SaveManager.save_default()
func get_progress() -> Dictionary: func get_progress() -> Dictionary:
return gsr.progress_dict return gsr.progress_dict
func save() -> void:
SaveManager.save_default()
func set_savepoint(levelName: String, position: Vector2) -> void: func set_savepoint(levelName: String, position: Vector2) -> void:
if(!gsr.progress_dict.has(levelName)): if(!gsr.progress_dict.has(levelName)):
gsr.progress_dict[levelName] = {} gsr.progress_dict[levelName] = {}
gsr.progress_dict[levelName]["savepoint"] = position gsr.progress_dict[levelName]["savepoint"] = position
SaveManager.save_default() SaveManager.save_default()
func get_property_value(levelName: String, propertyName: String) -> int: func get_property_value(levelName: String, propertyName: String) -> int:
if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has(propertyName): if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has(propertyName):
return gsr.progress_dict[levelName][propertyName] return gsr.progress_dict[levelName][propertyName]
else: else:
return 0 return 0
func get_savepoint(levelName: String) -> Vector2: func get_savepoint(levelName: String) -> Vector2:
if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("savepoint"): if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("savepoint"):
return gsr.progress_dict[levelName]["savepoint"] return gsr.progress_dict[levelName]["savepoint"]
else: else:
return Vector2() return Vector2()
# TODO This is permanent immediatly # TODO This is permanent immediatly
func set_wallet(value) -> void: func set_wallet(value) -> void:
gsr.wallet = value gsr.wallet = value
SaveManager.save_default() SaveManager.save_default()
func reinstate() -> void: func reinstate() -> void:
if gsr.input_map.size() <= 1: if gsr.input_map.size() <= 1:
InputMap.load_from_globals() InputMap.load_from_globals()
return return
for action in gsr.input_map: for action in gsr.input_map:
InputMap.action_erase_events(action) InputMap.action_erase_events(action)
# if(gsr.input_map[action].size() > 0 && gsr.input_map[action].size() < 2): # if(gsr.input_map[action].size() > 0 && gsr.input_map[action].size() < 2):
# InputMap.load_from_globals() # InputMap.load_from_globals()
for input_event in gsr.input_map[action]: for input_event in gsr.input_map[action]:
InputMap.action_add_event(action, input_event) InputMap.action_add_event(action, input_event)

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=17 format=2] [gd_scene load_steps=15 format=2]
[ext_resource path="res://src/Utilities/LevelState.tscn" type="PackedScene" id=1] [ext_resource path="res://src/Utilities/LevelState.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UserInterface/UserInterface.tscn" type="PackedScene" id=2] [ext_resource path="res://src/UserInterface/UserInterface.tscn" type="PackedScene" id=2]
@ -10,10 +10,8 @@
[ext_resource path="res://src/Contraptions/Portal/Portal.tscn" type="PackedScene" id=8] [ext_resource path="res://src/Contraptions/Portal/Portal.tscn" type="PackedScene" id=8]
[ext_resource path="res://src/Actors/Blobby/Blobby.tscn" type="PackedScene" id=9] [ext_resource path="res://src/Actors/Blobby/Blobby.tscn" type="PackedScene" id=9]
[ext_resource path="res://src/Levels/Level 1.gd" type="Script" id=10] [ext_resource path="res://src/Levels/Level 1.gd" type="Script" id=10]
[ext_resource path="res://addons/controller_icons/objects/Button.gd" type="Script" id=11]
[ext_resource path="res://src/Levels/TutorialThingy.gd" type="Script" id=12]
[sub_resource type="AnimationNodeStateMachinePlayback" id=4] [sub_resource type="AnimationNodeStateMachinePlayback" id=14]
[sub_resource type="ConvexPolygonShape2D" id=12] [sub_resource type="ConvexPolygonShape2D" id=12]
points = PoolVector2Array( 0, 0, 16, 0, 16, 16, 0, 16 ) points = PoolVector2Array( 0, 0, 16, 0, 16, 16, 0, 16 )
@ -101,18 +99,6 @@ unique_name_in_owner = true
[node name="Timer" parent="UserInterface/HUD/HUDOverlay/GetBackTimer" index="0"] [node name="Timer" parent="UserInterface/HUD/HUDOverlay/GetBackTimer" index="0"]
wait_time = 20.0 wait_time = 20.0
[node name="TutorialThingy" type="Sprite" parent="."]
z_index = 3
script = ExtResource( 12 )
[node name="ControllerButton" type="Button" parent="TutorialThingy"]
visible = false
margin_left = -10.0
margin_top = -10.0
margin_right = 10.0
margin_bottom = 10.0
script = ExtResource( 11 )
[node name="BlobbyCam" parent="." instance=ExtResource( 3 )] [node name="BlobbyCam" parent="." instance=ExtResource( 3 )]
unique_name_in_owner = true unique_name_in_owner = true
drag_margin_bottom = 0.3 drag_margin_bottom = 0.3
@ -121,16 +107,16 @@ drag_margin_bottom = 0.3
visible = true visible = true
[node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"]
frame = 11 frame = 3
[node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"]
frame = 4 frame = 6
[node name="Blobby" parent="." instance=ExtResource( 9 )] [node name="Blobby" parent="." instance=ExtResource( 9 )]
unique_name_in_owner = true unique_name_in_owner = true
[node name="AnimationTree" parent="Blobby/BlobbySprite" index="0"] [node name="AnimationTree" parent="Blobby/BlobbySprite" index="0"]
parameters/playback = SubResource( 4 ) parameters/playback = SubResource( 14 )
[node name="TileMap" type="TileMap" parent="."] [node name="TileMap" type="TileMap" parent="."]
unique_name_in_owner = true unique_name_in_owner = true

View File

@ -44,10 +44,10 @@ unique_name_in_owner = true
drag_margin_bottom = 0.3 drag_margin_bottom = 0.3
[node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"]
frame = 7 frame = 5
[node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"]
frame = 12 frame = 10
[node name="Blobby" parent="." instance=ExtResource( 9 )] [node name="Blobby" parent="." instance=ExtResource( 9 )]
unique_name_in_owner = true unique_name_in_owner = true
@ -228,19 +228,19 @@ position = Vector2( 756, -15 )
z_index = -1 z_index = -1
[node name="AnimatedSprite" parent="Coin" index="2"] [node name="AnimatedSprite" parent="Coin" index="2"]
frame = 301 frame = 5
[node name="Coin2" parent="." instance=ExtResource( 11 )] [node name="Coin2" parent="." instance=ExtResource( 11 )]
position = Vector2( 744, -120 ) position = Vector2( 744, -120 )
[node name="AnimatedSprite" parent="Coin2" index="2"] [node name="AnimatedSprite" parent="Coin2" index="2"]
frame = 232 frame = 250
[node name="Coin3" parent="." instance=ExtResource( 11 )] [node name="Coin3" parent="." instance=ExtResource( 11 )]
position = Vector2( 1320, -282 ) position = Vector2( 1320, -282 )
[node name="AnimatedSprite" parent="Coin3" index="2"] [node name="AnimatedSprite" parent="Coin3" index="2"]
frame = 232 frame = 250
[node name="Coin4" parent="." instance=ExtResource( 11 )] [node name="Coin4" parent="." instance=ExtResource( 11 )]
position = Vector2( 2340, -156 ) position = Vector2( 2340, -156 )

View File

@ -47,10 +47,10 @@ unique_name_in_owner = true
drag_margin_bottom = 0.3 drag_margin_bottom = 0.3
[node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"]
frame = 4 frame = 5
[node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"]
frame = 3 frame = 7
[node name="Blobby" parent="." instance=ExtResource( 5 )] [node name="Blobby" parent="." instance=ExtResource( 5 )]
unique_name_in_owner = true unique_name_in_owner = true
@ -67,6 +67,9 @@ position = Vector2( 264, -36 )
position = Vector2( 837, 72 ) position = Vector2( 837, 72 )
aggressive = false aggressive = false
[node name="FlyerSprite" parent="Flyer" index="2"]
frame = 0
[node name="AnimationTree" parent="Flyer/FlyerSprite" index="1"] [node name="AnimationTree" parent="Flyer/FlyerSprite" index="1"]
parameters/playback = SubResource( 5 ) parameters/playback = SubResource( 5 )

View File

@ -1 +1,34 @@
extends LevelTemplate extends LevelTemplate
func _ready() -> void:
var t = Timer.new()
t.set_wait_time(3)
t.set_one_shot(true)
self.add_child(t)
t.start()
yield(t, "timeout")
levelState.register_tutorial("move_right")
spawn_tutorial_thingy("move_right")
t.start()
yield(t, "timeout")
levelState.register_tutorial("move_left")
spawn_tutorial_thingy("move_left")
t.start()
yield(t, "timeout")
levelState.register_tutorial("jump")
spawn_tutorial_thingy("jump")
t.queue_free()
func spawn_tutorial_thingy(action: String) -> void:
var show_prompt: bool = GlobalState.gsr["tutorial_prompts"][action]
var thingy = ResourceLoader.load("res://src/UserInterface/TutorialThingy.tscn")
var instance = thingy.instance()
instance.action = action
var center = get_tree().root.get_child(3).get_node("%BlobbyCam").get_camera_screen_center()
var viewport = get_viewport_rect().size - Vector2(40,40)
instance.position = Vector2(rand_range(center.x - viewport.x/2, center.x + viewport.x/2), rand_range(center.y - viewport.y/2, center.y + viewport.y/2))
instance.velocity *= Vector2(sign(rand_range(-1,1)),sign(rand_range(-1,1)))
add_child(instance)

View File

@ -35,9 +35,6 @@ wait_time = 20.0
unique_name_in_owner = true unique_name_in_owner = true
drag_margin_bottom = 0.3 drag_margin_bottom = 0.3
[node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"]
frame = 4
[node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"]
frame = 3 frame = 3

View File

@ -5,6 +5,8 @@ onready var signalManager := $"%SignalManager"
onready var levelState := $"%LevelState" onready var levelState := $"%LevelState"
func _ready() -> void: func _ready() -> void:
signalManager.emit_signal("level_loaded") # should spawn the tutorial thingies which are still remembered in the progress dictionary
get_tree().paused = false signalManager.emit_signal("level_loaded")
get_tree().paused = false

View File

@ -1,38 +0,0 @@
extends Sprite
var screen_size: Vector2
var velocity = Vector2(1,1)
onready var blobby = $"%Blobby"
onready var cam = $"%BlobbyCam"
func _ready() -> void:
$ControllerButton.path = "move_right"
screen_size = cam.screen_rect
func _process(delta: float) -> void:
# TODO process less in each frame
self.texture = $ControllerButton.icon
self.scale.x = 0.5
self.scale.y = 0.5
var tex_size = Vector2(self.texture.get_width(), self.texture.get_height()/4)
var up_left_pos = cam.get_global_transform().affine_inverse() * (position - tex_size/2)
var down_right_pos = cam.get_global_transform().affine_inverse() * (position + tex_size/2)
var screen_stretch_factor = get_tree().root.size / screen_size
var blobby_screen_pos = (blobby.get_viewport_transform() * (blobby.position))/screen_stretch_factor
var corrected_screen = screen_size - blobby_screen_pos
print(get_tree().root.get_visible_rect().position)
if up_left_pos.x + blobby_screen_pos.x <= 0:
velocity.x = 1
elif down_right_pos.x >= corrected_screen.x:
velocity.x = -1
if up_left_pos.y + blobby_screen_pos.y <= 0:
velocity.y = 1
elif down_right_pos.y >= corrected_screen.y:
velocity.y = -1
self.position += velocity

View File

@ -8,34 +8,34 @@ onready var pause_title: Label = get_node("PauseOverlay/Title")
var paused := false setget set_paused var paused := false setget set_paused
func _ready(): func _ready():
#signalManager.connect("player_died", self, "_on_GlobalState_player_died") #signalManager.connect("player_died", self, "_on_GlobalState_player_died")
$ControlsMenu.visible = false $ControlsMenu.visible = false
pass pass
func _on_GlobalState_player_died() -> void: func _on_GlobalState_player_died() -> void:
self.paused = true self.paused = true
pause_title.text = "You lost" pause_title.text = "You lost"
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
# TODO don't match for specific text... why did i even consider that... did I pull that from the tutorial??? # TODO don't match for specific text... why did i even consider that... did I pull that from the tutorial???
if event.is_action_pressed("pause") && pause_title.text != "You lost" && !$ControlsMenu.visible: if event.is_action_pressed("pause") && pause_title.text != "You lost" && !$ControlsMenu.visible:
#not oder ! schaltet einen boolean um #not oder ! schaltet einen boolean um
#Ist self hier notwendig? #Ist self hier notwendig?
self.paused = not paused self.paused = not paused
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
func set_paused(value: bool) -> void: func set_paused(value: bool) -> void:
paused = value paused = value
get_tree().paused = value get_tree().paused = value
pause_overlay.visible = value pause_overlay.visible = value
if value == true: if value == true:
$PauseOverlay/VBoxContainer/Continue.grab_focus() $PauseOverlay/VBoxContainer/Continue.grab_focus()
func _on_Controls_button_up() -> void: func _on_Controls_button_up() -> void:
$ControlsMenu.visible = true $ControlsMenu.visible = true
$PauseOverlay.visible = false $PauseOverlay.visible = false
$ControlsMenu/ProfilesMenu.grab_focus() $ControlsMenu/ProfilesMenu.grab_focus()

View File

@ -5,36 +5,36 @@ onready var changes_made := false
onready var changes_saved := false onready var changes_saved := false
func _ready(): func _ready():
$InputMapper.connect('profile_changed', self, 'rebuild') $InputMapper.connect('profile_changed', self, 'rebuild')
$InputMapper.initialize_profiles() $InputMapper.initialize_profiles()
$ProfilesMenu.initialize($InputMapper) $ProfilesMenu.initialize($InputMapper)
$ProfilesMenu.grab_focus() $ProfilesMenu.grab_focus()
$InputMapper.change_profile($ProfilesMenu.selected) $InputMapper.change_profile($ProfilesMenu.selected)
func rebuild(input_profile): func rebuild(input_profile):
_action_list.clear() _action_list.clear()
var first = true var first = true
for input_action in input_profile.keys(): for input_action in input_profile.keys():
if(input_action.ends_with("_old") || input_action.begins_with(("ui_"))): if(input_action.ends_with("_old") || input_action.begins_with(("ui_"))):
continue continue
var line = _action_list.add_input_line(input_action, input_profile[input_action]) var line = _action_list.add_input_line(input_action, input_profile[input_action])
if first: if first:
$ProfilesMenu.focus_neighbour_bottom = line.get_child(2).get_path() $ProfilesMenu.focus_neighbour_bottom = line.get_child(2).get_path()
$Back.focus_neighbour_top = line.get_child(2).get_path() $Back.focus_neighbour_top = line.get_child(2).get_path()
$Save.focus_neighbour_top = line.get_child(2).get_path() $Save.focus_neighbour_top = line.get_child(2).get_path()
$Reset.focus_neighbour_top = line.get_child(2).get_path() $Reset.focus_neighbour_top = line.get_child(2).get_path()
first = false first = false
line.get_child(2).focus_neighbour_left = $Back.get_path() line.get_child(2).focus_neighbour_left = $Back.get_path()
line.get_child(2).focus_neighbour_right = $Save.get_path() line.get_child(2).focus_neighbour_right = $Save.get_path()
line.connect('change_button_pressed', self, '_on_InputLine_change_button_pressed', [input_action, line]) line.connect('change_button_pressed', self, '_on_InputLine_change_button_pressed', [input_action, line])
func _on_InputLine_change_button_pressed(action_name, line): func _on_InputLine_change_button_pressed(action_name, line):
var old_event = $InputMapper.get_selected_profile()[action_name] var old_event = $InputMapper.get_selected_profile()[action_name]
$"%KeySelectMenu".open() $"%KeySelectMenu".open()
var event = yield($"%KeySelectMenu", "key_selected") var event = yield($"%KeySelectMenu", "key_selected")
if event == null: if event == null:
return return
if($InputMapper.change_action_key(action_name, event, old_event)): if($InputMapper.change_action_key(action_name, event, old_event)):
changes_made = true changes_made = true
changes_saved = false changes_saved = false
line.update_key(event) line.update_key(event)

View File

@ -183,8 +183,6 @@ follow_focus = true
[node name="ActionKeyList" type="VBoxContainer" parent="KeymapViewer/ScrollContainer"] [node name="ActionKeyList" type="VBoxContainer" parent="KeymapViewer/ScrollContainer"]
unique_name_in_owner = true unique_name_in_owner = true
margin_right = 552.0
margin_bottom = 177.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
script = ExtResource( 14 ) script = ExtResource( 14 )

View File

@ -0,0 +1,63 @@
extends Node2D
export var action = "move_right"
export var velocity = Vector2(0.309,0.309)
export var press_limit = 3
onready var levelState := get_tree().root.get_child(3).get_node("%LevelState")
onready var cam = null
onready var button = $Button
var screen_size: Vector2
var tex_size: Vector2
var screen_stretch_factor: Vector2
func _unhandled_input(event: InputEvent) -> void:
if(ControllerIcons.parse_event(event) == button.texture):
press_limit -= 1
if press_limit < -1:
# Should delete itself from the progress dictionary when destroyed
$AnimationPlayer.play("cease_4_exist")
func _ready() -> void:
# Should save itself into the progress dictionary upon creation
$Button/ControllerButton.path = action
button.texture = $Button/ControllerButton.icon
button.scale.x = 0.4
button.scale.y = 0.4
tex_size = Vector2(button.texture.get_width()/2, button.texture.get_height()/2) * button.scale * 0.75
$Area/CollisionShape2D.shape.extents = tex_size
func _physics_process(delta: float) -> void:
if(cam == null):
cam = get_tree().root.get_child(3).get_node("%BlobbyCam")
return
button.texture = $Button/ControllerButton.icon
# TODO process less in each frame
var up_left_pos = cam.get_global_transform().affine_inverse() * (position - tex_size * self.scale)
var down_right_pos = cam.get_global_transform().affine_inverse() * (position + tex_size * self.scale)
if up_left_pos.x <= cam.screen_left.x:
velocity.x = abs(velocity.x)
elif down_right_pos.x >= cam.screen_right.x:
velocity.x = -abs(velocity.x)
if up_left_pos.y <= cam.screen_top.y:
velocity.y = abs(velocity.y)
elif down_right_pos.y >= cam.screen_bottom.y:
velocity.y = -abs(velocity.y)
self.position += velocity
func _on_Area_area_entered(area: Area2D) -> void:
var d = area.global_position - position
print(d)
if(abs(d.y) > tex_size.y * 2 - 1):
velocity.y *= -1
pass
else:
velocity.x *=-1
func _lesson_learned() -> void:
levelState.absolved_tutorial(action)
queue_free()

View File

@ -0,0 +1,97 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://assets/effects/pixelDissolve.tres" type="Material" id=1]
[ext_resource path="res://src/UserInterface/TutorialThingy.gd" type="Script" id=2]
[ext_resource path="res://addons/controller_icons/objects/Button.gd" type="Script" id=3]
[ext_resource path="res://assets/environment/decor/screen/Screen3.png" type="Texture" id=4]
[sub_resource type="RectangleShape2D" id=3]
[sub_resource type="Animation" id=4]
length = 0.001
tracks/0/type = "value"
tracks/0/path = NodePath("Button:material:shader_param/progress")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ 0.0 ]
}
[sub_resource type="Animation" id=5]
resource_name = "cease_4_exist"
length = 0.618
tracks/0/type = "value"
tracks/0/path = NodePath("Button:material:shader_param/progress")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.3 ),
"transitions": PoolRealArray( 1, 1 ),
"update": 0,
"values": [ 0.0, 1.0 ]
}
tracks/1/type = "method"
tracks/1/path = NodePath(".")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = {
"times": PoolRealArray( 0.4 ),
"transitions": PoolRealArray( 1 ),
"values": [ {
"args": [ ],
"method": "_lesson_learned"
} ]
}
[node name="TutorialThingy" type="Node2D"]
script = ExtResource( 2 )
[node name="Button" type="Sprite" parent="."]
material = ExtResource( 1 )
z_index = 3
[node name="ControllerButton" type="Button" parent="Button"]
visible = false
margin_right = 12.0
margin_bottom = 20.0
script = ExtResource( 3 )
[node name="Label" type="Label" parent="."]
visible = false
margin_left = -34.0
margin_top = -49.0
margin_right = 99.0
margin_bottom = 50.0
text = "Move Right:
"
uppercase = true
[node name="Area" type="Area2D" parent="."]
collision_layer = 512
collision_mask = 512
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area"]
shape = SubResource( 3 )
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
anims/RESET = SubResource( 4 )
anims/cease_4_exist = SubResource( 5 )
[node name="Sprite" type="Sprite" parent="."]
visible = false
texture = ExtResource( 4 )
[connection signal="area_entered" from="Area" to="." method="_on_Area_area_entered"]

View File

@ -3,5 +3,5 @@ class_name GlobalStateResource
export(Dictionary) var progress_dict := {} export(Dictionary) var progress_dict := {}
export(int) var wallet := 0 export(int) var wallet := 0
export(Dictionary) var tutorial_prompts := {}
export(Dictionary) var input_map export(Dictionary) var input_map
export(bool) var show_tutorial

View File

@ -12,81 +12,92 @@ var frees: = 0 setget set_frees
var is_dead: = false setget set_dead var is_dead: = false setget set_dead
func _ready() -> void: func _ready() -> void:
signalManager.connect("level_completed", self, "_on_level_completed") signalManager.connect("level_completed", self, "_on_level_completed")
signalManager.connect("player_died", self, "player_dying") signalManager.connect("player_died", self, "player_dying")
func reset() -> void: func reset() -> void:
deaths = 0 deaths = 0
kills = 0 kills = 0
currency = 0 currency = 0
frees = 0 frees = 0
# TODO Maybe not the place for this? # TODO Maybe not the place for this?
if GlobalState.gsr.progress_dict.has(levelName): if GlobalState.gsr.progress_dict.has(levelName):
GlobalState.gsr.progress_dict[levelName].erase("savepoint") GlobalState.gsr.progress_dict[levelName].erase("savepoint")
func set_currency(value: int) -> void: func set_currency(value: int) -> void:
currency = value currency = value
signalManager.emit_signal("currency_updated") signalManager.emit_signal("currency_updated")
func set_deaths(value: int) -> void: func set_deaths(value: int) -> void:
deaths = value deaths = value
func set_kills(value: int) -> void: func set_kills(value: int) -> void:
kills = value kills = value
signalManager.emit_signal("kills_updated") signalManager.emit_signal("kills_updated")
func set_frees(value: int) -> void: func set_frees(value: int) -> void:
frees = value frees = value
signalManager.emit_signal("frees_updated") signalManager.emit_signal("frees_updated")
func set_dead(value: bool) -> void: func set_dead(value: bool) -> void:
is_dead = value is_dead = value
func register_tutorial(action_path: String) -> void:
if(GlobalState.gsr.tutorial_prompts.has(action_path)):
return
GlobalState.gsr.tutorial_prompts[action_path] = true
GlobalState.save()
func absolved_tutorial(action_path: String) -> void:
if(!GlobalState.gsr.tutorial_prompts.has(action_path)):
return
GlobalState.gsr.tutorial_prompts[action_path] = false
GlobalState.save()
# Spends the currency when enough is available # Spends the currency when enough is available
# and returns true if so. Else it does not spend and return false. # and returns true if so. Else it does not spend and return false.
func spend_currency(cost: int) -> bool: func spend_currency(cost: int) -> bool:
if GlobalState.gsr.wallet + currency < cost: if GlobalState.gsr.wallet + currency < cost:
return false return false
var remainder = currency - cost var remainder = currency - cost
if remainder >= 0: if remainder >= 0:
currency = remainder currency = remainder
else: else:
currency = 0 currency = 0
GlobalState.set_wallet(GlobalState.gsr.wallet + remainder) GlobalState.set_wallet(GlobalState.gsr.wallet + remainder)
return true return true
func _on_level_completed(): func _on_level_completed():
update_global_state() update_global_state()
reset() reset()
func update_global_state() -> void: func update_global_state() -> void:
var progress_dict : Dictionary = GlobalState.get_progress() var progress_dict : Dictionary = GlobalState.get_progress()
var levelProgress : Dictionary = {} var levelProgress : Dictionary = {}
levelProgress["currency"] = currency levelProgress["currency"] = currency
levelProgress["kills"] = kills levelProgress["kills"] = kills
levelProgress["deaths"] = deaths levelProgress["deaths"] = deaths
levelProgress["frees"] = frees levelProgress["frees"] = frees
# TODO Doesnt account for multiple plays of same level # TODO Doesnt account for multiple plays of same level
if !progress_dict.has(levelName): if !progress_dict.has(levelName):
progress_dict[levelName] = levelProgress progress_dict[levelName] = levelProgress
else: else:
progress_dict[levelName]["currency"] = GlobalState.get_property_value(levelName,"currency") + currency progress_dict[levelName]["currency"] = GlobalState.get_property_value(levelName,"currency") + currency
progress_dict[levelName]["kills"] = GlobalState.get_property_value(levelName,"kills") + kills progress_dict[levelName]["kills"] = GlobalState.get_property_value(levelName,"kills") + kills
progress_dict[levelName]["deaths"] = GlobalState.get_property_value(levelName,"deaths") + deaths progress_dict[levelName]["deaths"] = GlobalState.get_property_value(levelName,"deaths") + deaths
progress_dict[levelName]["frees"] = GlobalState.get_property_value(levelName,"frees") + frees progress_dict[levelName]["frees"] = GlobalState.get_property_value(levelName,"frees") + frees
# TODO Wallet is independant from progress_dict because??? # TODO Wallet is independant from progress_dict because???
GlobalState.set_wallet(GlobalState.gsr.wallet + currency) GlobalState.set_wallet(GlobalState.gsr.wallet + currency)
GlobalState.set_progress(progress_dict) GlobalState.set_progress(progress_dict)
func player_dying(animation_number: int = 0) -> void: func player_dying(animation_number: int = 0) -> void:
currency = 0 currency = 0
kills = 0 kills = 0
frees = 0 frees = 0
is_dead = true is_dead = true
deaths += 1 deaths += 1
update_global_state() update_global_state()
deaths = 0 deaths = 0