This commit is contained in:
2025-11-26 18:57:37 -06:00
parent 1e83200bba
commit d532a9ab21
25 changed files with 525 additions and 534 deletions

View File

@@ -5,23 +5,23 @@ signal interactable(playerEntity:Player)
signal notInteractable(playerEntity:Player) signal notInteractable(playerEntity:Player)
func _enter_tree() -> void: func _enter_tree() -> void:
area_entered.connect(onAreaEntered) area_entered.connect(onAreaEntered)
area_exited.connect(onAreaExited) area_exited.connect(onAreaExited)
func _exit_tree() -> void: func _exit_tree() -> void:
area_entered.disconnect(onAreaEntered) area_entered.disconnect(onAreaEntered)
area_exited.disconnect(onAreaExited) area_exited.disconnect(onAreaExited)
func onAreaEntered(area:Area3D) -> void: func onAreaEntered(area:Area3D) -> void:
if !area.get_parent() or !(area.get_parent() is Player): if !area.get_parent() or !(area.get_parent() is Player):
return return
var player:Player = area.get_parent() as Player var player:Player = area.get_parent() as Player
interactable.emit(player) interactable.emit(player)
func onAreaExited(area:Area3D) -> void: func onAreaExited(area:Area3D) -> void:
if !area.get_parent() or !(area.get_parent() is Player): if !area.get_parent() or !(area.get_parent() is Player):
return return
var player:Player = area.get_parent() as Player var player:Player = area.get_parent() as Player
notInteractable.emit(player) notInteractable.emit(player)

View File

@@ -7,44 +7,44 @@ var interactableArea:InteractableArea
var shapeChild:CollisionShape3D var shapeChild:CollisionShape3D
func _enter_tree() -> void: func _enter_tree() -> void:
interactableArea = $InteractableArea interactableArea = $InteractableArea
if !mapResource: if !mapResource:
push_error("MapChangeInteract must have a mapResource set.") push_error("MapChangeInteract must have a mapResource set.")
return return
# Needs to be exactly one child. # Needs to be exactly one child.
if get_child_count() != 2: if get_child_count() != 2:
push_error("MapChangeInteract must have exactly one child InteractableArea node.") push_error("MapChangeInteract must have exactly one child InteractableArea node.")
return return
var child = get_child(1) var child = get_child(1)
if not (child is CollisionShape3D): if not (child is CollisionShape3D):
push_error("MapChangeInteract's child must be an CollisionShape3D node.") push_error("MapChangeInteract's child must be an CollisionShape3D node.")
shapeChild = child shapeChild = child
remove_child(shapeChild) remove_child(shapeChild)
interactableArea.add_child(shapeChild) interactableArea.add_child(shapeChild)
interactableArea.interactEvent.connect(onInteract) interactableArea.interactEvent.connect(onInteract)
interactableArea.interactable.connect(onInteractable) interactableArea.interactable.connect(onInteractable)
interactableArea.notInteractable.connect(onNotInteractable) interactableArea.notInteractable.connect(onNotInteractable)
func _exit_tree() -> void: func _exit_tree() -> void:
interactableArea.interactEvent.disconnect(onInteract) interactableArea.interactEvent.disconnect(onInteract)
interactableArea.interactable.disconnect(onInteractable) interactableArea.interactable.disconnect(onInteractable)
interactableArea.notInteractable.disconnect(onNotInteractable) interactableArea.notInteractable.disconnect(onNotInteractable)
interactableArea.remove_child(shapeChild) interactableArea.remove_child(shapeChild)
add_child(shapeChild) add_child(shapeChild)
func onInteract(_ent:Player) -> void: func onInteract(_ent:Player) -> void:
OVERWORLD.mapChange(mapResource, destinationPointNodeName) OVERWORLD.mapChange(mapResource, destinationPointNodeName)
func onInteractable(_ent:Player) -> void: func onInteractable(_ent:Player) -> void:
print("Able to leave map") print("Able to leave map")
pass pass
func onNotInteractable(_ent:Player) -> void: func onNotInteractable(_ent:Player) -> void:
print("Not able to leave map") print("Not able to leave map")
pass pass

View File

@@ -3,10 +3,10 @@ class_name CutsceneItem extends Node
var cutscene:Cutscene = null var cutscene:Cutscene = null
func start() -> void: func start() -> void:
# This method should be overridden by subclasses # This method should be overridden by subclasses
pass pass
func done() -> void: func done() -> void:
if !cutscene: if !cutscene:
return return
cutscene.nextItem() cutscene.nextItem()

View File

@@ -4,24 +4,24 @@ class_name EntityMovement extends Node
const FRICTION = 0.01 const FRICTION = 0.01
enum FacingDirection { enum FacingDirection {
SOUTH = 0, SOUTH = 0,
EAST = 1, EAST = 1,
NORTH = 2, NORTH = 2,
WEST = 3 WEST = 3
}; };
const FacingDirWalkAnimations = { const FacingDirWalkAnimations = {
FacingDirection.SOUTH: "walk_south", FacingDirection.SOUTH: "walk_south",
FacingDirection.EAST: "walk_east", FacingDirection.EAST: "walk_east",
FacingDirection.NORTH: "walk_north", FacingDirection.NORTH: "walk_north",
FacingDirection.WEST: "walk_west" FacingDirection.WEST: "walk_west"
}; };
const FacingDirAngle = { const FacingDirAngle = {
FacingDirection.SOUTH: 0.0, FacingDirection.SOUTH: 0.0,
FacingDirection.EAST: PI / 2, FacingDirection.EAST: PI / 2,
FacingDirection.NORTH: PI, FacingDirection.NORTH: PI,
FacingDirection.WEST: -PI / 2 FacingDirection.WEST: -PI / 2
}; };
var _inputDir:Vector2 = Vector2.ZERO var _inputDir:Vector2 = Vector2.ZERO
@@ -34,115 +34,115 @@ var _running:bool = false
@export var walkSpeed:float = 48.0 @export var walkSpeed:float = 48.0
@export var runSpeed:float = 64.0 @export var runSpeed:float = 64.0
@export var facingDir:FacingDirection = FacingDirection.SOUTH: @export var facingDir:FacingDirection = FacingDirection.SOUTH:
set(value): set(value):
_facingDir = value _facingDir = value
_updateSprite() _updateSprite()
get: get:
return _facingDir return _facingDir
# #
# Private Methods # Private Methods
# #
func _updateSprite() -> void: func _updateSprite() -> void:
if !sprite || sprite.animation == FacingDirWalkAnimations[facingDir]: if !sprite || sprite.animation == FacingDirWalkAnimations[facingDir]:
return return
sprite.animation = FacingDirWalkAnimations[facingDir] sprite.animation = FacingDirWalkAnimations[facingDir]
func _applyFacingDir() -> void: func _applyFacingDir() -> void:
if !sprite || _inputDir.length() <= 0.01: if !sprite || _inputDir.length() <= 0.01:
return return
if _inputDir.y > 0: if _inputDir.y > 0:
if facingDir != FacingDirection.NORTH && _inputDir.x != 0: if facingDir != FacingDirection.NORTH && _inputDir.x != 0:
if _inputDir.x > 0 && facingDir == FacingDirection.EAST: if _inputDir.x > 0 && facingDir == FacingDirection.EAST:
facingDir = FacingDirection.EAST facingDir = FacingDirection.EAST
elif _inputDir.x < 0 && facingDir == FacingDirection.WEST: elif _inputDir.x < 0 && facingDir == FacingDirection.WEST:
facingDir = FacingDirection.WEST facingDir = FacingDirection.WEST
else: else:
facingDir = FacingDirection.NORTH facingDir = FacingDirection.NORTH
else: else:
facingDir = FacingDirection.NORTH facingDir = FacingDirection.NORTH
elif _inputDir.y < 0: elif _inputDir.y < 0:
if facingDir != FacingDirection.SOUTH && _inputDir.x != 0: if facingDir != FacingDirection.SOUTH && _inputDir.x != 0:
if _inputDir.x > 0 && facingDir == FacingDirection.EAST: if _inputDir.x > 0 && facingDir == FacingDirection.EAST:
facingDir = FacingDirection.EAST facingDir = FacingDirection.EAST
elif _inputDir.x < 0 && facingDir == FacingDirection.WEST: elif _inputDir.x < 0 && facingDir == FacingDirection.WEST:
facingDir = FacingDirection.WEST facingDir = FacingDirection.WEST
else: else:
facingDir = FacingDirection.SOUTH facingDir = FacingDirection.SOUTH
else: else:
facingDir = FacingDirection.SOUTH facingDir = FacingDirection.SOUTH
elif _inputDir.x > 0: elif _inputDir.x > 0:
facingDir = FacingDirection.EAST facingDir = FacingDirection.EAST
else: else:
facingDir = FacingDirection.WEST facingDir = FacingDirection.WEST
func _applyGravity() -> void: func _applyGravity() -> void:
if !body.is_on_floor(): if !body.is_on_floor():
body.velocity += PHYSICS.GRAVITY * get_process_delta_time() body.velocity += PHYSICS.GRAVITY * get_process_delta_time()
func _applyMovement() -> void: func _applyMovement() -> void:
if !canMove(): if !canMove():
return return
var cameraCurrent = get_viewport().get_camera_3d() var cameraCurrent = get_viewport().get_camera_3d()
if !cameraCurrent: if !cameraCurrent:
return return
# Use camera orientation for movement direction # Use camera orientation for movement direction
var camBasis = cameraCurrent.global_transform.basis var camBasis = cameraCurrent.global_transform.basis
# Forward and right vectors, ignore vertical component # Forward and right vectors, ignore vertical component
var forward = -camBasis.z var forward = -camBasis.z
forward.y = 0 forward.y = 0
forward = forward.normalized() forward = forward.normalized()
var right = camBasis.x var right = camBasis.x
right.y = 0 right.y = 0
right = right.normalized() right = right.normalized()
var directionAdjusted = ( var directionAdjusted = (
forward * _inputDir.y + right * _inputDir.x forward * _inputDir.y + right * _inputDir.x
).normalized() ).normalized()
if directionAdjusted.length() <= 0.01: if directionAdjusted.length() <= 0.01:
return return
if rotate: if rotate:
var targetRot = atan2(directionAdjusted.x, directionAdjusted.z) var targetRot = atan2(directionAdjusted.x, directionAdjusted.z)
rotate.rotation.y = targetRot rotate.rotation.y = targetRot
var speed = walkSpeed var speed = walkSpeed
if _running: if _running:
speed = runSpeed speed = runSpeed
body.velocity.x = directionAdjusted.x * speed body.velocity.x = directionAdjusted.x * speed
body.velocity.z = directionAdjusted.z * speed body.velocity.z = directionAdjusted.z * speed
func _applyFriction(delta:float) -> void: func _applyFriction(delta:float) -> void:
body.velocity.x *= delta * FRICTION body.velocity.x *= delta * FRICTION
body.velocity.z *= delta * FRICTION body.velocity.z *= delta * FRICTION
# #
# Protected Methods # Protected Methods
# #
func canMove() -> bool: func canMove() -> bool:
return true return true
# #
# Callbacks # Callbacks
# #
func _enter_tree() -> void: func _enter_tree() -> void:
_updateSprite() _updateSprite()
func _physics_process(delta:float) -> void: func _physics_process(delta:float) -> void:
if Engine.is_editor_hint(): if Engine.is_editor_hint():
return return
if !body: if !body:
return return
_applyGravity() _applyGravity()
_applyFriction(delta) _applyFriction(delta)
_applyMovement() _applyMovement()
_applyFacingDir() _applyFacingDir()
body.move_and_slide() body.move_and_slide()

View File

@@ -4,35 +4,35 @@ class_name NPC extends CharacterBody3D
@export var _movement:NPCMovement @export var _movement:NPCMovement
@export var facingDirection:EntityMovement.FacingDirection: @export var facingDirection:EntityMovement.FacingDirection:
set(value): set(value):
if _movement: if _movement:
_movement.facingDir = value _movement.facingDir = value
get: get:
if _movement: if _movement:
return _movement.facingDir return _movement.facingDir
return EntityMovement.FacingDirection.SOUTH return EntityMovement.FacingDirection.SOUTH
@export var walkSpeed:float = 48.0: @export var walkSpeed:float = 48.0:
set(value): set(value):
if _movement: if _movement:
_movement.walkSpeed = value _movement.walkSpeed = value
get: get:
if _movement: if _movement:
return _movement.walkSpeed return _movement.walkSpeed
return 48.0 return 48.0
@export var runSpeed:float = 64.0: @export var runSpeed:float = 64.0:
set(value): set(value):
if _movement: if _movement:
_movement.runSpeed = value _movement.runSpeed = value
get: get:
if _movement: if _movement:
return _movement.runSpeed return _movement.runSpeed
return 64.0 return 64.0
func onInteract(player:Player) -> void: func onInteract(player:Player) -> void:
UI.TEXTBOX.setText("Hello, I'm an NPC!\nThis is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10"); UI.TEXTBOX.setText("Hello, I'm an NPC!\nThis is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10");
pass pass
func _enter_tree() -> void: func _enter_tree() -> void:
pass pass

View File

@@ -1,5 +1,5 @@
class_name NPCTest extends Node class_name NPCTest extends Node
func onInteract(playerEntity: Player) -> void: func onInteract(playerEntity: Player) -> void:
print("Player has interacted with the NPC.") print("Player has interacted with the NPC.")
UI.TEXTBOX.setText("You have interacted with the NPC.") UI.TEXTBOX.setText("You have interacted with the NPC.")

View File

@@ -4,28 +4,28 @@ class_name Player extends CharacterBody3D
@export var _movement:PlayerMovement @export var _movement:PlayerMovement
@export var facingDirection:EntityMovement.FacingDirection: @export var facingDirection:EntityMovement.FacingDirection:
set(value): set(value):
if _movement: if _movement:
_movement.facingDir = value _movement.facingDir = value
get: get:
if _movement: if _movement:
return _movement.facingDir return _movement.facingDir
return EntityMovement.FacingDirection.SOUTH return EntityMovement.FacingDirection.SOUTH
@export var walkSpeed:float = 48.0: @export var walkSpeed:float = 48.0:
set(value): set(value):
if _movement: if _movement:
_movement.walkSpeed = value _movement.walkSpeed = value
get: get:
if _movement: if _movement:
return _movement.walkSpeed return _movement.walkSpeed
return 48.0 return 48.0
@export var runSpeed:float = 64.0: @export var runSpeed:float = 64.0:
set(value): set(value):
if _movement: if _movement:
_movement.runSpeed = value _movement.runSpeed = value
get: get:
if _movement: if _movement:
return _movement.runSpeed return _movement.runSpeed
return 64.0 return 64.0

View File

@@ -9,25 +9,25 @@ const CAMERA_PIXEL_SCALE = 1.0
@export var offset:Vector3 = Vector3(0, 0, 12) @export var offset:Vector3 = Vector3(0, 0, 12)
func _process(delta: float) -> void: func _process(delta: float) -> void:
if !camera || !target: if !camera || !target:
return return
# I tried a few things but this is most consistent for both backbuffer and # I tried a few things but this is most consistent for both backbuffer and
# framebuffer viewports. # framebuffer viewports.
var viewportHeight = get_viewport().get_visible_rect().size.y; var viewportHeight = get_viewport().get_visible_rect().size.y;
var unitScale = CAMERA_PIXEL_SCALE * CAMERA_PIXELS_PER_UNIT; var unitScale = CAMERA_PIXEL_SCALE * CAMERA_PIXELS_PER_UNIT;
var z:float = ( var z:float = (
tan((deg_to_rad(180) - deg_to_rad(camera.fov)) / 2.0) * tan((deg_to_rad(180) - deg_to_rad(camera.fov)) / 2.0) *
(viewportHeight / 2.0) (viewportHeight / 2.0)
) / unitScale; ) / unitScale;
var look = target.global_position; var look = target.global_position;
var position = offset + look; var position = offset + look;
camera.look_at_from_position( camera.look_at_from_position(
Vector3(position.x, position.y + z, position.z), Vector3(position.x, position.y + z, position.z),
look look
); );
pass pass

View File

@@ -4,10 +4,10 @@ class_name PlayerInput extends Node
@export var movement:PlayerMovement @export var movement:PlayerMovement
func _process(delta: float) -> void: func _process(delta: float) -> void:
if Input.is_action_just_pressed("pause"): if Input.is_action_just_pressed("pause"):
PAUSE.menuPause() PAUSE.menuPause()
if Input.is_action_just_pressed("interact"): if Input.is_action_just_pressed("interact"):
interaction.interact() interaction.interact()
movement._inputDir = Input.get_vector("move_left", "move_right", "move_back", "move_forward").normalized() movement._inputDir = Input.get_vector("move_left", "move_right", "move_back", "move_forward").normalized()

View File

@@ -4,24 +4,24 @@ class_name PlayerInteraction extends Node
@export var player:CharacterBody3D @export var player:CharacterBody3D
func canInteract() -> bool: func canInteract() -> bool:
if PAUSE.isMovementPaused(): if PAUSE.isMovementPaused():
return false return false
return true return true
func interact() -> void: func interact() -> void:
if !canInteract(): if !canInteract():
return return
var overlapping = interactableArea.get_overlapping_areas() var overlapping = interactableArea.get_overlapping_areas()
var interactable: InteractableArea = null var interactable: InteractableArea = null
for node in overlapping: for node in overlapping:
if !(node is InteractableArea): if !(node is InteractableArea):
continue continue
interactable = node interactable = node
break break
if !interactable: if !interactable:
return return
interactable.interactEvent.emit(player) interactable.interactEvent.emit(player)

View File

@@ -2,6 +2,6 @@
class_name PlayerMovement extends "res://entity/EntityMovement.gd" class_name PlayerMovement extends "res://entity/EntityMovement.gd"
func canMove() -> bool: func canMove() -> bool:
if PAUSE.isMovementPaused(): if PAUSE.isMovementPaused():
return false return false
return true return true

View File

@@ -3,32 +3,32 @@ class_name OverworldScene extends Node
@export var map:Node3D = null @export var map:Node3D = null
func _enter_tree() -> void: func _enter_tree() -> void:
OVERWORLD.mapChanged.connect(onMapChanged) OVERWORLD.mapChanged.connect(onMapChanged)
func _ready() -> void: func _ready() -> void:
pass pass
func _exit_tree() -> void: func _exit_tree() -> void:
OVERWORLD.mapChanged.disconnect(onMapChanged) OVERWORLD.mapChanged.disconnect(onMapChanged)
func onMapChanged(newMap:PackedScene, playerDestinationNodeName:String) -> void: func onMapChanged(newMap:PackedScene, playerDestinationNodeName:String) -> void:
print("New map time.", newMap) print("New map time.", newMap)
for childScene in map.get_children(): for childScene in map.get_children():
map.remove_child(childScene) map.remove_child(childScene)
if !newMap: if !newMap:
return return
var newMapInstance = newMap.instantiate() var newMapInstance = newMap.instantiate()
map.add_child(newMapInstance) map.add_child(newMapInstance)
# Find Player. # Find Player.
if playerDestinationNodeName: if playerDestinationNodeName:
var player = newMapInstance.get_node("Player") var player = newMapInstance.get_node("Player")
var destNode = newMapInstance.get_node(playerDestinationNodeName) var destNode = newMapInstance.get_node(playerDestinationNodeName)
if player && player is Player && destNode: if player && player is Player && destNode:
player.global_position = destNode.global_position player.global_position = destNode.global_position
player.global_rotation.y = destNode.global_rotation.y player.global_rotation.y = destNode.global_rotation.y
elif playerDestinationNodeName: elif playerDestinationNodeName:
push_error("Player, or destination node not found in new map.") push_error("Player, or destination node not found in new map.")
pass pass

View File

@@ -4,31 +4,31 @@ class_name RootScene extends Node3D
@export var initial:Node3D = null @export var initial:Node3D = null
func _enter_tree() -> void: func _enter_tree() -> void:
SCENE.sceneChanged.connect(onSceneChange) SCENE.sceneChanged.connect(onSceneChange)
SCENE.setScene(SceneSingleton.SceneType.INITIAL) SCENE.setScene(SceneSingleton.SceneType.INITIAL)
func _exit_tree() -> void: func _exit_tree() -> void:
push_error("RootScene should not be removed from the scene tree. This is a bug.") push_error("RootScene should not be removed from the scene tree. This is a bug.")
func onSceneChange(newScene:SceneSingleton.SceneType) -> void: func onSceneChange(newScene:SceneSingleton.SceneType) -> void:
print("overworld", overworld) print("overworld", overworld)
remove_child(overworld) remove_child(overworld)
remove_child(initial) remove_child(initial)
overworld.visible = false overworld.visible = false
initial.visible = false initial.visible = false
match newScene: match newScene:
SceneSingleton.SceneType.INITIAL: SceneSingleton.SceneType.INITIAL:
add_child(initial) add_child(initial)
initial.visible = true initial.visible = true
SceneSingleton.SceneType.OVERWORLD: SceneSingleton.SceneType.OVERWORLD:
add_child(overworld) add_child(overworld)
overworld.visible = true overworld.visible = true
SceneSingleton.SceneType.UNSET: SceneSingleton.SceneType.UNSET:
pass pass
_: _:
pass pass

View File

@@ -24,7 +24,6 @@ UI="*res://singleton/UI.tscn"
QUEST="*res://singleton/Quest.tscn" QUEST="*res://singleton/Quest.tscn"
OVERWORLD="*res://singleton/Overworld.gd" OVERWORLD="*res://singleton/Overworld.gd"
SCENE="*res://singleton/Scene.gd" SCENE="*res://singleton/Scene.gd"
MadTalkGlobals="*res://addons/madtalk/runtime/MadTalkGlobals.tscn"
ControllerIcons="*res://addons/controller_icons/ControllerIcons.gd" ControllerIcons="*res://addons/controller_icons/ControllerIcons.gd"
[debug] [debug]
@@ -47,7 +46,7 @@ project/assembly_name="Dawn Godot"
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/controller_icons/plugin.cfg", "res://addons/madtalk/plugin.cfg") enabled=PackedStringArray("res://addons/controller_icons/plugin.cfg")
[filesystem] [filesystem]

View File

@@ -9,77 +9,77 @@ var playerDestinationNodeName:String
var newMapLoaded:bool = false var newMapLoaded:bool = false
func isMapChanging() -> bool: func isMapChanging() -> bool:
return newMapPath != "" return newMapPath != ""
func _enter_tree() -> void: func _enter_tree() -> void:
pass pass
func _exit_tree() -> void: func _exit_tree() -> void:
TRANSITION.fadeOutEnd.disconnect(onFadeOutEnd) TRANSITION.fadeOutEnd.disconnect(onFadeOutEnd)
TRANSITION.fadeInEnd.disconnect(onFadeInEnd) TRANSITION.fadeInEnd.disconnect(onFadeInEnd)
func _process(_delta:float) -> void: func _process(_delta:float) -> void:
if(!isMapChanging()): if(!isMapChanging()):
return return
var status = ResourceLoader.load_threaded_get_status(newMapPath) var status = ResourceLoader.load_threaded_get_status(newMapPath)
if status == ResourceLoader.THREAD_LOAD_FAILED: if status == ResourceLoader.THREAD_LOAD_FAILED:
push_error("Failed to load map: " + newMapPath) push_error("Failed to load map: " + newMapPath)
newMapPath = "" newMapPath = ""
elif status == ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: elif status == ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
push_error("Failed to load map: " + newMapPath) push_error("Failed to load map: " + newMapPath)
newMapPath = "" newMapPath = ""
elif status == ResourceLoader.THREAD_LOAD_IN_PROGRESS: elif status == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
print("Map loading in progress for: " + newMapPath) print("Map loading in progress for: " + newMapPath)
elif status == ResourceLoader.THREAD_LOAD_LOADED: elif status == ResourceLoader.THREAD_LOAD_LOADED:
newMapLoaded = true newMapLoaded = true
newMapReady() newMapReady()
func mapChange(map:String, playerDestinationNodeName:String) -> void: func mapChange(map:String, playerDestinationNodeName:String) -> void:
# Begin Loading new map # Begin Loading new map
newMapPath = map newMapPath = map
playerDestinationNodeName = playerDestinationNodeName playerDestinationNodeName = playerDestinationNodeName
ResourceLoader.load_threaded_request(newMapPath) ResourceLoader.load_threaded_request(newMapPath)
# Begin fadeout # Begin fadeout
hasFadedOut = false hasFadedOut = false
TRANSITION.fade(TransitionSingleton.FadeType.FADE_OUT) TRANSITION.fade(TransitionSingleton.FadeType.FADE_OUT)
TRANSITION.fadeOutEnd.connect(onFadeOutEnd) TRANSITION.fadeOutEnd.connect(onFadeOutEnd)
func onFadeOutEnd() -> void: func onFadeOutEnd() -> void:
# Fade out has occurred, are we still waiting for the map to load? # Fade out has occurred, are we still waiting for the map to load?
TRANSITION.fadeOutEnd.disconnect(onFadeOutEnd) TRANSITION.fadeOutEnd.disconnect(onFadeOutEnd)
hasFadedOut = true hasFadedOut = true
# Make sure we aren't still waiting for the map to load. # Make sure we aren't still waiting for the map to load.
if !newMapLoaded: if !newMapLoaded:
return return
newMapReady() newMapReady()
func newMapReady(): func newMapReady():
if !hasFadedOut: if !hasFadedOut:
return return
# Instantiate the new map # Instantiate the new map
var mapResource = ResourceLoader.load_threaded_get(newMapPath) var mapResource = ResourceLoader.load_threaded_get(newMapPath)
if mapResource == null: if mapResource == null:
push_error("Failed to load map resource: " + newMapPath) push_error("Failed to load map resource: " + newMapPath)
return return
# Stop tracking new map name # Stop tracking new map name
newMapPath = "" newMapPath = ""
# Emit event # Emit event
mapChanged.emit(mapResource, playerDestinationNodeName) mapChanged.emit(mapResource, playerDestinationNodeName)
# Begin Fade In # Begin Fade In
TRANSITION.fade(TransitionSingleton.FadeType.FADE_IN) TRANSITION.fade(TransitionSingleton.FadeType.FADE_IN)
TRANSITION.fadeInEnd.connect(onFadeInEnd) TRANSITION.fadeInEnd.connect(onFadeInEnd)
func onFadeInEnd() -> void: func onFadeInEnd() -> void:
TRANSITION.fadeInEnd.disconnect(onFadeInEnd) TRANSITION.fadeInEnd.disconnect(onFadeInEnd)

View File

@@ -3,28 +3,28 @@ class_name PauseSingleton extends Node
var cutscenePaused:bool = false var cutscenePaused:bool = false
func cutscenePause() -> void: func cutscenePause() -> void:
cutscenePaused = true cutscenePaused = true
func cutsceneResume() -> void: func cutsceneResume() -> void:
cutscenePaused = false cutscenePaused = false
func isMovementPaused() -> bool: func isMovementPaused() -> bool:
if cutscenePaused: if cutscenePaused:
return true return true
if !UI.TEXTBOX.isClosed: if !UI.TEXTBOX.isClosed:
return true return true
if UI.PAUSE.isOpen(): if UI.PAUSE.isOpen():
return true return true
if OVERWORLD.isMapChanging(): if OVERWORLD.isMapChanging():
return true return true
return false return false
func menuPause() -> void: func menuPause() -> void:
if UI.PAUSE.isOpen(): if UI.PAUSE.isOpen():
UI.PAUSE.close() UI.PAUSE.close()
else: else:
UI.PAUSE.open() UI.PAUSE.open()

View File

@@ -1,34 +1,34 @@
class_name ClosableMenu extends Control class_name ClosableMenu extends Control
@export var isOpen: bool: @export var isOpen: bool:
set(newValue): set(newValue):
isOpen = newValue isOpen = newValue
visible = newValue visible = newValue
if newValue: if newValue:
opened.emit() opened.emit()
else: else:
closed.emit() closed.emit()
get(): get():
return isOpen return isOpen
signal closed signal closed
signal opened signal opened
func _enter_tree() -> void: func _enter_tree() -> void:
visible = isOpen visible = isOpen
func _exit_tree() -> void: func _exit_tree() -> void:
visible = false visible = false
func _ready() -> void: func _ready() -> void:
visible = isOpen visible = isOpen
print("ClosableMenu is ready, isOpen: ", isOpen) print("ClosableMenu is ready, isOpen: ", isOpen)
func close() -> void: func close() -> void:
isOpen = false isOpen = false
func open() -> void: func open() -> void:
isOpen = true isOpen = true
func toggle() -> void: func toggle() -> void:
isOpen = !isOpen isOpen = !isOpen

View File

@@ -12,62 +12,62 @@ var currentViewScrolled = true;
var isSpeedupDown = false; var isSpeedupDown = false;
var isClosed:bool = false: var isClosed:bool = false:
get(): get():
return !self.visible; return !self.visible;
set(value): set(value):
self.visible = !value; self.visible = !value;
signal textboxClosing signal textboxClosing
func _ready() -> void: func _ready() -> void:
label = $MarginContainer/Label label = $MarginContainer/Label
isClosed = true isClosed = true
func _process(delta: float) -> void: func _process(delta: float) -> void:
if label.getFinalText() == "": if label.getFinalText() == "":
isClosed = true; isClosed = true;
return; return;
if label.visible_characters >= label.getCharactersDisplayedCount(): if label.visible_characters >= label.getCharactersDisplayedCount():
if (label.maxLines + label.startLine) < label.getTotalLineCount(): if (label.maxLines + label.startLine) < label.getTotalLineCount():
# Not on last page. # Not on last page.
if Input.is_action_just_pressed("interact"): if Input.is_action_just_pressed("interact"):
label.startLine += label.maxLines; label.startLine += label.maxLines;
label.visible_characters = 0; label.visible_characters = 0;
currentViewScrolled = false; currentViewScrolled = false;
return return
currentViewScrolled = true; currentViewScrolled = true;
else: else:
# On last page # On last page
if Input.is_action_just_released("interact"): if Input.is_action_just_released("interact"):
textboxClosing.emit(); textboxClosing.emit();
isClosed = true; isClosed = true;
currentViewScrolled = true currentViewScrolled = true
return; return;
if Input.is_action_just_pressed("interact"): if Input.is_action_just_pressed("interact"):
isSpeedupDown = true; isSpeedupDown = true;
elif Input.is_action_just_released("interact"): elif Input.is_action_just_released("interact"):
isSpeedupDown = false; isSpeedupDown = false;
elif !Input.is_action_pressed("interact"): elif !Input.is_action_pressed("interact"):
isSpeedupDown = false; isSpeedupDown = false;
revealTimer += delta; revealTimer += delta;
if isSpeedupDown: if isSpeedupDown:
revealTimer += delta; revealTimer += delta;
if revealTimer > VN_REVEAL_TIME: if revealTimer > VN_REVEAL_TIME:
revealTimer = 0; revealTimer = 0;
label.visible_characters += 1; label.visible_characters += 1;
func setText(text:String) -> void: func setText(text:String) -> void:
isClosed = false; isClosed = false;
revealTimer = 0; revealTimer = 0;
currentViewScrolled = false; currentViewScrolled = false;
label.visible_characters = 0; label.visible_characters = 0;
label.startLine = 0; label.startLine = 0;
label.text = "" label.text = ""
await get_tree().process_frame# Absolutely needed to make the text wrap await get_tree().process_frame# Absolutely needed to make the text wrap
label.text = text; label.text = text;
label.visible_characters = 0; label.visible_characters = 0;

View File

@@ -57,15 +57,7 @@ Line 10"
_finalText = "Hello, I'm an NPC! _finalText = "Hello, I'm an NPC!
This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot's built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing
Line 1 Line 1
Line 2 Line 2"
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10"
_newLineIndexes = Array[int]([0]) _newLineIndexes = Array[int]([0])
_lines = PackedStringArray("Hello, I\'m an NPC!", "This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot\'s built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing", "Line 1", "Line 2", "Line 3", "Line 4", "Line 5", "Line 6", "Line 7", "Line 8", "Line 9", "Line 10") _lines = PackedStringArray("Hello, I\'m an NPC!", "This is the second line here, I am purposefully adding a tonne of words so that it is forced to go across multiple lines and you can see how the word wrapping works, not only using Godot\'s built in word wrapping but with my advanced visibile characters smart wrapping. Now I am doing a multiline thing", "Line 1", "Line 2", "Line 3", "Line 4", "Line 5", "Line 6", "Line 7", "Line 8", "Line 9", "Line 10")
maxLines = 4 maxLines = 4

View File

@@ -9,133 +9,133 @@ class_name AdvancedRichText extends RichTextLabel
# Hides the original RichTextLabel text property # Hides the original RichTextLabel text property
func _set(property: StringName, value) -> bool: func _set(property: StringName, value) -> bool:
if property == "text": if property == "text":
userText = value userText = value
_recalcText() _recalcText()
return true return true
elif property == "richtextlabel_text": elif property == "richtextlabel_text":
text = value text = value
return true return true
return false return false
func _get(property: StringName): func _get(property: StringName):
if property == "text": if property == "text":
return userText return userText
elif property == "richtextlabel_text": elif property == "richtextlabel_text":
return text return text
return null return null
@export var translate:bool = true: @export var translate:bool = true:
set(value): set(value):
translate = value translate = value
_recalcText() _recalcText()
get(): get():
return translate return translate
@export var smartWrap:bool = true: @export var smartWrap:bool = true:
set(value): set(value):
smartWrap = value smartWrap = value
_recalcText() _recalcText()
get(): get():
return smartWrap return smartWrap
@export var maxLines:int = -1: @export var maxLines:int = -1:
set(value): set(value):
maxLines = value maxLines = value
_recalcText() _recalcText()
get(): get():
return maxLines return maxLines
@export var startLine:int = 0: @export var startLine:int = 0:
set(value): set(value):
startLine = value startLine = value
_recalcText() _recalcText()
get(): get():
return startLine return startLine
# Returns count of characters that can be displayed, assuming visible_chars = -1 # Returns count of characters that can be displayed, assuming visible_chars = -1
func getCharactersDisplayedCount() -> int: func getCharactersDisplayedCount() -> int:
# Count characters # Count characters
var count = 0 var count = 0
var lineCount = min(startLine + maxLines, _lines.size()) - startLine var lineCount = min(startLine + maxLines, _lines.size()) - startLine
for i in range(startLine, startLine + lineCount): for i in range(startLine, startLine + lineCount):
count += _lines[i].length() count += _lines[i].length()
if lineCount > 1: if lineCount > 1:
count += lineCount - 1 # Add newlines count += lineCount - 1 # Add newlines
return count return count
func getFinalText() -> String: func getFinalText() -> String:
return _finalText return _finalText
func getTotalLineCount() -> int: func getTotalLineCount() -> int:
return _lines.size() return _lines.size()
func _enter_tree() -> void: func _enter_tree() -> void:
self.threaded = false; self.threaded = false;
func _recalcText() -> void: func _recalcText() -> void:
_lines.clear() _lines.clear()
if userText.is_empty(): if userText.is_empty():
self.richtextlabel_text = "" self.richtextlabel_text = ""
return return
# Translate if needed # Translate if needed
var textTranslated = userText var textTranslated = userText
if self.translate: if self.translate:
textTranslated = tr(textTranslated) textTranslated = tr(textTranslated)
# Replace input bb tags. # Replace input bb tags.
var regex = RegEx.new() var regex = RegEx.new()
regex.compile(r"\[input action=(.*?)\](.*?)\[/input\]") regex.compile(r"\[input action=(.*?)\](.*?)\[/input\]")
var inputIconText = textTranslated var inputIconText = textTranslated
for match in regex.search_all(textTranslated): for match in regex.search_all(textTranslated):
var action = match.get_string(1).to_lower() var action = match.get_string(1).to_lower()
var height:int = get_theme_font_size("normal_font_size") var height:int = get_theme_font_size("normal_font_size")
var img_tag = "[img height=%d valign=center,center]res://ui/input/%s.tres[/img]" % [ height, action ] var img_tag = "[img height=%d valign=center,center]res://ui/input/%s.tres[/img]" % [ height, action ]
inputIconText = inputIconText.replace(match.get_string(0), img_tag) inputIconText = inputIconText.replace(match.get_string(0), img_tag)
# Perform smart wrapping # Perform smart wrapping
var wrappedText = inputIconText var wrappedText = inputIconText
if smartWrap: if smartWrap:
var unwrappedText = wrappedText.strip_edges() var unwrappedText = wrappedText.strip_edges()
self.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART; self.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART;
self.richtextlabel_text = unwrappedText self.richtextlabel_text = unwrappedText
self.visible_characters = -1; self.visible_characters = -1;
self.fit_content = false; self.fit_content = false;
_newLineIndexes = []; _newLineIndexes = [];
# Determine where the wrapped newlines are # Determine where the wrapped newlines are
var line = 0; var line = 0;
var wasNewLine = false; var wasNewLine = false;
for i in range(0, self.richtextlabel_text.length()): for i in range(0, self.richtextlabel_text.length()):
var tLine = self.get_character_line(i); var tLine = self.get_character_line(i);
if tLine == line: if tLine == line:
wasNewLine = false wasNewLine = false
if self.richtextlabel_text[i] == "\n": if self.richtextlabel_text[i] == "\n":
wasNewLine = true wasNewLine = true
continue; continue;
if !wasNewLine: if !wasNewLine:
_newLineIndexes.append(i); _newLineIndexes.append(i);
line = tLine; line = tLine;
# Create fake pre-wrapped text. # Create fake pre-wrapped text.
wrappedText = ""; wrappedText = "";
for i in range(0, self.richtextlabel_text.length()): for i in range(0, self.richtextlabel_text.length()):
if _newLineIndexes.find(i) != -1 and i != 0: if _newLineIndexes.find(i) != -1 and i != 0:
wrappedText += "\n"; wrappedText += "\n";
wrappedText += self.richtextlabel_text[i]; wrappedText += self.richtextlabel_text[i];
# Handle max and start line(s) # Handle max and start line(s)
var maxText = wrappedText var maxText = wrappedText
if maxLines > 0: if maxLines > 0:
_lines = maxText.split("\n", true); _lines = maxText.split("\n", true);
var selectedLines = []; var selectedLines = [];
for i in range(startLine, min(startLine + maxLines, _lines.size())): for i in range(startLine, min(startLine + maxLines, _lines.size())):
selectedLines.append(_lines[i]); selectedLines.append(_lines[i]);
maxText = "\n".join(selectedLines); maxText = "\n".join(selectedLines);
_finalText = maxText _finalText = maxText
self.richtextlabel_text = maxText self.richtextlabel_text = maxText
print("Updated text") print("Updated text")

View File

@@ -6,13 +6,13 @@ class_name MainMenu extends Control
@export_file("*.tscn") var newGameScene:String @export_file("*.tscn") var newGameScene:String
func _ready() -> void: func _ready() -> void:
btnNewGame.pressed.connect(onNewGamePressed) btnNewGame.pressed.connect(onNewGamePressed)
btnSettings.pressed.connect(onSettingsPressed) btnSettings.pressed.connect(onSettingsPressed)
func onNewGamePressed() -> void: func onNewGamePressed() -> void:
SCENE.setScene(SceneSingleton.SceneType.OVERWORLD) SCENE.setScene(SceneSingleton.SceneType.OVERWORLD)
OVERWORLD.mapChange(newGameScene, "PlayerSpawnPoint") OVERWORLD.mapChange(newGameScene, "PlayerSpawnPoint")
func onSettingsPressed() -> void: func onSettingsPressed() -> void:
print("Settings button pressed") print("Settings button pressed")
settingsMenu.isOpen = true settingsMenu.isOpen = true

View File

@@ -1,18 +1,18 @@
class_name PauseMain extends VBoxContainer class_name PauseMain extends VBoxContainer
func _ready() -> void: func _ready() -> void:
visible = false visible = false
$HBoxContainer/ItemList.item_selected.connect(onItemSelected) $HBoxContainer/ItemList.item_selected.connect(onItemSelected)
func open(): func open():
visible = true visible = true
$HBoxContainer/ItemList.clear() $HBoxContainer/ItemList.clear()
func close(): func close():
visible = false visible = false
func isOpen() -> bool: func isOpen() -> bool:
return visible return visible
func onItemSelected(index:int) -> void: func onItemSelected(index:int) -> void:
print("Selected item index: ", index) print("Selected item index: ", index)

View File

@@ -4,16 +4,16 @@ class_name PauseMenu extends Control
@export var SETTINGS:PauseSettings @export var SETTINGS:PauseSettings
func _ready() -> void: func _ready() -> void:
close() close()
func isOpen() -> bool: func isOpen() -> bool:
return visible return visible
func open() -> void: func open() -> void:
visible = true visible = true
MAIN.open() MAIN.open()
func close() -> void: func close() -> void:
visible = false visible = false
MAIN.close() MAIN.close()
SETTINGS.close() SETTINGS.close()

View File

@@ -1,10 +1,10 @@
class_name PauseSettings extends Control class_name PauseSettings extends Control
func open() -> void: func open() -> void:
visible = true visible = true
func close() -> void: func close() -> void:
visible = false visible = false
func isOpen() -> bool: func isOpen() -> bool:
return visible return visible

View File

@@ -4,12 +4,12 @@ class_name SettingsMenu extends Control
@export var tabControls:Array[Control] @export var tabControls:Array[Control]
func _ready() -> void: func _ready() -> void:
tabs.tab_changed.connect(onTabChanged) tabs.tab_changed.connect(onTabChanged)
onTabChanged(tabs.current_tab) onTabChanged(tabs.current_tab)
func onTabChanged(tabIndex:int) -> void: func onTabChanged(tabIndex:int) -> void:
for control in tabControls: for control in tabControls:
control.visible = false control.visible = false
if tabIndex >= 0 and tabIndex < tabControls.size(): if tabIndex >= 0 and tabIndex < tabControls.size():
tabControls[tabIndex].visible = true tabControls[tabIndex].visible = true