485 lines
13 KiB
GDScript
485 lines
13 KiB
GDScript
extends CharacterBody2D
|
||
|
||
const speed = 100
|
||
const dodge_speed = 200 # Speed during dodge
|
||
const dodge_duration = 0.5 # How long the dodge lasts (in seconds)
|
||
const dodge_cooldown_time = 3.0 # Dodge cooldown (in seconds)
|
||
|
||
var current_direction = "none"
|
||
var is_dodging = false
|
||
var dodge_cooldown_active = false
|
||
|
||
var enemy_in_attack_range = false
|
||
var enemy_attack_cooldown = true
|
||
var healt = 100 # return to 100 before turnin
|
||
var player_alive = true
|
||
var attack_ip = false
|
||
|
||
var attack_in_progress = false # New state for attack-in-progress
|
||
|
||
# variables for game progress
|
||
var has_blue_potion = false
|
||
var has_red_potion = false
|
||
var has_green_potion = false
|
||
var coin_count = 0
|
||
|
||
var word_chunks = [] # Stores the words to show in chunks
|
||
var current_chunk_index = 0 # Tracks which chunk we're on
|
||
|
||
# Call this to start displaying the text in chunks
|
||
func show_text_in_chunks(full_text: String) -> void:
|
||
# Add wizard/knight-style ending at the end
|
||
full_text += " Ave Domine Limosus, gloria regni slime aeterni!"
|
||
|
||
# Tokenize the full text into words and punctuation
|
||
word_chunks = tokenize_text(full_text)
|
||
current_chunk_index = 0
|
||
$"TextEdit".visible = true
|
||
$"TextEdit".text = "" # Start clean
|
||
|
||
set_process(true) # Allow input checking in _process
|
||
|
||
# Tokenize text into words and punctuation marks separately
|
||
func tokenize_text(text: String) -> Array:
|
||
var regex = RegEx.new()
|
||
regex.compile(r"[\w']+|[.,!?;]") # Match words and punctuation
|
||
var matches = regex.search_all(text)
|
||
var tokens = []
|
||
for match in matches:
|
||
tokens.append(match.strings[0])
|
||
return tokens
|
||
|
||
# Called every frame, listens for just-pressed input to show next chunk
|
||
func _process(_delta):
|
||
if Input.is_action_just_pressed("ui_accept"):
|
||
if current_chunk_index * 7 < word_chunks.size():
|
||
_display_next_chunk()
|
||
elif $"TextEdit".visible:
|
||
_display_empty_chunk()
|
||
|
||
# Show the current chunk of exactly 7 words
|
||
func _display_next_chunk() -> void:
|
||
var start = current_chunk_index * 7
|
||
var end = min(start + 7, word_chunks.size())
|
||
var chunk_text = " ".join(word_chunks.slice(start, end))
|
||
$"TextEdit".text = chunk_text
|
||
current_chunk_index += 1
|
||
|
||
# Show empty chunk and hide the box
|
||
func _display_empty_chunk() -> void:
|
||
$"TextEdit".text = ""
|
||
$"TextEdit".visible = false
|
||
set_process(false)
|
||
|
||
|
||
|
||
func resetGame():
|
||
has_blue_potion = false
|
||
has_red_potion = false
|
||
has_green_potion = false
|
||
current_direction = "none"
|
||
is_dodging = false
|
||
dodge_cooldown_active = false
|
||
enemy_in_attack_range = false
|
||
enemy_attack_cooldown = true
|
||
healt = 100
|
||
player_alive = true
|
||
attack_ip = false
|
||
position.x = 0
|
||
position.y = 0
|
||
|
||
|
||
func _ready():
|
||
var animation = $AnimatedSprite2D
|
||
animation.play("front_idle")
|
||
|
||
# Initialize dodge cooldown bar at full value
|
||
$DodgeCoolDownBar.max_value = dodge_cooldown_time
|
||
$DodgeCoolDownBar.value = dodge_cooldown_time # Start at full
|
||
|
||
# Make sure healthbar is always visible
|
||
$healthbar.visible = true # Set to true to always display health bar
|
||
|
||
func _physics_process(delta):
|
||
player_movement()
|
||
enemy_attack()
|
||
play_attack_animation()
|
||
update_healt()
|
||
update_dodge_cooldown(delta)
|
||
|
||
if healt <= 0:
|
||
player_alive = false
|
||
healt = 0
|
||
print("Player has been killed")
|
||
resetGame()
|
||
|
||
func player_movement():
|
||
velocity = Vector2.ZERO
|
||
|
||
if Input.is_action_pressed("ui_right"):
|
||
velocity.x = speed
|
||
current_direction = "right"
|
||
|
||
if Input.is_action_pressed("ui_left"):
|
||
velocity.x = -speed
|
||
current_direction = "left"
|
||
|
||
if Input.is_action_pressed("ui_down"):
|
||
velocity.y = speed
|
||
current_direction = "down"
|
||
|
||
if Input.is_action_pressed("ui_up"):
|
||
velocity.y = -speed
|
||
current_direction = "up"
|
||
|
||
# Normalize velocity for diagonal movement
|
||
if velocity.length() > speed:
|
||
velocity = velocity.normalized() * speed
|
||
|
||
if is_dodging:
|
||
velocity = velocity.normalized() * dodge_speed # Apply dodge boost
|
||
|
||
# Use move_and_slide() with only the velocity
|
||
move_and_slide()
|
||
|
||
# Play walking or idle animation
|
||
if velocity != Vector2.ZERO and !attack_in_progress:
|
||
play_animation(1) # Walking animation
|
||
elif !attack_in_progress:
|
||
play_animation(0) # Idle animation
|
||
|
||
func play_animation(movement):
|
||
var dir = current_direction
|
||
var animation = $AnimatedSprite2D
|
||
|
||
if dir == "right":
|
||
animation.flip_h = false
|
||
if movement == 1:
|
||
animation.play("side_walk")
|
||
elif movement == 0:
|
||
if !attack_ip:
|
||
animation.play("side_idle")
|
||
if dir == "left":
|
||
animation.flip_h = true
|
||
if movement == 1:
|
||
animation.play("side_walk")
|
||
elif movement == 0:
|
||
if !attack_ip:
|
||
animation.play("side_idle")
|
||
if dir == "down":
|
||
animation.flip_h = false
|
||
if movement == 1:
|
||
animation.play("front_walk")
|
||
elif movement == 0:
|
||
if !attack_ip:
|
||
animation.play("front_idle")
|
||
if dir == "up":
|
||
animation.flip_h = false
|
||
if movement == 1:
|
||
animation.play("back_walk")
|
||
elif movement == 0:
|
||
if !attack_ip:
|
||
animation.play("back_idle")
|
||
|
||
func play_attack_animation():
|
||
var dir = current_direction
|
||
var animation = $AnimatedSprite2D
|
||
var timer = $deal_attack_timer
|
||
|
||
if Input.is_action_just_pressed("attack") and !attack_in_progress:
|
||
attack_in_progress = true
|
||
global.player_current_attack = true
|
||
attack_ip = true
|
||
if dir == "right":
|
||
animation.flip_h = false
|
||
animation.play("side_attack")
|
||
timer.start()
|
||
elif dir == "left":
|
||
animation.flip_h = true
|
||
animation.play("side_attack")
|
||
timer.start()
|
||
elif dir == "down":
|
||
animation.flip_h = false
|
||
animation.play("front_attack")
|
||
timer.start()
|
||
elif dir == "up":
|
||
animation.flip_h = false
|
||
animation.play("back_attack")
|
||
timer.start()
|
||
|
||
func _on_deal_attack_timer_timeout():
|
||
var timer = $deal_attack_timer
|
||
timer.stop()
|
||
global.player_current_attack = false
|
||
attack_ip = false
|
||
attack_in_progress = false # Reset attack progress
|
||
|
||
func _on_player_hitbox_body_entered(body):
|
||
if body.has_method("enemy"):
|
||
enemy_in_attack_range = true
|
||
|
||
func _on_player_hitbox_body_exited(body):
|
||
if body.has_method("enemy"):
|
||
enemy_in_attack_range = false
|
||
|
||
func player():
|
||
pass
|
||
|
||
func enemy_attack():
|
||
if enemy_in_attack_range && enemy_attack_cooldown:
|
||
healt -= 10
|
||
enemy_attack_cooldown = false
|
||
$attack_cooldown.start()
|
||
print("Current healt: ", healt)
|
||
|
||
func _on_attack_cooldown_timeout():
|
||
enemy_attack_cooldown = true
|
||
|
||
func update_healt():
|
||
var healtbar = $healthbar
|
||
healtbar.value = healt
|
||
# Healthbar is now always visible
|
||
if healt >= 100:
|
||
healtbar.visible = true # Ensure it's visible at all times
|
||
else:
|
||
healtbar.visible = true # Ensure it's visible even when health drops below 100
|
||
|
||
func _on_regen_timer_timeout():
|
||
if healt < 100:
|
||
healt = healt + 20
|
||
if healt > 100:
|
||
healt = 100
|
||
if healt <= 0:
|
||
healt = 0
|
||
|
||
# Handle Dodge
|
||
func _input(event):
|
||
if event.is_action_pressed("dodge") and !dodge_cooldown_active:
|
||
is_dodging = true
|
||
# When dodge is triggered, set the cooldown to 0 (start cooldown)
|
||
$DodgeCoolDownBar.value = 0
|
||
# Dodge lasts for a short time
|
||
await get_tree().create_timer(dodge_duration).timeout
|
||
is_dodging = false
|
||
dodge_cooldown_active = true
|
||
|
||
# Update Dodge cooldown
|
||
func update_dodge_cooldown(delta):
|
||
if dodge_cooldown_active:
|
||
$DodgeCoolDownBar.value += delta # Slowly fill up the dodge bar over time
|
||
if $DodgeCoolDownBar.value >= dodge_cooldown_time:
|
||
dodge_cooldown_active = false
|
||
$DodgeCoolDownBar.value = dodge_cooldown_time # Reset to full after cooldown ends
|
||
|
||
|
||
func gameEnd() -> void:
|
||
while $"TextEdit".visible:
|
||
await get_tree().create_timer(1.0).timeout
|
||
print("The game has been beaten!")
|
||
queue_free()
|
||
get_tree().quit()
|
||
|
||
|
||
|
||
func enterHouseNico(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(1290, 1130)
|
||
|
||
|
||
func leaveHouseNico(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(1300, 580)
|
||
|
||
|
||
func goIntoBasementNico(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(1100, 1130)
|
||
|
||
|
||
func leaveBasementNico(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(1250, 1200)
|
||
|
||
|
||
func enterHouse(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(400, -800)
|
||
|
||
|
||
func leaveHouse(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(80, -30)
|
||
|
||
|
||
func enterCastle(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(3820, -1025)
|
||
|
||
|
||
func leaveCastle(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(2420, -1225)
|
||
|
||
|
||
func layer0(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(3620, -1425)
|
||
|
||
|
||
func layer1(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(4220, -1025)
|
||
|
||
|
||
func layer2(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(4820, -1025)
|
||
|
||
|
||
func layer3(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(4220, -1325)
|
||
|
||
|
||
func layer4(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(4820, -1325)
|
||
|
||
|
||
func layer5(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(5420, -1325)
|
||
|
||
|
||
func layer6(body: Node2D) -> void:
|
||
if body == self:
|
||
self.position = Vector2(5420, -1025)
|
||
|
||
|
||
func leaveDungeon(body: Node2D) -> void:
|
||
if body == self:
|
||
enterCastle(self)
|
||
|
||
|
||
|
||
func keyPickUp(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/Area2D".queue_free()
|
||
$"../center-left/StaticBody2D".queue_free()
|
||
|
||
|
||
|
||
func pickUpRedPotion(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/Area2D2".queue_free()
|
||
has_red_potion = true
|
||
|
||
|
||
func pickUpBluePotion(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/Area2D3".queue_free()
|
||
has_blue_potion = true
|
||
|
||
|
||
func pickUpGreenPotion(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/Area2D4".queue_free()
|
||
has_green_potion = true
|
||
|
||
|
||
func pickUpCoin1(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin1".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
func pickUpCoin2(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin2".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
|
||
func pickUpCoin3(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin3".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
|
||
func pickUpCoin4(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin4".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
|
||
func pickUpCoin5(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin5".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
|
||
func pickUpCoin6(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin6".queue_free()
|
||
coin_count +=1
|
||
print(coin_count)
|
||
|
||
|
||
func pickUpCoin7(body: Node2D) -> void:
|
||
if body == self:
|
||
$"../collectables/coins/coin7".queue_free()
|
||
coin_count += 1
|
||
print(coin_count)
|
||
|
||
|
||
func wizardTalking(body: Node2D) -> void:
|
||
if body == self:
|
||
if has_blue_potion and has_green_potion and has_red_potion:
|
||
show_text_in_chunks(
|
||
"By the stars, you actually did it! You brought all three potions—red, green, and blue!
|
||
|
||
...Oh. Uh, yeah—about that. So here's the thing...
|
||
You only need to drink the red one to return to your world. The other two?
|
||
Yeah those were... kinda for me. I just really like how the green one tastes with toast. And the blue one? Makes bath time way more interesting. Heh.
|
||
|
||
Anyway! Bottoms up with that red one, and—poof!—you’re home.
|
||
|
||
No refunds!"
|
||
)
|
||
|
||
gameEnd()
|
||
else:
|
||
show_text_in_chunks("Ah... you're not from this world, are you? Curious. Your aura—it's foreign, like a breeze from a land long forgotten. Fear not! I can send you back... but the spell requires three ancient potions: red, green, and blue. They're scattered deep within this slime-infested forest. Charming place, really. Oh, before you go stumbling into a slime's belly, a few pointers: WASD or arrow keys to move, Spacebar for a quick dodge—handy for avoiding gooey hugs, and to deal with trouble, just left-click or press E to strike. Now go! The forest won’t wait... and neither will the slimes.")
|
||
|
||
|
||
func knightTalking(body: Node2D) -> void:
|
||
if body == self and coin_count >= 6:
|
||
show_text_in_chunks(
|
||
"...Well now. You actually returned... and with all seven coins of Lazaret, no less.
|
||
|
||
Many have entered that cursed dungeon—most were not so fortunate.
|
||
|
||
You’ve proven your strength... and your spirit. As promised, I shall open the gate.
|
||
|
||
The blue potion is yours. Use it wisely—it holds power not meant for the faint of heart.
|
||
|
||
May your path forward be clear... and your enemies less so."
|
||
)
|
||
|
||
$"../north/StaticBody2D2".queue_free()
|
||
coin_count = 0
|
||
elif body == self:
|
||
show_text_in_chunks(
|
||
"Ah, I see your eyes are fixed on that glowing prize beyond the gate—the blue potion. So close you could almost taste it… yet still out of reach.
|
||
|
||
Know this: that potion is not yours for the taking—not yet. Only those who prove their worth may claim it.
|
||
|
||
Venture into the dungeon below and retrieve the Sixa Gold Coins of Lazaret. Only then will I raise this gate.
|
||
|
||
Do not return empty-handed... or not at all."
|
||
)
|
||
|
||
|