Files
liborng/src/liborng/nodes/level_scene.cpp
T
ActualHorse c8ecf7329e - Added Character3D as a C++ class.
- LevelScene now executes a signal when a Character3D falls out of the world.
- slice_mesh() now transforms the slice cap normals to match the vertices.
2026-08-25 01:33:07 -04:00

110 lines
3.5 KiB
C++

/*
* ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
*
* If this source code makes it to the public internet, this software can be
* considered to be protected by the MIT licence. Have fun with it.
*/
#include "level_scene.h"
#include "character_3d.h"
#include "nodes/player_spawn.h"
#include "singletons/save_manager.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void LevelScene::_ready()
{
#ifdef DEBUG_ENABLED
if(Engine::get_singleton()->is_editor_hint()) { return; }
#endif // DEBUG_ENABLED
// We defer this call until the end of the setup frame, since we'll need
// SaveManager to have been registered as a global singleton before we can
// find the correct player spawn. This should not be necessary any time
// after the first scene has loaded and the singleton has been registered,
// but for safety purposes this still makes sense right now.
this->call_deferred("_scene_ready");
}
void LevelScene::_scene_ready()
{
// We should probably assert that there is a valid GameClasses file first
// thing, since that's vital for initialising LevelScenes.
#ifdef DEBUG_ENABLED
if (!this->game_classes.is_valid())
{
UtilityFunctions::push_error("No GameClasses resource attached to level.");
return;
}
#endif
this->level_death_plane_node = cast_to<Area3D>(this->get_node_or_null(this->level_death_plane));
if (this->level_death_plane_node)
{
this->level_death_plane_node->set_monitoring(true);
this->level_death_plane_node->set_gravity_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->set_linear_damp_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->set_angular_damp_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->connect("body_entered", callable_mp(this, &LevelScene::_body_fell_out_of_world));
}
SpawnPointMap spawn_points = this->_find_player_spawns();
const StringName &current_checkpoint = SaveManager::get_singleton()->get_current_checkpoint();
const PlayerOrigin *spawn_point = spawn_points[current_checkpoint];
#ifdef DEBUG_ENABLED
if (!spawn_point)
{
UtilityFunctions::push_error("Could not find a spawn point named ", current_checkpoint, "; defaulting to origin...");
spawn_point = spawn_points[PLAYER_ORIGIN_TAG];
DEV_ASSERT(spawn_point);
}
#endif
// Attempt to spawn the player scene at this spawn point.
spawn_point->spawn_player(this, this->game_classes->get_player_scene());
}
SpawnPointMap LevelScene::_find_player_spawns() const
{
SpawnPointMap spawn_points;
this->_find_player_spawns_recursive(this, spawn_points);
return spawn_points;
}
void LevelScene::_find_player_spawns_recursive(const Node *node, SpawnPointMap &spawn_points) const
{
if (const PlayerOrigin *potential_spawn = cast_to<PlayerOrigin>(node))
{
spawn_points.emplace(potential_spawn->get_tag(), potential_spawn);
}
const TypedArray<Node> &children = node->get_children();
for (int i = 0; i < children.size(); i++)
{
this->_find_player_spawns_recursive(cast_to<Node>(children[i]), spawn_points);
}
}
void LevelScene::_body_fell_out_of_world(Node3D *body)
{
if (Character3D *character3d = cast_to<Character3D>(body))
{
character3d->emit_signal(FELL_OUT_OF_WORLD_SIGNAL);
}
else if (body->has_method(FELL_OUT_OF_WORLD_METHOD))
{
body->call(FELL_OUT_OF_WORLD_METHOD);
}
}