From edf779ebc60b4c6a6048db462ab39f3f5a659945 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 5 Feb 2024 23:18:38 +0100 Subject: [PATCH] feat: implemented character customization from editor --- src/player.cpp | 101 ++++++++++++++++++++++++++++++++++--------------- src/player.hpp | 17 ++++----- 2 files changed, 78 insertions(+), 40 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index a594174..67d9660 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -10,8 +10,7 @@ namespace godot { void Player::_bind_methods() { #define CLASSNAME Player #ifndef NDEBUG - GDPROPERTY(selected, Variant::STRING); - GDPROPERTY(index, Variant::INT); + GDPROPERTY(active_customization, Variant::DICTIONARY); #endif } void Player::_enter_tree() { @@ -21,8 +20,8 @@ void Player::_enter_tree() { } void Player::_exit_tree() { - for(std::pair>>& pair: this->customization) { - for(Node3D *option: pair.second.second) { + for(std::pair& pair: this->customization) { + for(Node3D *option: pair.second.options) { if(!option->is_inside_tree()) { option->queue_free(); } @@ -34,43 +33,85 @@ void Player::_process(double deltaTime) {} void Player::_physics_process(double deltaTime) {} void Player::customization_init() { - customizationParent = this->get_node("Model/RootNode/Skeleton3D"); - if(!customizationParent) + this->customizationParent = this->get_node("Model/RootNode/Skeleton3D"); + if(!this->customizationParent) return; for(int i = 0; i < customizationParent->get_child_count(); ++i) { - Node3D *child = Object::cast_to(customizationParent->get_child(i)); - if(!child) - continue; - String name = child->get_name(); - PackedStringArray slices = name.split("_", false); - if(customization.find(slices[1]) != customization.end()) { - auto &array = customization.at(slices[1]); - array.second.push_back(child); - customizationParent->remove_child(child); + // get the next valid child + Node3D *child = Object::cast_to(this->customizationParent->get_child(i)); + if(!child) continue; + // split it's name into parts based on _ + // the format for the names will be Chr__ + // number IS NOT guaranteed to start at 0 or 1 + // the category name will serve as the key to the customization map + PackedStringArray slices = child->get_name().split("_", false); + // create a new customization state if one does not exist for this category + if(this->customization.find(slices[1]) == this->customization.end()) + this->customization.insert({slices[1], {0, {}}}); + // add the new child to the customization options for this category + CustomizationState &state = this->customization.at(slices[1]); + state.options.push_back(child); + // only allow this child to exist in the scene tree if it was saved as the current selection + if(state.currentSelected != state.options.size()-1) { + this->customizationParent->remove_child(child); --i; - } else { - customization.insert({slices[1], {0, {child}}}); } } - UtilityFunctions::print("customization categories:"); - for(std::pair>>& pair: this->customization) { - UtilityFunctions::print("- ", pair.first); + std::vector empty{}; + UtilityFunctions::print("categories:"); + for(std::pair const& pair: this->customization) { + UtilityFunctions::print("- ", pair.first, " selected ", pair.second.currentSelected); + if(pair.second.options.size() == 0) + empty.push_back(pair.first); + } + for(String const& key: empty) { + this->customization.erase(key); + UtilityFunctions::print("removing invalid category: ", key); } } void Player::select_customization(String key, int index) { - std::pair> &pair = this->customization.at(key); - if(index > pair.second.size()) + if(this->customization.find(key) == this->customization.end()) return; - if(index == pair.first) + CustomizationState &pair = this->customization.at(key); + // invalid keys are stored as the size of the array + if(index < 0 || index > pair.options.size()) + index = pair.options.size(); + // nothing changes + if(index == pair.currentSelected) return; - customizationParent->remove_child(pair.second[pair.first]); - customizationParent->add_child(pair.second[index]); - pair.first = index; + // disable previous chosen option, if any + if(pair.currentSelected < pair.options.size()) + customizationParent->remove_child(pair.options[pair.currentSelected]); + // enable chosen option, if any + if(index < pair.options.size()) + customizationParent->add_child(pair.options[index]); + pair.currentSelected = index; } -#ifndef NDEBUG -void Player::set_selected(String value) { this->selected = value; } -String Player::get_selected() const { return this->selected; } -#endif +Dictionary Player::get_active_customization() const { + Dictionary result{}; + // translate the map to a dictionary of keys and indexes + for(std::pair const &pair: this->customization) + result[pair.first] = pair.second.currentSelected >= pair.second.options.size() + ? -1 // an invalid key will always be -1 + : pair.second.currentSelected; + return result; +} + +void Player::set_active_customization(Dictionary value) { + for(int64_t i = 0; i < value.size(); ++i) { + String key = value.keys()[i]; + size_t selection = value[key]; + bool exists = this->customization.find(key) != this->customization.end(); // wether or not the a given key exists in the customization map + // if not, it can be added, as long as the customization map has not yet been initialized by _enter_tree + if(!exists && !this->is_inside_tree()) + this->customization.insert({key, {selection, {}}}); + // if the key does not exist in customization and _enter_tree is already called, the key is not valid and discarted + else if(!exists) + return; + else + this->select_customization(key, selection); + } +} } // namespace godot diff --git a/src/player.hpp b/src/player.hpp index 0ca7baa..d8901ed 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -10,15 +10,16 @@ namespace godot { class Player : public CharacterBody3D { GDCLASS(Player, CharacterBody3D) static void _bind_methods(); + struct CustomizationState { + size_t currentSelected; + std::vector options; + }; protected: + std::map customization{}; Node3D* model{nullptr}; AnimationTree* animTree{nullptr}; - std::map>> customization{}; Skeleton3D *customizationParent{nullptr}; -#ifndef NDEBUG - String selected; -#endif public: virtual void _enter_tree() override; @@ -31,12 +32,8 @@ public: void select_customization(String key, int value); -#ifndef NDEBUG - void set_selected(String value); - String get_selected() const; - void set_index(int value) { this->select_customization(this->selected, value); } -int get_index() { return this->customization.find(this->selected) == this->customization.end() ? 0 : this->customization.at(this->selected).first; } -#endif + Dictionary get_active_customization() const; + void set_active_customization(Dictionary value); }; } // namespace godot