#include "godot_scene.h" #include #include #include #include namespace com::fourisland::lingo2_archipelago { namespace { class GodotSceneImpl : public GodotScene { public: GodotSceneImpl(std::map ext_resources, std::unique_ptr root, std::vector> descendents) : ext_resources_(std::move(ext_resources)), root_(std::move(root)), descendents_(std::move(descendents)) {} virtual const GodotExtResource* GetExtResource(const std::string& id) const { auto it = ext_resources_.find(id); if (it != ext_resources_.end()) { return &it->second; } else { return nullptr; } } virtual const GodotNode& GetRoot() const { return *root_; } private: std::map ext_resources_; std::unique_ptr root_; std::vector> descendents_; }; struct Heading { std::string type; std::string id; std::string path; std::string resource_type; std::string name; std::string parent; GodotInstanceType instance_type; }; Heading ParseTscnHeading(absl::string_view line) { std::string original_line(line); Heading heading; if (line[0] != '[') { std::ostringstream errormsg; errormsg << "Heading must start with [." << std::endl << "Bad heading: " << original_line; throw std::invalid_argument(errormsg.str()); } line.remove_prefix(1); int divider = line.find_first_of(" ]"); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } heading.type = std::string(line.substr(0, divider)); line.remove_prefix(divider + 1); while (!line.empty()) { divider = line.find_first_of("="); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string key(line.substr(0, divider)); line.remove_prefix(divider + 1); if (line[0] == '"') { line.remove_prefix(1); divider = line.find_first_of("\""); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string strval(line.substr(0, divider)); line.remove_prefix(divider + 2); if (key == "name") { heading.name = strval; } else if (key == "parent") { heading.parent = strval; } else if (key == "path") { heading.path = strval; } else if (key == "type") { heading.resource_type = strval; } else if (key == "id") { heading.id = strval; } } else if (line[0] == 'S' || line[0] == 'E') { GodotInstanceType rrval; char internal = line[0]; line.remove_prefix(13); // SubResource(" divider = line.find_first_of("\""); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string refid = std::string(line.substr(0, divider)); line.remove_prefix(divider + 3); GodotInstanceType instance_type; if (internal == 'E') { instance_type = GodotExtResourceRef{.id = refid}; } else { // SubResource is not supported right now. } if (key == "instance") { heading.instance_type = instance_type; } else { // Other keys aren't supported right now. } } else { divider = line.find_first_of(" ]"); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } int numval = std::atoi(line.substr(0, divider).data()); line.remove_prefix(divider + 1); // keyvals_[key] = numval; } } return heading; } } // namespace void GodotNode::AddChild(GodotNode& child) { children_[child.GetName()] = &child; child.parent_ = this; } std::string GodotNode::GetPath() const { if (parent_ == nullptr || parent_->GetName() == "") { return name_; } else { return parent_->GetPath() + "/" + name_; } } const GodotNode* GodotNode::GetNode(absl::string_view path) const { std::vector names = absl::StrSplit(path, "/"); auto it = children_.find(names[0]); if (it == children_.end()) { return nullptr; } else { if (names.size() == 1) { return it->second; } else { path.remove_prefix(names[0].size() + 1); return it->second->GetNode(path); } } } GodotNode* GodotNode::GetNode(absl::string_view path) { return const_cast( const_cast(this)->GetNode(path)); } std::unique_ptr ReadGodotSceneFromFile(const std::string& path) { std::map ext_resources; auto root = std::make_unique("", GodotInstanceType{}); std::vector> descendents; std::ifstream input(path); std::string line; bool section_started = false; Heading cur_heading; std::ostringstream cur_value; bool value_started = false; auto handle_end_of_section = [&]() { section_started = false; value_started = false; if (cur_heading.type == "sub_resource") { // sub_resources_[std::get(cur_heading.GetKeyval("id"))] = // {cur_heading, cur_value.str(), ""}; } else { // other_.emplace_back(cur_heading, cur_value.str()); } cur_value = {}; }; while (std::getline(input, line)) { if (section_started && (line.empty() || line[0] == '[')) { handle_end_of_section(); } if (!line.empty() && line[0] == '[') { Heading heading = ParseTscnHeading(line); if (heading.type == "gd_scene") { // file_descriptor_ = heading; } else if (heading.type == "ext_resource") { GodotExtResource ext_resource; ext_resource.path = heading.path; ext_resource.type = heading.resource_type; ext_resources[heading.id] = ext_resource; } else if (heading.type == "node") { if (heading.parent != "") { descendents.push_back( std::make_unique(heading.name, heading.instance_type)); GodotNode* child = descendents.back().get(); if (heading.parent == ".") { root->AddChild(*child); } else { root->GetNode(heading.parent)->AddChild(*child); } } } else { cur_heading = heading; section_started = true; } } else if (!line.empty()) { if (value_started) { cur_value << std::endl; } else { value_started = true; } cur_value << line; } } if (section_started) { handle_end_of_section(); } return std::make_unique( std::move(ext_resources), std::move(root), std::move(descendents)); } } // namespace com::fourisland::lingo2_archipelago color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
extends CanvasLayer

var _message_queue = []
var _font
var _container
var _ordered_labels = []


func _ready():
	_container = VBoxContainer.new()
	_container.set_name("Container")
	_container.anchor_bottom = 1
	_container.offset_left = 20.0
	_container.offset_right = 1920.0
	_container.offset_top = 0.0
	_container.offset_bottom = -20.0
	_container.alignment = BoxContainer.ALIGNMENT_END
	_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
	self.add_child(_container)

	_font = load("res://assets/fonts/Lingo2.ttf")


func _add_message(text):
	var new_label = RichTextLabel.new()
	new_label.push_font(_font)
	new_label.push_font_size(36)
	new_label.push_outline_color(Color(0, 0, 0, 1))
	new_label.push_outline_size(2)
	new_label.append_text(text)
	new_label.fit_content = true

	_container.add_child(new_label)
	_ordered_labels.push_back(new_label)


func showMessage(text):
	if _ordered_labels.size() >= 9:
		_message_queue.append(text)
		return

	_add_message(text)

	if _ordered_labels.size() > 1:
		return

	var timeout = 10.0
	while !_ordered_labels.is_empty():
		await get_tree().create_timer(timeout).timeout

		if !_ordered_labels.is_empty():
			var to_remove = _ordered_labels.pop_front()
			var to_tween = get_tree().create_tween().bind_node(to_remove)
			to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5)
			to_tween.tween_callback(to_remove.queue_free)

		if !_message_queue.is_empty():
			var next_msg = _message_queue.pop_front()
			_add_message(next_msg)

		if timeout > 4:
			timeout -= 3


func clear():
	_message_queue.clear()

	for message_label in _ordered_labels:
		message_label.queue_free()

	_ordered_labels.clear()