#!/usr/bin/env python3

import argparse
import contextlib
import os
import socket
import subprocess
import sys
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path

# See cpython GH-17851 and GH-17864.
class DualStackServer(HTTPServer):
    def server_bind(self):
        # Suppress exception when protocol is IPv4.
        with contextlib.suppress(Exception):
            self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
        return super().server_bind()

class CORSRequestHandler(SimpleHTTPRequestHandler):
    def end_headers(self):
        self.send_header("Cross-Origin-Opener-Policy", "same-origin")
        self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
        self.send_header("Access-Control-Allow-Origin", "*")
        super().end_headers()

def shell_open(url):
    if sys.platform == "win32":
        os.startfile(url)
    else:
        opener = "open" if sys.platform == "darwin" else "xdg-open"
        subprocess.call([opener, url])

def serve(root, port, run_browser):
    os.chdir(root)

    address = ("", port)
    httpd = DualStackServer(address, CORSRequestHandler)

    url = f"http://127.0.0.1:{port}"
    if run_browser:
        # Open the served page in the user's default browser.
        print(f"Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this): {url}")
        shell_open(url)
    else:
        print(f"Serving at: {url}")

    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\nKeyboard interrupt received, stopping server.")
    finally:
        # Clean-up server
        httpd.server_close()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int)
    parser.add_argument(
        "-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="../../bin", type=Path
    )
    browser_parser = parser.add_mutually_exclusive_group(required=False)
    browser_parser.add_argument(
        "-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false"
    )
    parser.set_defaults(browser=True)
    args = parser.parse_args()

    # Change to the directory where the script is located,
    # so that the script can be run from any location.
    os.chdir(Path(__file__).resolve().parent)

    serve(args.root, args.port, args.browser)


/**************************************************************************/
/*  http_client_web.h                                                     */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#ifndef HTTP_CLIENT_WEB_H
#define HTTP_CLIENT_WEB_H

#include "core/io/http_client.h"

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>

typedef enum {
	GODOT_JS_FETCH_STATE_REQUESTING = 0,
	GODOT_JS_FETCH_STATE_BODY = 1,
	GODOT_JS_FETCH_STATE_DONE = 2,
	GODOT_JS_FETCH_STATE_ERROR = -1,
} godot_js_fetch_state_t;

extern int godot_js_fetch_create(const char *p_method, const char *p_url, const char **p_headers, int p_headers_len, const uint8_t *p_body, int p_body_len);
extern int godot_js_fetch_read_headers(int p_id, void (*parse_callback)(int p_size, const char **p_headers, void *p_ref), void *p_ref);
extern int godot_js_fetch_read_chunk(int p_id, uint8_t *p_buf, int p_buf_size);
extern void godot_js_fetch_free(int p_id);
extern godot_js_fetch_state_t godot_js_fetch_state_get(int p_id);
extern int godot_js_fetch_http_status_get(int p_id);
extern int godot_js_fetch_is_chunked(int p_id);

#ifdef __cplusplus
}
#endif

class HTTPClientWeb : public HTTPClient {
private:
	int js_id = 0;
	Status status = STATUS_DISCONNECTED;

	// 64 KiB by default (favors fast download speeds at the cost of memory usage).
	int read_limit = 65536;

	String host;
	int port = -1;
	bool use_tls = false;

	int polled_response_code = 0;
	Vector<String> response_headers;
	Vector<uint8_t> response_buffer;

#ifdef DEBUG_ENABLED
	uint64_t last_polling_frame = 0;
#endif

	static void _parse_headers(int p_len, const char **p_headers, void *p_ref);

public:
	static HTTPClient *_create_func(bool p_notify_postinitialize);

	Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;

	Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) override;
	void set_connection(const Ref<StreamPeer> &p_connection) override;
	Ref<StreamPeer> get_connection() const override;
	void close() override;
	Status get_status() const override;
	bool has_response() const override;
	bool is_response_chunked() const override;
	int get_response_code() const override;
	Error get_response_headers(List<String> *r_response) override;
	int64_t get_response_body_length() const override;
	PackedByteArray read_response_body_chunk() override;
	void set_blocking_mode(bool p_enable) override;
	bool is_blocking_mode_enabled() const override;
	void set_read_chunk_size(int p_size) override;
	int get_read_chunk_size() const override;
	Error poll() override;
	HTTPClientWeb();
	~HTTPClientWeb();
};

#endif // HTTP_CLIENT_WEB_H


const Features = {
	/**
	 * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
	 *
	 * @param {number=} [majorVersion=1] The major WebGL version to check for.
	 * @returns {boolean} If the given major version of WebGL is available.
	 * @function Engine.isWebGLAvailable
	 */
	isWebGLAvailable: function (majorVersion = 1) {
		try {
			return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
		} catch (e) { /* Not available */ }
		return false;
	},

	/**
	 * Check whether the Fetch API available and supports streaming responses.
	 *
	 * @returns {boolean} If the Fetch API is available and supports streaming responses.
	 * @function Engine.isFetchAvailable
	 */
	isFetchAvailable: function () {
		return 'fetch' in window && 'Response' in window && 'body' in window.Response.prototype;
	},

	/**
	 * Check whether the engine is running in a Secure Context.
	 *
	 * @returns {boolean} If the engine is running in a Secure Context.
	 * @function Engine.isSecureContext
	 */
	isSecureContext: function () {
		return window['isSecureContext'] === true;
	},

	/**
	 * Check whether the engine is cross origin isolated.
	 * This value is dependent on Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers sent by the server.
	 *
	 * @returns {boolean} If the engine is running in a Secure Context.
	 * @function Engine.isSecureContext
	 */
	isCrossOriginIsolated: function () {
		return window['crossOriginIsolated'] === true;
	},

	/**
	 * Check whether SharedBufferArray is available.
	 *
	 * Most browsers require the page to be running in a secure context, and the
	 * the server to provide specific CORS headers for SharedArrayBuffer to be available.
	 *
	 * @returns {boolean} If SharedArrayBuffer is available.
	 * @function Engine.isSharedArrayBufferAvailable
	 */
	isSharedArrayBufferAvailable: function () {
		return 'SharedArrayBuffer' in window;
	},

	/**
	 * Check whether the AudioContext supports AudioWorkletNodes.
	 *
	 * @returns {boolean} If AudioWorkletNode is available.
	 * @function Engine.isAudioWorkletAvailable
	 */
	isAudioWorkletAvailable: function () {
		return 'AudioContext' in window && 'audioWorklet' in AudioContext.prototype;
	},

	/**
	 * Return an array of missing required features (as string).
	 *
	 * @returns {Array<string>} A list of human-readable missing features.
	 * @function Engine.getMissingFeatures
	 * @param {{threads: (boolean|undefined)}} supportedFeatures
	 */
	getMissingFeatures: function (supportedFeatures = {}) {
		const {
			// Quotes are needed for the Closure compiler.
			'threads': supportsThreads = true,
		} = supportedFeatures;

		const missing = [];
		if (!Features.isWebGLAvailable(2)) {
			missing.push('WebGL2 - Check web browser configuration and hardware support');
		}
		if (!Features.isFetchAvailable()) {
			missing.push('Fetch - Check web browser version');
		}
		if (!Features.isSecureContext()) {
			missing.push('Secure Context - Check web server configuration (use HTTPS)');
		}

		if (supportsThreads) {
			if (!Features.isCrossOriginIsolated()) {
				missing.push('Cross-Origin Isolation - Check that the web server configuration sends the correct headers.');
			}
			if (!Features.isSharedArrayBufferAvailable()) {
				missing.push('SharedArrayBuffer - Check that the web server configuration sends the correct headers.');
			}
		}

		// Audio is normally optional since we have a dummy fallback.
		return missing;
	},
};


<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorExportPlatformWeb" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Exporter for the Web.
	</brief_description>
	<description>
		The Web exporter customizes how a web build is handled. In the editor's "Export" window, it is created when adding a new "Web" preset.
		[b]Note:[/b] Godot on Web is rendered inside a [code]<canvas>[/code] tag. Normally, the canvas cannot be positioned or resized manually, but otherwise acts as the main [Window] of the application.
	</description>
	<tutorials>
		<link title="Exporting for the Web">$DOCS_URL/tutorials/export/exporting_for_web.html</link>
		<link title="Web documentation index">$DOCS_URL/tutorials/platform/web/index.html</link>
	</tutorials>
	<members>
		<member name="custom_template/debug" type="String" setter="" getter="">
			File path to the custom export template used for debug builds. If left empty, the default template is used.
		</member>
		<member name="custom_template/release" type="String" setter="" getter="">
			File path to the custom export template used for release builds. If left empty, the default template is used.
		</member>
		<member name="html/canvas_resize_policy" type="int" setter="" getter="">
			Determines how the canvas should be resized by Godot.
			- [b]None:[/b] The canvas is not automatically resized.
			- [b]Project:[/b] The size of the canvas is dependent on the [ProjectSettings].
			- [b]Adaptive:[/b] The canvas is automatically resized to fit as much of the web page as possible.
		</member>
		<member name="html/custom_html_shell" type="String" setter="" getter="">
			The custom HTML page that wraps the exported web build. If left empty, the default HTML shell is used.
			For more information, see the [url=$DOCS_URL/tutorials/platform/web/customizing_html5_shell.html]Customizing HTML5 Shell[/url] tutorial.
		</member>
		<member name="html/experimental_virtual_keyboard" type="bool" setter="" getter="" experimental="">
			If [code]true[/code], embeds support for a virtual keyboard into the web page, which is shown when necessary on touchscreen devices.
		</member>
		<member name="html/export_icon" type="bool" setter="" getter="">
			If [code]true[/code], the project icon will be used as the favicon for this application's web page.
		</member>
		<member name="html/focus_canvas_on_start" type="bool" setter="" getter="">
			If [code]true[/code], the canvas will be focused as soon as the application is loaded, if the browser window is already in focus.
		</member>
		<member name="html/head_include" type="String" setter="" getter="">
			Additional HTML tags to include inside the [code]<head>[/code], such as [code]<meta>[/code] tags.
			[b]Note:[/b] You do not need to add a [code]<title>[/code] tag, as it is automatically included based on the project's name.
		</member>
		<member name="progressive_web_app/background_color" type="Color" setter="" getter="">
			The background color used behind the web application.
		</member>
		<member name="progressive_web_app/display" type="int" setter="" getter="">
			The [url=https://developer.mozilla.org/en-US/docs/Web/Manifest/display/]display mode[/url] to use for this progressive web application. Different browsers and platforms may not behave the same.
			- [b]Fullscreen:[/b] Displays the app in fullscreen and hides all of the browser's UI elements.
			- [b]Standalone:[/b] Displays the app in a separate window and hides all of the browser's UI elements.
			- [b]Minimal UI:[/b] Displays the app in a separate window and only shows the browser's UI elements for navigation.
			- [b]Browser:[/b] Displays the app as a normal web page.
		</member>
		<member name="progressive_web_app/enabled" type="bool" setter="" getter="">
			If [code]true[/code], turns this web build into a [url=https://en.wikipedia.org/wiki/Progressive_web_app]progressive web application[/url] (PWA).
		</member>
		<member name="progressive_web_app/ensure_cross_origin_isolation_headers" type="bool" setter="" getter="">
			When enabled, the progressive web app will make sure that each request has cross-origin isolation headers (COEP/COOP).
			This can simplify the setup to serve the exported game.
		</member>
		<member name="progressive_web_app/icon_144x144" type="String" setter="" getter="">
			File path to the smallest icon for this web application. If not defined, defaults to the project icon.
			[b]Note:[/b] If the icon is not 144x144, it will be automatically resized for the final build.
		</member>
		<member name="progressive_web_app/icon_180x180" type="String" setter="" getter="">
			File path to the small icon for this web application. If not defined, defaults to the project icon.
			[b]Note:[/b] If the icon is not 180x180, it will be automatically resized for the final build.
		</member>
		<member name="progressive_web_app/icon_512x512" type="String" setter="" getter="">
			File path to the smallest icon for this web application. If not defined, defaults to the project icon.
			[b]Note:[/b] If the icon is not 512x512, it will be automatically resized for the final build.
		</member>
		<member name="progressive_web_app/offline_page" type="String" setter="" getter="">
			The page to display, should the server hosting the page not be available. This page is saved in the client's machine.
		</member>
		<member name="progressive_web_app/orientation" type="int" setter="" getter="">
			The orientation to use when the web application is run through a mobile device.
			- [b]Any:[/b] No orientation is forced.
			- [b]Landscape:[/b] Forces a horizontal layout (wider than it is taller).
			- [b]Portrait:[/b] Forces a vertical layout (taller than it is wider).
		</member>
		<member name="variant/extensions_support" type="bool" setter="" getter="">
			If [code]true[/code] enables [GDExtension] support for this web build.
		</member>
		<member name="variant/thread_support" type="bool" setter="" getter="">
			If [code]true[/code], the exported game will support threads. It requires [url=https://web.dev/articles/coop-coep]a "cross-origin isolated" website[/url], which may be difficult to set up and is limited for security reasons (such as not being able to communicate with third-party websites).
			If [code]false[/code], the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on an HTTPS website.
		</member>
		<member name="vram_texture_compression/for_desktop" type="bool" setter="" getter="">
			If [code]true[/code], allows textures to be optimized for desktop through the S3TC algorithm.
		</member>
		<member name="vram_texture_compression/for_mobile" type="bool" setter="" getter="">
			If [code]true[/code] allows textures to be optimized for mobile through the ETC2 algorithm.
		</member>
	</members>
</class>


boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
		return handleMouseEvent(event, eventActionOverride, event.getButtonState(), doubleTap);
	}

	boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) {
		final float x = event.getX();
		final float y = event.getY();

		final float pressure = event.getPressure();

		float verticalFactor = 0;
		float horizontalFactor = 0;

		// If event came from RotaryEncoder (Bezel or Crown rotate event on Wear OS smart watches),
		// convert it to mouse wheel event.
		if (event.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER)) {
			if (rotaryInputAxis == ROTARY_INPUT_HORIZONTAL_AXIS) {
				horizontalFactor = -event.getAxisValue(MotionEvent.AXIS_SCROLL);
			} else {
				// If rotaryInputAxis is not ROTARY_INPUT_HORIZONTAL_AXIS then use default ROTARY_INPUT_VERTICAL_AXIS axis.
				verticalFactor = -event.getAxisValue(MotionEvent.AXIS_SCROLL);
			}
		} else {
			verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
			horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
		}
		boolean sourceMouseRelative = false;
		if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
			sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
		}
		return handleMouseEvent(eventActionOverride, buttonMaskOverride, x, y, horizontalFactor, verticalFactor, doubleTap, sourceMouseRelative, pressure, getEventTiltX(event), getEventTiltY(event));
	}

	boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) {
		return handleMouseEvent(eventAction, 0, 0f, 0f, 0f, 0f, false, sourceMouseRelative, 1f, 0f, 0f);
	}

	boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return false;
		}

		// Fix the buttonsMask
		switch (eventAction) {
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				// Zero-up the button state
				buttonsMask = 0;
				break;
			case MotionEvent.ACTION_DOWN:
			case MotionEvent.ACTION_MOVE:
				if (buttonsMask == 0) {
					buttonsMask = MotionEvent.BUTTON_PRIMARY;
				}
				break;
		}

		// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
		// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
		// stream of events to the engine.
		switch (eventAction) {
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_DOWN:
			case MotionEvent.ACTION_HOVER_ENTER:
			case MotionEvent.ACTION_HOVER_EXIT:
			case MotionEvent.ACTION_HOVER_MOVE:
			case MotionEvent.ACTION_MOVE:
			case MotionEvent.ACTION_SCROLL: {
				runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
				dispatchInputEventRunnable(runnable);
				return true;
			}
		}
		return false;
	}

	boolean handleTouchEvent(final MotionEvent event) {
		return handleTouchEvent(event, event.getActionMasked());
	}

	boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) {
		return handleTouchEvent(event, eventActionOverride, false);
	}

	boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
		if (event.getPointerCount() == 0) {
			return true;
		}

		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return false;
		}

		switch (eventActionOverride) {
			case MotionEvent.ACTION_DOWN:
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_MOVE:
			case MotionEvent.ACTION_POINTER_UP:
			case MotionEvent.ACTION_POINTER_DOWN: {
				runnable.setTouchEvent(event, eventActionOverride, doubleTap);
				dispatchInputEventRunnable(runnable);
				return true;
			}
		}
		return false;
	}

	void handleMagnifyEvent(float x, float y, float factor) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setMagnifyEvent(x, y, factor);
		dispatchInputEventRunnable(runnable);
	}

	void handlePanEvent(float x, float y, float deltaX, float deltaY) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setPanEvent(x, y, deltaX, deltaY);
		dispatchInputEventRunnable(runnable);
	}

	private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setJoystickButtonEvent(device, button, pressed);
		dispatchInputEventRunnable(runnable);
	}

	private void handleJoystickAxisEvent(int device, int axis, float value) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setJoystickAxisEvent(device, axis, value);
		dispatchInputEventRunnable(runnable);
	}

	private void handleJoystickHatEvent(int device, int hatX, int hatY) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setJoystickHatEvent(device, hatX, hatY);
		dispatchInputEventRunnable(runnable);
	}

	private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setJoystickConnectionChangedEvent(device, connected, name);
		dispatchInputEventRunnable(runnable);
	}

	void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo);
		dispatchInputEventRunnable(runnable);
	}

	private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) {
		if (shouldDispatchInputToRenderThread()) {
			godot.runOnRenderThread(runnable);
		} else {
			runnable.run();
		}
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		final float[] values = event.values;
		if (values == null || values.length != 3) {
			return;
		}

		InputEventRunnable runnable = InputEventRunnable.obtain();
		if (runnable == null) {
			return;
		}

		float rotatedValue0 = 0f;
		float rotatedValue1 = 0f;
		float rotatedValue2 = 0f;
		switch (windowManager.getDefaultDisplay().getRotation()) {
			case Surface.ROTATION_0:
				rotatedValue0 = values[0];
				rotatedValue1 = values[1];
				rotatedValue2 = values[2];
				break;

			case Surface.ROTATION_90:
				rotatedValue0 = -values[1];
				rotatedValue1 = values[0];
				rotatedValue2 = values[2];
				break;

			case Surface.ROTATION_180:
				rotatedValue0 = -values[0];
				rotatedValue1 = -values[1];
				rotatedValue2 = values[2];
				break;

			case Surface.ROTATION_270:
				rotatedValue0 = values[1];
				rotatedValue1 = -values[0];
				rotatedValue2 = values[2];
				break;
		}

		runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2);
		godot.runOnRenderThread(runnable);
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}


{
	const Vector3 vector_x = Vector3(1, 0, 0);
	const Vector3 vector_y = Vector3(0, 1, 0);
	const Vector3 vector_z = Vector3(0, 0, 1);
	const Vector3 a = Vector3(3.5, 8.5, 2.3);
	const Vector3 b = Vector3(5.2, 4.6, 7.8);
	CHECK_MESSAGE(
			vector_x.cross(vector_y) == vector_z,
			"Vector3 cross product of X and Y should give Z.");
	CHECK_MESSAGE(
			vector_y.cross(vector_x) == -vector_z,
			"Vector3 cross product of Y and X should give negative Z.");
	CHECK_MESSAGE(
			vector_y.cross(vector_z) == vector_x,
			"Vector3 cross product of Y and Z should give X.");
	CHECK_MESSAGE(
			vector_z.cross(vector_x) == vector_y,
			"Vector3 cross product of Z and X should give Y.");
	CHECK_MESSAGE(
			a.cross(b).is_equal_approx(Vector3(55.72, -15.34, -28.1)),
			"Vector3 cross should return expected value.");
	CHECK_MESSAGE(
			Vector3(-a.x, a.y, -a.z).cross(Vector3(b.x, -b.y, b.z)).is_equal_approx(Vector3(55.72, 15.34, -28.1)),
			"Vector2 cross should return expected value.");

	CHECK_MESSAGE(
			vector_x.dot(vector_y) == 0.0,
			"Vector3 dot product of perpendicular vectors should be zero.");
	CHECK_MESSAGE(
			vector_x.dot(vector_x) == 1.0,
			"Vector3 dot product of identical unit vectors should be one.");
	CHECK_MESSAGE(
			(vector_x * 10).dot(vector_x * 10) == 100.0,
			"Vector3 dot product of same direction vectors should behave as expected.");
	CHECK_MESSAGE(
			a.dot(b) == doctest::Approx((real_t)75.24),
			"Vector3 dot should return expected value.");
	CHECK_MESSAGE(
			Vector3(-a.x, a.y, -a.z).dot(Vector3(b.x, -b.y, b.z)) == doctest::Approx((real_t)-75.24),
			"Vector3 dot should return expected value.");
}

TEST_CASE("[Vector3] Finite number checks") {
	const double infinite[] = { NAN, INFINITY, -INFINITY };

	CHECK_MESSAGE(
			Vector3(0, 1, 2).is_finite(),
			"Vector3(0, 1, 2) should be finite");

	for (double x : infinite) {
		CHECK_FALSE_MESSAGE(
				Vector3(x, 1, 2).is_finite(),
				"Vector3 with one component infinite should not be finite.");
		CHECK_FALSE_MESSAGE(
				Vector3(0, x, 2).is_finite(),
				"Vector3 with one component infinite should not be finite.");
		CHECK_FALSE_MESSAGE(
				Vector3(0, 1, x).is_finite(),
				"Vector3 with one component infinite should not be finite.");
	}

	for (double x : infinite) {
		for (double y : infinite) {
			CHECK_FALSE_MESSAGE(
					Vector3(x, y, 2).is_finite(),
					"Vector3 with two components infinite should not be finite.");
			CHECK_FALSE_MESSAGE(
					Vector3(x, 1, y).is_finite(),
					"Vector3 with two components infinite should not be finite.");
			CHECK_FALSE_MESSAGE(
					Vector3(0, x, y).is_finite(),
					"Vector3 with two components infinite should not be finite.");
		}
	}

	for (double x : infinite) {
		for (double y : infinite) {
			for (double z : infinite) {
				CHECK_FALSE_MESSAGE(
						Vector3(x, y, z).is_finite(),
						"Vector3 with three components infinite should not be finite.");
			}
		}
	}
}

} // namespace TestVector3

#endif // TEST_VECTOR3_H


{
	// Arbitrary Euler angles.
	Vector3 euler_yxz(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
	// Basis vectors from online calculation of rotation matrix.
	Vector3 i_unit(0.5545787, 0.1823950, 0.8118957);
	Vector3 j_unit(-0.5249245, 0.8337420, 0.1712555);
	Vector3 k_unit(-0.6456754, -0.5211586, 0.5581192);
	// Quaternion from online calculation.
	Quaternion q_calc(0.2016913, -0.4245716, 0.206033, 0.8582598);
	// Quaternion from local calculation.
	Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34));
	// Quaternion from Euler angles constructor.
	Quaternion q_euler = Quaternion::from_euler(euler_yxz);
	CHECK(q_calc.is_equal_approx(q_local));
	CHECK(q_local.is_equal_approx(q_euler));

	// Calculate Basis and construct Quaternion.
	// When this is written, C++ Basis class does not construct from basis vectors.
	// This is by design, but may be subject to change.
	// Workaround by constructing Basis from Euler angles.
	// basis_axes = Basis(i_unit, j_unit, k_unit);
	Basis basis_axes = Basis::from_euler(euler_yxz);
	Quaternion q(basis_axes);

	CHECK(basis_axes.get_column(0).is_equal_approx(i_unit));
	CHECK(basis_axes.get_column(1).is_equal_approx(j_unit));
	CHECK(basis_axes.get_column(2).is_equal_approx(k_unit));

	CHECK(q.is_equal_approx(q_calc));
	CHECK_FALSE(q.inverse().is_equal_approx(q_calc));
	CHECK(q.is_equal_approx(q_local));
	CHECK(q.is_equal_approx(q_euler));
	CHECK(q[0] == doctest::Approx(0.2016913));
	CHECK(q[1] == doctest::Approx(-0.4245716));
	CHECK(q[2] == doctest::Approx(0.206033));
	CHECK(q[3] == doctest::Approx(0.8582598));
}

TEST_CASE("[Quaternion] Get Euler Orders") {
	double x = Math::deg_to_rad(30.0);
	double y = Math::deg_to_rad(45.0);
	double z = Math::deg_to_rad(10.0);
	Vector3 euler(x, y, z);
	for (int i = 0; i < 6; i++) {
		EulerOrder order = (EulerOrder)i;
		Basis basis = Basis::from_euler(euler, order);
		Quaternion q = Quaternion(basis);
		Vector3 check = q.get_euler(order);
		CHECK_MESSAGE(check.is_equal_approx(euler),
				"Quaternion get_euler method should return the original angles.");
		CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)),
				"Quaternion get_euler method should behave the same as Basis get_euler.");
	}
}

TEST_CASE("[Quaternion] Product (book)") {
	// Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108.
	Quaternion p(1.0, -2.0, 1.0, 3.0);
	Quaternion q(-1.0, 2.0, 3.0, 2.0);

	Quaternion pq = p * q;
	CHECK(pq[0] == doctest::Approx(-9.0));
	CHECK(pq[1] == doctest::Approx(-2.0));
	CHECK(pq[2] == doctest::Approx(11.0));
	CHECK(pq[3] == doctest::Approx(8.0));
}

TEST_CASE("[Quaternion] Product") {
	double yaw = Math::deg_to_rad(45.0);
	double pitch = Math::deg_to_rad(30.0);
	double roll = Math::deg_to_rad(10.0);

	Vector3 euler_y(0.0, yaw, 0.0);
	Quaternion q_y = Quaternion::from_euler(euler_y);
	CHECK(q_y[0] == doctest::Approx(0.0));
	CHECK(q_y[1] == doctest::Approx(0.382684));
	CHECK(q_y[2] == doctest::Approx(0.0));
	CHECK(q_y[3] == doctest::Approx(0.923879));

	Vector3 euler_p(pitch, 0.0, 0.0);
	Quaternion q_p = Quaternion::from_euler(euler_p);
	CHECK(q_p[0] == doctest::Approx(0.258819));
	CHECK(q_p[1] == doctest::Approx(0.0));
	CHECK(q_p[2] == doctest::Approx(0.0));
	CHECK(q_p[3] == doctest::Approx(0.965926));

	Vector3 euler_r(0.0, 0.0, roll);
	Quaternion q_r = Quaternion::from_euler(euler_r);
	CHECK(q_r[0] == doctest::Approx(0.0));
	CHECK(q_r[1] == doctest::Approx(0.0));
	CHECK(q_r[2] == doctest::Approx(0.0871558));
	CHECK(q_r[3] == doctest::Approx(0.996195));

	// Test ZYX dynamic-axes since test data is available online.
	// Rotate first about X axis, then new Y axis, then new Z axis.
	// (Godot uses YXZ Yaw-Pitch-Roll order).
	Quaternion q_yp = q_y * q_p;
	CHECK(q_yp[0] == doctest::Approx(0.239118));
	CHECK(q_yp[1] == doctest::Approx(0.369644));
	CHECK(q_yp[2] == doctest::Approx(-0.099046));
	CHECK(q_yp[3] == doctest::Approx(0.892399));

	Quaternion q_ryp = q_r * q_yp;
	CHECK(q_ryp[0] == doctest::Approx(0.205991));
	CHECK(q_ryp[1] == doctest::Approx(0.389078));
	CHECK(q_ryp[2] == doctest::Approx(-0.0208912));
	CHECK(q_ryp[3] == doctest::Approx(0.897636));
}

TEST_CASE("[Quaternion] xform unit vectors")


/**************************************************************************/
/*  test_button.h                                                         */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#ifndef TEST_BUTTON_H
#define TEST_BUTTON_H

#include "scene/gui/button.h"
#include "scene/main/window.h"

#include "tests/test_macros.h"

namespace TestButton {
TEST_CASE("[SceneTree][Button] is_hovered()") {
	// Create new button instance.
	Button *button = memnew(Button);
	CHECK(button != nullptr);
	Window *root = SceneTree::get_singleton()->get_root();
	root->add_child(button);

	// Set up button's size and position.
	button->set_size(Size2i(50, 50));
	button->set_position(Size2i(10, 10));

	// Button should initially be not hovered.
	CHECK(button->is_hovered() == false);

	// Simulate mouse entering the button.
	SEND_GUI_MOUSE_MOTION_EVENT(Point2i(25, 25), MouseButtonMask::NONE, Key::NONE);
	CHECK(button->is_hovered() == true);

	// Simulate mouse exiting the button.
	SEND_GUI_MOUSE_MOTION_EVENT(Point2i(150, 150), MouseButtonMask::NONE, Key::NONE);
	CHECK(button->is_hovered() == false);

	memdelete(button);
}

} //namespace TestButton
#endif // TEST_BUTTON_H


{
		List<Node *> nodes;
		SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
		CHECK(nodes.is_empty());

		SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes);
		CHECK(nodes.is_empty());

		node1->add_to_group("nodes");
		node2->add_to_group("other_nodes");
		node1_1->add_to_group("nodes");
		node1_1->add_to_group("other_nodes");

		SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
		CHECK_EQ(nodes.size(), 2);

		List<Node *>::Element *E = nodes.front();
		CHECK_EQ(E->get(), node1);
		E = E->next();
		CHECK_EQ(E->get(), node1_1);

		// Clear and try again with the other group.
		nodes.clear();

		SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes);
		CHECK_EQ(nodes.size(), 2);

		E = nodes.front();
		CHECK_EQ(E->get(), node1_1);
		E = E->next();
		CHECK_EQ(E->get(), node2);

		// Clear and try again with the other group and one node removed.
		nodes.clear();

		node1->remove_from_group("nodes");
		SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
		CHECK_EQ(nodes.size(), 1);

		E = nodes.front();
		CHECK_EQ(E->get(), node1_1);
	}

	SUBCASE("Nodes added as siblings of another node should be right next to it") {
		node1->remove_child(node1_1);

		node1->add_sibling(node1_1);

		CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 3);
		CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);

		CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(0), node1);
		CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(1), node1_1);
		CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(2), node2);
	}

	SUBCASE("Replaced nodes should be be removed and the replacing node added") {
		SceneTree::get_singleton()->get_root()->remove_child(node2);

		node1->replace_by(node2);

		CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
		CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3);

		CHECK_FALSE(node1->is_inside_tree());
		CHECK(node2->is_inside_tree());

		CHECK_EQ(node1->get_parent(), nullptr);
		CHECK_EQ(node2->get_parent(), SceneTree::get_singleton()->get_root());
		CHECK_EQ(node2->get_child_count(), 1);
		CHECK_EQ(node2->get_child(0), node1_1);
	}

	SUBCASE("Replacing nodes should keep the groups of the replaced nodes") {
		SceneTree::get_singleton()->get_root()->remove_child(node2);

		node1->add_to_group("nodes");
		node1->replace_by(node2, true);

		List<Node *> nodes;
		SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
		CHECK_EQ(nodes.size(), 1);

		List<Node *>::Element *E = nodes.front();
		CHECK_EQ(E->get(), node2);
	}

	SUBCASE("Duplicating a node should also duplicate the children") {
		node1->set_name("MyName1");
		node1_1->set_name("MyName1_1");
		Node *duplicate1 = node1->duplicate();

		CHECK_EQ(duplicate1->get_child_count(), node1->get_child_count());
		Node *duplicate1_1 = duplicate1->get_child(0);

		CHECK_EQ(duplicate1_1->get_child_count(), node1_1->get_child_count());

		CHECK_EQ(duplicate1->get_name(), node1->get_name());
		CHECK_EQ(duplicate1_1->get_name(), node1_1->get_name());

		CHECK_FALSE(duplicate1->is_inside_tree());
		CHECK_FALSE(duplicate1_1->is_inside_tree());

		memdelete(duplicate1_1);
		memdelete(duplicate1);
	}

	memdelete(node1_1);
	memdelete(node1);
	memdelete(node2);
}

TEST_CASE("[SceneTree][Node]Exported node checks") {
	TestNode *node = memnew(TestNode);
	SceneTree::get_singleton()->get_root()->add_child(node);

	Node *child = memnew(Node);
	child->set_name("Child");
	node->add_child(child);
	child->set_owner(node);

	Node *child2 = memnew(Node);
	child2->set_name("Child2");
	node->add_child(child2);
	child2->set_owner(node);

	Array children;
	children.append(child);

	node->set("exported_node", child);
	node->set("exported_nodes", children);

	SUBCASE("Property of duplicated node should point to duplicated child") {
		GDREGISTER_CLASS(TestNode);

		TestNode *dup = Object::cast_to<TestNode>(node->duplicate());
		Node *new_exported = Object::cast_to<Node>(dup->get("exported_node"));
		CHECK(new_exported == dup->get_child(0));

		memdelete(dup);
	}

#ifdef TOOLS_ENABLED
	SUBCASE("Saving instance with exported nodes should not store the unchanged property") {
		Ref<PackedScene> ps;
		ps.instantiate();
		ps->pack(node);

		String scene_path = TestUtils::get_temp_path("test_scene.tscn");
		ps->set_path(scene_path);

		Node *root = memnew(Node);

		Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
		root->add_child(sub_child);
		sub_child->set_owner(root);

		Ref<PackedScene> ps2;
		ps2.instantiate();
		ps2->pack(root);

		scene_path = TestUtils::get_temp_path("new_test_scene.tscn");
		ResourceSaver::save(ps2, scene_path);
		memdelete(root);

		bool is_wrong = false;
		Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
		while (!fa->eof_reached()) {
			const String line = fa->get_line();
			if (line.begins_with("exported_node")) {
				// The property was saved, while it shouldn't.
				is_wrong = true;
				break;
			}
		}
		CHECK_FALSE(is_wrong);
	}

	SUBCASE("Saving instance with exported nodes should store property if changed") {
		Ref<PackedScene> ps;
		ps.instantiate();
		ps->pack(node);

		String scene_path = TestUtils::get_temp_path("test_scene.tscn");
		ps->set_path(scene_path);

		Node *root = memnew(Node);

		Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
		root->add_child(sub_child);
		sub_child->set_owner(root);

		sub_child->set("exported_node", sub_child->get_child(1));

		children = Array();
		children.append(sub_child->get_child(1));
		sub_child->set("exported_nodes", children);

		Ref<PackedScene> ps2;
		ps2.instantiate();
		ps2->pack(root);

		scene_path = TestUtils::get_temp_path("new_test_scene2.tscn");
		ResourceSaver::save(ps2, scene_path);
		memdelete(root);

		int stored_properties = 0;
		Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
		while (!fa->eof_reached()) {
			const String line = fa->get_line();
			if (line.begins_with("exported_node")) {
				stored_properties++;
			}
		}
		CHECK_EQ(stored_properties, 2);
	}
#endif // TOOLS_ENABLED

	memdelete(node);
}

TEST_CASE("[Node] Processing checks")


{
	// Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
	Camera3D *test_camera = memnew(Camera3D);
	SubViewport *mock_viewport = memnew(SubViewport);
	// 4:2.
	mock_viewport->set_size(Vector2(400, 200));
	SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
	mock_viewport->add_child(test_camera);
	test_camera->set_global_position(Vector3(0, 0, 0));
	test_camera->set_global_rotation(Vector3(0, 0, 0));
	test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);

	SUBCASE("project_position") {
		SUBCASE("Orthogonal projection") {
			test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
			// Center.
			CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
			// Top left.
			CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f)));
			// Bottom right.
			CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f)));
		}

		SUBCASE("Perspective projection") {
			test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
			// Center.
			CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
			CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f)));
			// 3/4th way to Top left.
			CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)));
			CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)));
			// 3/4th way to Bottom right.
			CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)));
			CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)));
		}
	}

	// Uses cases that are the inverse of the above sub-case.
	SUBCASE("unproject_position") {
		SUBCASE("Orthogonal projection") {
			test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
			// Center
			CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
			// Top left
			CHECK(test_camera->unproject_position(Vector3(-5.0f, 2.5f, -1.5f)).is_equal_approx(Vector2(0, 0)));
			// Bottom right
			CHECK(test_camera->unproject_position(Vector3(5.0f, -2.5f, -5.0f)).is_equal_approx(Vector2(400, 200)));
		}

		SUBCASE("Perspective projection") {
			test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
			// Center.
			CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
			CHECK(test_camera->unproject_position(Vector3(0, 0, -100.0f)).is_equal_approx(Vector2(200, 100)));
			// 3/4th way to Top left.
			WARN(test_camera->unproject_position(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(100, 50)));
			WARN(test_camera->unproject_position(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(100, 50)));
			// 3/4th way to Bottom right.
			CHECK(test_camera->unproject_position(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(300, 150)));
			CHECK(test_camera->unproject_position(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(300, 150)));
		}
	}

	memdelete(test_camera);
	memdelete(mock_viewport);
}

TEST_CASE("[SceneTree][Camera3D] Project ray")


{
	SubViewport *mock_viewport = memnew(SubViewport);
	Camera2D *test_camera = memnew(Camera2D);

	mock_viewport->set_size(Vector2(400, 200));
	SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
	mock_viewport->add_child(test_camera);

	SUBCASE("Anchor mode") {
		test_camera->set_anchor_mode(Camera2D::ANCHOR_MODE_DRAG_CENTER);
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(0, 0)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));

		test_camera->set_anchor_mode(Camera2D::ANCHOR_MODE_FIXED_TOP_LEFT);
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(200, 100)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));
	}

	SUBCASE("Offset") {
		test_camera->set_offset(Vector2(100, 100));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(100, 100)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));

		test_camera->set_offset(Vector2(-100, 300));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(-100, 300)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));

		test_camera->set_offset(Vector2(0, 0));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(0, 0)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));
	}

	SUBCASE("Limits") {
		test_camera->set_limit(SIDE_LEFT, 100);
		test_camera->set_limit(SIDE_TOP, 50);

		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(300, 150)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));

		test_camera->set_limit(SIDE_LEFT, 0);
		test_camera->set_limit(SIDE_TOP, 0);

		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(200, 100)));
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));
	}

	SUBCASE("Drag") {
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(0, 0)));

		// horizontal
		test_camera->set_drag_horizontal_enabled(true);
		test_camera->set_drag_margin(SIDE_RIGHT, 0.5);

		test_camera->set_position(Vector2(100, 100));
		test_camera->force_update_scroll();
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 100)));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(0, 100)));
		test_camera->set_position(Vector2(101, 101));
		test_camera->force_update_scroll();
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(1, 101)));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(1, 101)));

		// test align
		test_camera->set_position(Vector2(0, 0));
		test_camera->align();
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(0, 0)));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(0, 0)));

		// vertical
		test_camera->set_drag_vertical_enabled(true);
		test_camera->set_drag_horizontal_enabled(false);
		test_camera->set_drag_margin(SIDE_TOP, 0.3);

		test_camera->set_position(Vector2(200, -20));
		test_camera->force_update_scroll();
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(200, 0)));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(200, 0)));
		test_camera->set_position(Vector2(250, -55));
		test_camera->force_update_scroll();
		CHECK(test_camera->get_camera_position().is_equal_approx(Vector2(250, -25)));
		CHECK(test_camera->get_camera_screen_center().is_equal_approx(Vector2(250, -25)));
	}

	memdelete(test_camera);
	memdelete(mock_viewport);
}

TEST_CASE("[SceneTree][Camera2D] Transforms") {
	SubViewport *mock_viewport = memnew(SubViewport);
	Camera2D *test_camera = memnew(Camera2D);

	mock_viewport->set_size(Vector2(400, 200));
	SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
	mock_viewport->add_child(test_camera);

	SUBCASE("Default camera") {
		Transform2D xform = mock_viewport->get_canvas_transform();
		// x,y are basis vectors, origin = screen center
		Transform2D test_xform = Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));
	}

	SUBCASE("Zoom") {
		test_camera->set_zoom(Vector2(0.5, 2));
		Transform2D xform = mock_viewport->get_canvas_transform();
		Transform2D test_xform = Transform2D(Vector2(0.5, 0), Vector2(0, 2), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));

		test_camera->set_zoom(Vector2(10, 10));
		xform = mock_viewport->get_canvas_transform();
		test_xform = Transform2D(Vector2(10, 0), Vector2(0, 10), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));

		test_camera->set_zoom(Vector2(1, 1));
		xform = mock_viewport->get_canvas_transform();
		test_xform = Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));
	}

	SUBCASE("Rotation") {
		test_camera->set_rotation(Math_PI / 2);
		Transform2D xform = mock_viewport->get_canvas_transform();
		Transform2D test_xform = Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));

		test_camera->set_ignore_rotation(false);
		xform = mock_viewport->get_canvas_transform();
		test_xform = Transform2D(Vector2(0, -1), Vector2(1, 0), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));

		test_camera->set_rotation(-1 * Math_PI);
		test_camera->force_update_scroll();
		xform = mock_viewport->get_canvas_transform();
		test_xform = Transform2D(Vector2(-1, 0), Vector2(0, -1), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));

		test_camera->set_rotation(0);
		test_camera->force_update_scroll();
		xform = mock_viewport->get_canvas_transform();
		test_xform = Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(200, 100));
		CHECK(xform.is_equal_approx(test_xform));
	}

	memdelete(test_camera);
	memdelete(mock_viewport);
}

} // namespace TestCamera2D

#endif // TEST_CAMERA_2D_H


{
			SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
			SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
			Array signal_args;
			signal_args.push_back(Array());

			node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);

			// Move to Control node_j.
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Change node_i to MOUSE_FILTER_IGNORE. node_i should receive Mouse Exit.
			node_i->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK_FALSE(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);

			// Change node_i to MOUSE_FILTER_PASS. node_i should receive Mouse Enter.
			node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Change node_j to MOUSE_FILTER_IGNORE. After updating the mouse motion, node_i should now have mouse_over_self.
			node_j->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK(node_i->mouse_over_self);
			CHECK_FALSE(node_j->mouse_over);
			CHECK_FALSE(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Change node_j to MOUSE_FILTER_PASS. After updating the mouse motion, node_j should now have mouse_over_self.
			node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			CHECK_FALSE(node_h->invalid_order);
			CHECK_FALSE(node_i->invalid_order);
			CHECK_FALSE(node_j->invalid_order);

			node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);

			SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
			SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
		}

		SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when removing the hovered Control.") {
			SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
			SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
			Array signal_args;
			signal_args.push_back(Array());

			node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);

			// Move to Control node_j.
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Remove node_i from the tree. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
			node_h->remove_child(node_i);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK_FALSE(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK_FALSE(node_j->mouse_over);
			CHECK_FALSE(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Add node_i to the tree and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
			node_h->add_child(node_i);
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			CHECK_FALSE(node_h->invalid_order);
			CHECK_FALSE(node_i->invalid_order);
			CHECK_FALSE(node_j->invalid_order);

			node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);

			SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
			SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
		}

		SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when hiding the hovered Control.") {
			SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
			SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
			Array signal_args;
			signal_args.push_back(Array());

			node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);

			// Move to Control node_j.
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Hide node_i. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
			node_i->hide();
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK_FALSE(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK_FALSE(node_j->mouse_over);
			CHECK_FALSE(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			// Show node_i and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
			node_i->show();
			SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
			CHECK(node_h->mouse_over);
			CHECK_FALSE(node_h->mouse_over_self);
			CHECK(node_i->mouse_over);
			CHECK_FALSE(node_i->mouse_over_self);
			CHECK(node_j->mouse_over);
			CHECK(node_j->mouse_over_self);
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
			SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));

			CHECK_FALSE(node_h->invalid_order);
			CHECK_FALSE(node_i->invalid_order);
			CHECK_FALSE(node_j->invalid_order);

			node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
			node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);

			SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
			SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
		}

		SUBCASE("[Viewport][GuiInputEvent] Window Mouse Enter/Exit signals.")


<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicsBody3D" inherits="CollisionObject3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Abstract base class for 3D game objects affected by physics.
	</brief_description>
	<description>
		[PhysicsBody3D] is an abstract base class for 3D game objects affected by physics. All 3D physics bodies inherit from it.
		[b]Warning:[/b] With a non-uniform scale, this node will likely not behave as expected. It is advised to keep its scale the same on all axes and adjust its collision shape(s) instead.
	</description>
	<tutorials>
		<link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link>
	</tutorials>
	<methods>
		<method name="add_collision_exception_with">
			<return type="void" />
			<param index="0" name="body" type="Node" />
			<description>
				Adds a body to the list of bodies that this body can't collide with.
			</description>
		</method>
		<method name="get_axis_lock" qualifiers="const">
			<return type="bool" />
			<param index="0" name="axis" type="int" enum="PhysicsServer3D.BodyAxis" />
			<description>
				Returns [code]true[/code] if the specified linear or rotational [param axis] is locked.
			</description>
		</method>
		<method name="get_collision_exceptions">
			<return type="PhysicsBody3D[]" />
			<description>
				Returns an array of nodes that were added as collision exceptions for this body.
			</description>
		</method>
		<method name="get_gravity" qualifiers="const">
			<return type="Vector3" />
			<description>
				Returns the gravity vector computed from all sources that can affect the body, including all gravity overrides from [Area3D] nodes and the global world gravity.
			</description>
		</method>
		<method name="move_and_collide">
			<return type="KinematicCollision3D" />
			<param index="0" name="motion" type="Vector3" />
			<param index="1" name="test_only" type="bool" default="false" />
			<param index="2" name="safe_margin" type="float" default="0.001" />
			<param index="3" name="recovery_as_collision" type="bool" default="false" />
			<param index="4" name="max_collisions" type="int" default="1" />
			<description>
				Moves the body along the vector [param motion]. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [param motion] should be computed using [code]delta[/code].
				The body will stop if it collides. Returns a [KinematicCollision3D], which contains information about the collision when stopped, or when touching another body along the motion.
				If [param test_only] is [code]true[/code], the body does not move but the would-be collision information is given.
				[param safe_margin] is the extra margin used for collision recovery (see [member CharacterBody3D.safe_margin] for more details).
				If [param recovery_as_collision] is [code]true[/code], any depenetration from the recovery phase is also reported as a collision; this is used e.g. by [CharacterBody3D] for improving floor detection during floor snapping.
				[param max_collisions] allows to retrieve more than one collision result.
			</description>
		</method>
		<method name="remove_collision_exception_with">
			<return type="void" />
			<param index="0" name="body" type="Node" />
			<description>
				Removes a body from the list of bodies that this body can't collide with.
			</description>
		</method>
		<method name="set_axis_lock">
			<return type="void" />
			<param index="0" name="axis" type="int" enum="PhysicsServer3D.BodyAxis" />
			<param index="1" name="lock" type="bool" />
			<description>
				Locks or unlocks the specified linear or rotational [param axis] depending on the value of [param lock].
			</description>
		</method>
		<method name="test_move" keywords="check, collision, sweep">
			<return type="bool" />
			<param index="0" name="from" type="Transform3D" />
			<param index="1" name="motion" type="Vector3" />
			<param index="2" name="collision" type="KinematicCollision3D" default="null" />
			<param index="3" name="safe_margin" type="float" default="0.001" />
			<param index="4" name="recovery_as_collision" type="bool" default="false" />
			<param index="5" name="max_collisions" type="int" default="1" />
			<description>
				Checks for collisions without moving the body. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [param motion] should be computed using [code]delta[/code].
				Virtually sets the node's position, scale and rotation to that of the given [Transform3D], then tries to move the body along the vector [param motion]. Returns [code]true[/code] if a collision would stop the body from moving along the whole path.
				[param collision] is an optional object of type [KinematicCollision3D], which contains additional information about the collision when stopped, or when touching another body along the motion.
				[param safe_margin] is the extra margin used for collision recovery (see [member CharacterBody3D.safe_margin] for more details).
				If [param recovery_as_collision] is [code]true[/code], any depenetration from the recovery phase is also reported as a collision; this is useful for checking whether the body would [i]touch[/i] any other bodies.
				[param max_collisions] allows to retrieve more than one collision result.
			</description>
		</method>
	</methods>
	<members>
		<member name="axis_lock_angular_x" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's rotation in the X axis.
		</member>
		<member name="axis_lock_angular_y" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's rotation in the Y axis.
		</member>
		<member name="axis_lock_angular_z" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's rotation in the Z axis.
		</member>
		<member name="axis_lock_linear_x" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's linear movement in the X axis.
		</member>
		<member name="axis_lock_linear_y" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's linear movement in the Y axis.
		</member>
		<member name="axis_lock_linear_z" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
			Lock the body's linear movement in the Z axis.
		</member>
	</members>
</class>


<?xml version="1.0" encoding="UTF-8" ?>
<class name="BaseButton" inherits="Control" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Abstract base class for GUI buttons.
	</brief_description>
	<description>
		[BaseButton] is an abstract base class for GUI buttons. It doesn't display anything by itself.
	</description>
	<tutorials>
	</tutorials>
	<methods>
		<method name="_pressed" qualifiers="virtual">
			<return type="void" />
			<description>
				Called when the button is pressed. If you need to know the button's pressed state (and [member toggle_mode] is active), use [method _toggled] instead.
			</description>
		</method>
		<method name="_toggled" qualifiers="virtual">
			<return type="void" />
			<param index="0" name="toggled_on" type="bool" />
			<description>
				Called when the button is toggled (only if [member toggle_mode] is active).
			</description>
		</method>
		<method name="get_draw_mode" qualifiers="const">
			<return type="int" enum="BaseButton.DrawMode" />
			<description>
				Returns the visual state used to draw the button. This is useful mainly when implementing your own draw code by either overriding _draw() or connecting to "draw" signal. The visual state of the button is defined by the [enum DrawMode] enum.
			</description>
		</method>
		<method name="is_hovered" qualifiers="const">
			<return type="bool" />
			<description>
				Returns [code]true[/code] if the mouse has entered the button and has not left it yet.
			</description>
		</method>
		<method name="set_pressed_no_signal">
			<return type="void" />
			<param index="0" name="pressed" type="bool" />
			<description>
				Changes the [member button_pressed] state of the button, without emitting [signal toggled]. Use when you just want to change the state of the button without sending the pressed event (e.g. when initializing scene). Only works if [member toggle_mode] is [code]true[/code].
				[b]Note:[/b] This method doesn't unpress other buttons in [member button_group].
			</description>
		</method>
	</methods>
	<members>
		<member name="action_mode" type="int" setter="set_action_mode" getter="get_action_mode" enum="BaseButton.ActionMode" default="1">
			Determines when the button is considered clicked, one of the [enum ActionMode] constants.
		</member>
		<member name="button_group" type="ButtonGroup" setter="set_button_group" getter="get_button_group">
			The [ButtonGroup] associated with the button. Not to be confused with node groups.
			[b]Note:[/b] The button will be configured as a radio button if a [ButtonGroup] is assigned to it.
		</member>
		<member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask" enum="MouseButtonMask" is_bitfield="true" default="1">
			Binary mask to choose which mouse buttons this button will respond to.
			To allow both left-click and right-click, use [code]MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT[/code].
		</member>
		<member name="button_pressed" type="bool" setter="set_pressed" getter="is_pressed" default="false">
			If [code]true[/code], the button's state is pressed. Means the button is pressed down or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is [code]true[/code].
			[b]Note:[/b] Changing the value of [member button_pressed] will result in [signal toggled] to be emitted. If you want to change the pressed state without emitting that signal, use [method set_pressed_no_signal].
		</member>
		<member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false" keywords="enabled">
			If [code]true[/code], the button is in disabled state and can't be clicked or toggled.
		</member>
		<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
		<member name="keep_pressed_outside" type="bool" setter="set_keep_pressed_outside" getter="is_keep_pressed_outside" default="false">
			If [code]true[/code], the button stays pressed when moving the cursor outside the button while pressing it.
			[b]Note:[/b] This property only affects the button's visual appearance. Signals will be emitted at the same moment regardless of this property's value.
		</member>
		<member name="shortcut" type="Shortcut" setter="set_shortcut" getter="get_shortcut">
			[Shortcut] associated to the button.
		</member>
		<member name="shortcut_feedback" type="bool" setter="set_shortcut_feedback" getter="is_shortcut_feedback" default="true">
			If [code]true[/code], the button will highlight for a short amount of time when its shortcut is activated. If [code]false[/code] and [member toggle_mode] is [code]false[/code], the shortcut will activate without any visual feedback.
		</member>
		<member name="shortcut_in_tooltip" type="bool" setter="set_shortcut_in_tooltip" getter="is_shortcut_in_tooltip_enabled" default="true">
			If [code]true[/code], the button will add information about its shortcut in the tooltip.
			[b]Note:[/b] This property does nothing when the tooltip control is customized using [method Control._make_custom_tooltip].
		</member>
		<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" default="false">
			If [code]true[/code], the button is in toggle mode. Makes the button flip state between pressed and unpressed each time its area is clicked.
		</member>
	</members>
	<signals>
		<signal name="button_down">
			<description>
				Emitted when the button starts being held down.
			</description>
		</signal>
		<signal name="button_up">
			<description>
				Emitted when the button stops being held down.
			</description>
		</signal>
		<signal name="pressed">
			<description>
				Emitted when the button is toggled or pressed. This is on [signal button_down] if [member action_mode] is [constant ACTION_MODE_BUTTON_PRESS] and on [signal button_up] otherwise.
				If you need to know the button's pressed state (and [member toggle_mode] is active), use [signal toggled] instead.
			</description>
		</signal>
		<signal name="toggled">
			<param index="0" name="toggled_on" type="bool" />
			<description>
				Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param toggled_on] argument.
			</description>
		</signal>
	</signals>
	<constants>
		<constant name="DRAW_NORMAL" value="0" enum="DrawMode">
			The normal state (i.e. not pressed, not hovered, not toggled and enabled) of buttons.
		</constant>
		<constant name="DRAW_PRESSED" value="1" enum="DrawMode">
			The state of buttons are pressed.
		</constant>
		<constant name="DRAW_HOVER" value="2" enum="DrawMode">
			The state of buttons are hovered.
		</constant>
		<constant name="DRAW_DISABLED" value="3" enum="DrawMode">
			The state of buttons are disabled.
		</constant>
		<constant name="DRAW_HOVER_PRESSED" value="4" enum="DrawMode">
			The state of buttons are both hovered and pressed.
		</constant>
		<constant name="ACTION_MODE_BUTTON_PRESS" value="0" enum="ActionMode">
			Require just a press to consider the button clicked.
		</constant>
		<constant name="ACTION_MODE_BUTTON_RELEASE" value="1" enum="ActionMode">
			Require a press and a subsequent release before considering the button clicked.
		</constant>
	</constants>
</class>


<?xml version="1.0" encoding="UTF-8" ?>
<class name="MeshInstance3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Node that instances meshes into a scenario.
	</brief_description>
	<description>
		MeshInstance3D is a node that takes a [Mesh] resource and adds it to the current scenario by creating an instance of it. This is the class most often used render 3D geometry and can be used to instance a single [Mesh] in many places. This allows reusing geometry, which can save on resources. When a [Mesh] has to be instantiated more than thousands of times at close proximity, consider using a [MultiMesh] in a [MultiMeshInstance3D] instead.
	</description>
	<tutorials>
		<link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link>
		<link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link>
		<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link>
		<link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link>
	</tutorials>


<methods>
		<method name="bake_mesh_from_current_blend_shape_mix">
			<return type="ArrayMesh" />
			<param index="0" name="existing" type="ArrayMesh" default="null" />
			<description>
				Takes a snapshot from the current [ArrayMesh] with all blend shapes applied according to their current weights and bakes it to the provided [param existing] mesh. If no [param existing] mesh is provided a new [ArrayMesh] is created, baked and returned. Mesh surface materials are not copied.
				[b]Performance:[/b] [Mesh] data needs to be received from the GPU, stalling the [RenderingServer] in the process.
			</description>
		</method>
		<method name="bake_mesh_from_current_skeleton_pose">
			<return type="ArrayMesh" />
			<param index="0" name="existing" type="ArrayMesh" default="null" />
			<description>
				Takes a snapshot of the current animated skeleton pose of the skinned mesh and bakes it to the provided [param existing] mesh. If no [param existing] mesh is provided a new [ArrayMesh] is created, baked, and returned. Requires a skeleton with a registered skin to work. Blendshapes are ignored. Mesh surface materials are not copied.
				[b]Performance:[/b] [Mesh] data needs to be retrieved from the GPU, stalling the [RenderingServer] in the process.
			</description>
		</method>
		<method name="create_convex_collision">
			<return type="void" />
			<param index="0" name="clean" type="bool" default="true" />
			<param index="1" name="simplify" type="bool" default="false" />
			<description>
				This helper creates a [StaticBody3D] child node with a [ConvexPolygonShape3D] collision shape calculated from the mesh geometry. It's mainly used for testing.
				If [param clean] is [code]true[/code] (default), duplicate and interior vertices are removed automatically. You can set it to [code]false[/code] to make the process faster if not needed.
				If [param simplify] is [code]true[/code], the geometry can be further simplified to reduce the number of vertices. Disabled by default.
			</description>
		</method>
		<method name="create_debug_tangents">
			<return type="void" />
			<description>
				This helper creates a [MeshInstance3D] child node with gizmos at every vertex calculated from the mesh geometry. It's mainly used for testing.
			</description>
		</method>
		<method name="create_multiple_convex_collisions">
			<return type="void" />
			<param index="0" name="settings" type="MeshConvexDecompositionSettings" default="null" />
			<description>
				This helper creates a [StaticBody3D] child node with multiple [ConvexPolygonShape3D] collision shapes calculated from the mesh geometry via convex decomposition. The convex decomposition operation can be controlled with parameters from the optional [param settings].
			</description>
		</method>
		<method name="create_trimesh_collision">
			<return type="void" />
			<description>
				This helper creates a [StaticBody3D] child node with a [ConcavePolygonShape3D] collision shape calculated from the mesh geometry. It's mainly used for testing.
			</description>
		</method>
		<method name="find_blend_shape_by_name">
			<return type="int" />
			<param index="0" name="name" type="StringName" />
			<description>
				Returns the index of the blend shape with the given [param name]. Returns [code]-1[/code] if no blend shape with this name exists, including when [member mesh] is [code]null[/code].
			</description>
		</method>
		<method name="get_active_material" qualifiers="const">
			<return type="Material" />
			<param index="0" name="surface" type="int" />
			<description>
				Returns the [Material] that will be used by the [Mesh] when drawing. This can return the [member GeometryInstance3D.material_override], the surface override [Material] defined in this [MeshInstance3D], or the surface [Material] defined in the [member mesh]. For example, if [member GeometryInstance3D.material_override] is used, all surfaces will return the override material.
				Returns [code]null[/code] if no material is active, including when [member mesh] is [code]null[/code].
			</description>
		</method>
		<method name="get_blend_shape_count" qualifiers="const">
			<return type="int" />
			<description>
				Returns the number of blend shapes available. Produces an error if [member mesh] is [code]null[/code].
			</description>
		</method>
		<method name="get_blend_shape_value" qualifiers="const">
			<return type="float" />
			<param index="0" name="blend_shape_idx" type="int" />
			<description>
				Returns the value of the blend shape at the given [param blend_shape_idx]. Returns [code]0.0[/code] and produces an error if [member mesh] is [code]null[/code] or doesn't have a blend shape at that index.
			</description>
		</method>
		<method name="get_skin_reference" qualifiers="const">
			<return type="SkinReference" />
			<description>
				Returns the internal [SkinReference] containing the skeleton's [RID] attached to this RID. See also [method Resource.get_rid], [method SkinReference.get_skeleton], and [method RenderingServer.instance_attach_skeleton].
			</description>
		</method>
		<method name="get_surface_override_material" qualifiers="const">
			<return type="Material" />
			<param index="0" name="surface" type="int" />
			<description>
				Returns the override [Material] for the specified [param surface] of the [Mesh] resource. See also [method get_surface_override_material_count].
				[b]Note:[/b] This returns the [Material] associated to the [MeshInstance3D]'s Surface Material Override properties, not the material within the [Mesh] resource. To get the material within the [Mesh] resource, use [method Mesh.surface_get_material] instead.
			</description>
		</method>
		<method name="get_surface_override_material_count" qualifiers="const">
			<return type="int" />
			<description>
				Returns the number of surface override materials. This is equivalent to [method Mesh.get_surface_count]. See also [method get_surface_override_material].
			</description>
		</method>
		<method name="set_blend_shape_value">
			<return type="void" />
			<param index="0" name="blend_shape_idx" type="int" />
			<param index="1" name="value" type="float" />
			<description>
				Sets the value of the blend shape at [param blend_shape_idx] to [param value]. Produces an error if [member mesh] is [code]null[/code] or doesn't have a blend shape at that index.
			</description>
		</method>
		<method name="set_surface_override_material">
			<return type="void" />
			<param index="0" name="surface" type="int" />
			<param index="1" name="material" type="Material" />
			<description>
				Sets the override [param material] for the specified [param surface] of the [Mesh] resource. This material is associated with this [MeshInstance3D] rather than with [member mesh].
				[b]Note:[/b] This assigns the [Material] associated to the [MeshInstance3D]'s Surface Material Override properties, not the material within the [Mesh] resource. To set the material within the [Mesh] resource, use [method Mesh.surface_get_material] instead.
			</description>
		</method>
	</methods>


<?xml version="1.0" encoding="UTF-8" ?>
<class name="Viewport" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Abstract base class for viewports. Encapsulates drawing and interaction with a game world.
	</brief_description>
	<description>
		A [Viewport] creates a different view into the screen, or a sub-view inside another viewport. Child 2D nodes will display on it, and child Camera3D 3D nodes will render on it too.
		Optionally, a viewport can have its own 2D or 3D world, so it doesn't share what it draws with other viewports.
		Viewports can also choose to be audio listeners, so they generate positional audio depending on a 2D or 3D camera child of it.
		Also, viewports can be assigned to different screens in case the devices have multiple screens.
		Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw.
	</description>
	<tutorials>
		<link title="Using Viewports">$DOCS_URL/tutorials/rendering/viewports.html</link>
		<link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link>
		<link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link>
		<link title="3D in 2D Viewport Demo">https://godotengine.org/asset-library/asset/2804</link>
		<link title="2D in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2803</link>
		<link title="Screen Capture Demo">https://godotengine.org/asset-library/asset/2808</link>
		<link title="Dynamic Split Screen Demo">https://godotengine.org/asset-library/asset/2806</link>
		<link title="3D Resolution Scaling Demo">https://godotengine.org/asset-library/asset/2805</link>
	</tutorials>


<methods>
		<method name="find_world_2d" qualifiers="const">
			<return type="World2D" />
			<description>
				Returns the first valid [World2D] for this viewport, searching the [member world_2d] property of itself and any Viewport ancestor.
			</description>
		</method>
		<method name="find_world_3d" qualifiers="const">
			<return type="World3D" />
			<description>
				Returns the first valid [World3D] for this viewport, searching the [member world_3d] property of itself and any Viewport ancestor.
			</description>
		</method>
		<method name="get_audio_listener_2d" qualifiers="const">
			<return type="AudioListener2D" />
			<description>
				Returns the currently active 2D audio listener. Returns [code]null[/code] if there are no active 2D audio listeners, in which case the active 2D camera will be treated as listener.
			</description>
		</method>
		<method name="get_audio_listener_3d" qualifiers="const">
			<return type="AudioListener3D" />
			<description>
				Returns the currently active 3D audio listener. Returns [code]null[/code] if there are no active 3D audio listeners, in which case the active 3D camera will be treated as listener.
			</description>
		</method>
		<method name="get_camera_2d" qualifiers="const">
			<return type="Camera2D" />
			<description>
				Returns the currently active 2D camera. Returns null if there are no active cameras.
			</description>
		</method>
		<method name="get_camera_3d" qualifiers="const">
			<return type="Camera3D" />
			<description>
				Returns the currently active 3D camera.
			</description>
		</method>
		<method name="get_canvas_cull_mask_bit" qualifiers="const">
			<return type="bool" />
			<param index="0" name="layer" type="int" />
			<description>
				Returns an individual bit on the rendering layer mask.
			</description>
		</method>
		<method name="get_embedded_subwindows" qualifiers="const">
			<return type="Window[]" />
			<description>
				Returns a list of the visible embedded [Window]s inside the viewport.
				[b]Note:[/b] [Window]s inside other viewports will not be listed.
			</description>
		</method>
		<method name="get_final_transform" qualifiers="const">
			<return type="Transform2D" />
			<description>
				Returns the transform from the viewport's coordinate system to the embedder's coordinate system.
			</description>
		</method>
		<method name="get_mouse_position" qualifiers="const">
			<return type="Vector2" />
			<description>
				Returns the mouse's position in this [Viewport] using the coordinate system of this [Viewport].
			</description>
		</method>
		<method name="get_positional_shadow_atlas_quadrant_subdiv" qualifiers="const">
			<return type="int" enum="Viewport.PositionalShadowAtlasQuadrantSubdiv" />
			<param index="0" name="quadrant" type="int" />
			<description>
				Returns the positional shadow atlas quadrant subdivision of the specified quadrant.
			</description>
		</method>
		<method name="get_render_info">
			<return type="int" />
			<param index="0" name="type" type="int" enum="Viewport.RenderInfoType" />
			<param index="1" name="info" type="int" enum="Viewport.RenderInfo" />
			<description>
				Returns rendering statistics of the given type. See [enum RenderInfoType] and [enum RenderInfo] for options.
			</description>
		</method>
		<method name="get_screen_transform" qualifiers="const">
			<return type="Transform2D" />
			<description>
				Returns the transform from the Viewport's coordinates to the screen coordinates of the containing window manager window.
			</description>
		</method>
		<method name="get_texture" qualifiers="const">
			<return type="ViewportTexture" />
			<description>
				Returns the viewport's texture.
				[b]Note:[/b] When trying to store the current texture (e.g. in a file), it might be completely black or outdated if used too early, especially when used in e.g. [method Node._ready]. To make sure the texture you get is correct, you can await [signal RenderingServer.frame_post_draw] signal.
				[codeblock]
				func _ready():
				    await RenderingServer.frame_post_draw
				    $Viewport.get_texture().get_image().save_png("user://Screenshot.png")
				[/codeblock]
				[b]Note:[/b] When [member use_hdr_2d] is [code]true[/code] the returned texture will be an HDR image encoded in linear space.
			</description>
		</method>
		<method name="get_viewport_rid" qualifiers="const">
			<return type="RID" />
			<description>
				Returns the viewport's RID from the [RenderingServer].
			</description>
		</method>
		<method name="get_visible_rect" qualifiers="const">
			<return type="Rect2" />
			<description>
				Returns the visible rectangle in global screen coordinates.
			</description>
		</method>
		<method name="gui_cancel_drag">
			<return type="void" />
			<description>
				Cancels the drag operation that was previously started through [method Control._get_drag_data] or forced with [method Control.force_drag].
			</description>
		</method>
		<method name="gui_get_drag_data" qualifiers="const">
			<return type="Variant" />
			<description>
				Returns the drag data from the GUI, that was previously returned by [method Control._get_drag_data].
			</description>
		</method>
		<method name="gui_get_focus_owner" qualifiers="const">
			<return type="Control" />
			<description>
				Returns the [Control] having the focus within this viewport. If no [Control] has the focus, returns null.
			</description>
		</method>
		<method name="gui_get_hovered_control" qualifiers="const">
			<return type="Control" />
			<description>
				Returns the [Control] that the mouse is currently hovering over in this viewport. If no [Control] has the cursor, returns null.
				Typically the leaf [Control] node or deepest level of the subtree which claims hover. This is very useful when used together with [method Node.is_ancestor_of] to find if the mouse is within a control tree.
			</description>
		</method>
		<method name="gui_is_drag_successful" qualifiers="const">
			<return type="bool" />
			<description>
				Returns [code]true[/code] if the drag operation is successful.
			</description>
		</method>
		<method name="gui_is_dragging" qualifiers="const">
			<return type="bool" />
			<description>
				Returns [code]true[/code] if a drag operation is currently ongoing and where the drop action could happen in this viewport.
				Alternative to [constant Node.NOTIFICATION_DRAG_BEGIN] and [constant Node.NOTIFICATION_DRAG_END] when you prefer polling the value.
			</description>
		</method>
		<method name="gui_release_focus">
			<return type="void" />
			<description>
				Removes the focus from the currently focused [Control] within this viewport. If no [Control] has the focus, does nothing.
			</description>
		</method>
		<method name="is_input_handled" qualifiers="const">
			<return type="bool" />
			<description>
				Returns whether the current [InputEvent] has been handled. Input events are not handled until [method set_input_as_handled] has been called during the lifetime of an [InputEvent].
				This is usually done as part of input handling methods like [method Node._input], [method Control._gui_input] or others, as well as in corresponding signal handlers.
				If [member handle_input_locally] is set to [code]false[/code], this method will try finding the first parent viewport that is set to handle input locally, and return its value for [method is_input_handled] instead.
			</description>
		</method>


<?xml version="1.0" encoding="UTF-8" ?>
<class name="ProjectSettings" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
	<brief_description>
		Stores globally-accessible variables.
	</brief_description>
	<description>
		Stores variables that can be accessed from everywhere. Use [method get_setting], [method set_setting] or [method has_setting] to access them. Variables stored in [code]project.godot[/code] are also loaded into [ProjectSettings], making this object very useful for reading custom game configuration options.
		When naming a Project Settings property, use the full path to the setting including the category. For example, [code]"application/config/name"[/code] for the project name. Category and property names can be viewed in the Project Settings dialog.
		[b]Feature tags:[/b] Project settings can be overridden for specific platforms and configurations (debug, release, ...) using [url=$DOCS_URL/tutorials/export/feature_tags.html]feature tags[/url].
		[b]Overriding:[/b] Any project setting can be overridden by creating a file named [code]override.cfg[/code] in the project's root directory. This can also be used in exported projects by placing this file in the same directory as the project binary. Overriding will still take the base project settings' [url=$DOCS_URL/tutorials/export/feature_tags.html]feature tags[/url] in account. Therefore, make sure to [i]also[/i] override the setting with the desired feature tags if you want them to override base project settings on all platforms and configurations.
	</description>
	<tutorials>
		<link title="Project Settings">$DOCS_URL/tutorials/editor/project_settings.html</link>
		<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link>
		<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link>
		<link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link>
	</tutorials>


<methods>
		<method name="add_property_info">
			<return type="void" />
			<param index="0" name="hint" type="Dictionary" />
			<description>
				Adds a custom property info to a property. The dictionary must contain:
				- [code]"name"[/code]: [String] (the property's name)
				- [code]"type"[/code]: [int] (see [enum Variant.Type])
				- optionally [code]"hint"[/code]: [int] (see [enum PropertyHint]) and [code]"hint_string"[/code]: [String]
				[codeblocks]
				[gdscript]
				ProjectSettings.set("category/property_name", 0)

				var property_info = {
				    "name": "category/property_name",
				    "type": TYPE_INT,
				    "hint": PROPERTY_HINT_ENUM,
				    "hint_string": "one,two,three"
				}

				ProjectSettings.add_property_info(property_info)
				[/gdscript]
				[csharp]
				ProjectSettings.Singleton.Set("category/property_name", 0);

				var propertyInfo = new Godot.Collections.Dictionary
				{
				    {"name", "category/propertyName"},
				    {"type", (int)Variant.Type.Int},
				    {"hint", (int)PropertyHint.Enum},
				    {"hint_string", "one,two,three"},
				};

				ProjectSettings.AddPropertyInfo(propertyInfo);
				[/csharp]
				[/codeblocks]
			</description>
		</method>
		<method name="clear">
			<return type="void" />
			<param index="0" name="name" type="String" />
			<description>
				Clears the whole configuration (not recommended, may break things).
			</description>
		</method>
		<method name="get_global_class_list">
			<return type="Dictionary[]" />
			<description>
				Returns an [Array] of registered global classes. Each global class is represented as a [Dictionary] that contains the following entries:
				- [code]base[/code] is a name of the base class;
				- [code]class[/code] is a name of the registered global class;
				- [code]icon[/code] is a path to a custom icon of the global class, if it has any;
				- [code]language[/code] is a name of a programming language in which the global class is written;
				- [code]path[/code] is a path to a file containing the global class.
				[b]Note:[/b] Both the script and the icon paths are local to the project filesystem, i.e. they start with [code]res://[/code].
			</description>
		</method>
		<method name="get_order" qualifiers="const">
			<return type="int" />
			<param index="0" name="name" type="String" />
			<description>
				Returns the order of a configuration value (influences when saved to the config file).
			</description>
		</method>
		<method name="get_setting" qualifiers="const">
			<return type="Variant" />
			<param index="0" name="name" type="String" />
			<param index="1" name="default_value" type="Variant" default="null" />
			<description>
				Returns the value of the setting identified by [param name]. If the setting doesn't exist and [param default_value] is specified, the value of [param default_value] is returned. Otherwise, [code]null[/code] is returned.
				[codeblocks]
				[gdscript]
				print(ProjectSettings.get_setting("application/config/name"))
				print(ProjectSettings.get_setting("application/config/custom_description", "No description specified."))
				[/gdscript]
				[csharp]
				GD.Print(ProjectSettings.GetSetting("application/config/name"));
				GD.Print(ProjectSettings.GetSetting("application/config/custom_description", "No description specified."));
				[/csharp]
				[/codeblocks]
				[b]Note:[/b] This method doesn't take potential feature overrides into account automatically. Use [method get_setting_with_override] to handle seamlessly.
			</description>
		</method>
		<method name="get_setting_with_override" qualifiers="const">
			<return type="Variant" />
			<param index="0" name="name" type="StringName" />
			<description>
				Similar to [method get_setting], but applies feature tag overrides if any exists and is valid.
				[b]Example:[/b]	If the setting override [code]"application/config/name.windows"[/code] exists, and the following code is executed on a [i]Windows[/i] operating system, the overridden setting is printed instead:
				[codeblocks]
				[gdscript]
				print(ProjectSettings.get_setting_with_override("application/config/name"))
				[/gdscript]
				[csharp]
				GD.Print(ProjectSettings.GetSettingWithOverride("application/config/name"));
				[/csharp]
				[/codeblocks]
			</description>
		</method>
		<method name="globalize_path" qualifiers="const">
			<return type="String" />
			<param index="0" name="path" type="String" />
			<description>
				Returns the absolute, native OS path corresponding to the localized [param path] (starting with [code]res://[/code] or [code]user://[/code]). The returned path will vary depending on the operating system and user preferences. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] to see what those paths convert to. See also [method localize_path].
				[b]Note:[/b] [method globalize_path] with [code]res://[/code] will not work in an exported project. Instead, prepend the executable's base directory to the path when running from an exported project:
				[codeblock]
				var path = ""
				if OS.has_feature("editor"):
				    # Running from an editor binary.
				    # `path` will contain the absolute path to `hello.txt` located in the project root.
				    path = ProjectSettings.globalize_path("res://hello.txt")
				else:
				    # Running from an exported project.
				    # `path` will contain the absolute path to `hello.txt` next to the executable.
				    # This is *not* identical to using `ProjectSettings.globalize_path()` with a `res://` path,
				    # but is close enough in spirit.
				    path = OS.get_executable_path().get_base_dir().path_join("hello.txt")
				[/codeblock]
			</description>
		</method>
		<method name="has_setting" qualifiers="const">
			<return type="bool" />
			<param index="0" name="name" type="String" />
			<description>
				Returns [code]true[/code] if a configuration value is present.
			</description>
		</method>
		<method name="load_resource_pack">
			<return type="bool" />
			<param index="0" name="pack" type="String" />
			<param index="1" name="replace_files" type="bool" default="true" />
			<param index="2" name="offset" type="int" default="0" />
			<description>
				Loads the contents of the .pck or .zip file specified by [param pack] into the resource filesystem ([code]res://[/code]). Returns [code]true[/code] on success.
				[b]Note:[/b] If a file from [param pack] shares the same path as a file already in the resource filesystem, any attempts to load that file will use the file from [param pack] unless [param replace_files] is set to [code]false[/code].
				[b]Note:[/b] The optional [param offset] parameter can be used to specify the offset in bytes to the start of the resource pack. This is only supported for .pck files.
			</description>
		</method>
		<method name="localize_path" qualifiers="const">
			<return type="String" />
			<param index="0" name="path" type="String" />
			<description>
				Returns the localized path (starting with [code]res://[/code]) corresponding to the absolute, native OS [param path]. See also [method globalize_path].
			</description>
		</method>