commit 946e7d962118366ae979641666dda199529ec2bd Author: greg Date: Mon Jan 20 12:59:40 2025 +0100 push minetest server diff --git a/README.md b/README.md new file mode 100644 index 0000000..02fd0e1 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# MINETEST SERVER + +## PRE REQUIS + +- docker + +### DEPLOYER LOCALEMENT + +```bash + +``` + +### DEPLOYER SUR INTERNET + +```bash + +``` \ No newline at end of file diff --git a/conf/minetest.conf b/conf/minetest.conf new file mode 100644 index 0000000..e34711f --- /dev/null +++ b/conf/minetest.conf @@ -0,0 +1,3497 @@ +# NAME + MOTD (MESSAGE D'ENTREE) +server_name = "Minetest Garage" +server_description = "Minetest server du Garage!" +motd = Bienvenue sur les serveur Minetest du Garage Enjoy! + +# password +# type: string +default_password = LeGarage + +# ADMIN +name = garage + +# MSG +kick_msg_shutdown = "Le serveur du Garage ferme!" +kick_msg_crash = "Le serveur a crasher... sorry!" + +# A CHANGER ( TOUT LE MONDE AVEC GOD MODE) +default_privs = admin + +# AU CAS OU! +enable_rollback_recording = true + +# Server / Singleplayer +# + +# Name of the server, to be displayed when players join and in the serverlist. +# type: string +# server_name = Minetest server + +# Description of server, to be displayed when players join and in the serverlist. +# type: string +# server_description = mine here + +# Domain name of server, to be displayed in the serverlist. +# type: string +# server_address = game.minetest.net + +# Homepage of server, to be displayed in the serverlist. +# type: string +# server_url = https://minetest.net + +# Automatically report to the serverlist. +# type: bool +# server_announce = false + +# Announce to this serverlist. +# type: string +# serverlist_url = servers.minetest.net + +# Remove color codes from incoming chat messages +# Use this to stop players from being able to use color in their messages +# type: bool +# strip_color_codes = false + +## Network + +# Network port to listen (UDP). +# This value will be overridden when starting from the main menu. +# type: int +# port = 30000 + +# The network interface that the server listens on. +# type: string +# bind_address = + +# Enable to disallow old clients from connecting. +# Older clients are compatible in the sense that they will not crash when connecting +# to new servers, but they may not support all new features that you are expecting. +# type: bool +# strict_protocol_version_checking = false + +# Specifies URL from which client fetches media instead of using UDP. +# $filename should be accessible from $remote_media$filename via cURL +# (obviously, remote_media should end with a slash). +# Files that are not present will be fetched the usual way. +# type: string +# remote_media = + +# Enable/disable running an IPv6 server. +# Ignored if bind_address is set. +# Needs enable_ipv6 to be enabled. +# type: bool +# ipv6_server = false + +### Advanced + +# Maximum number of blocks that are simultaneously sent per client. +# The maximum total count is calculated dynamically: +# max_total = ceil((#clients + max_users) * per_client / 4) +# type: int +# max_simultaneous_block_sends_per_client = 40 + +# To reduce lag, block transfers are slowed down when a player is building something. +# This determines how long they are slowed down after placing or removing a node. +# type: float +# full_block_send_enable_min_time_from_building = 2.0 + +# Maximum number of packets sent per send step, if you have a slow connection +# try reducing it, but don't reduce it to a number below double of targeted +# client number. +# type: int +# max_packets_per_iteration = 1024 + +# Compression level to use when sending mapblocks to the client. +# -1 - use default compression level +# 0 - least compression, fastest +# 9 - best compression, slowest +# type: int min: -1 max: 9 +# map_compression_level_net = -1 + +## Game + +# Default game when creating a new world. +# This will be overridden when creating a world from the main menu. +# type: string +# default_game = garage + +# Message of the day displayed to players connecting. +# type: string +# motd = + +# Maximum number of players that can be connected simultaneously. +# type: int +# max_users = 15 + +# World directory (everything in the world is stored here). +# Not needed if starting from the main menu. +# type: path +# map-dir = + +# Time in seconds for item entity (dropped items) to live. +# Setting it to -1 disables the feature. +# type: int +# item_entity_ttl = 900 + +# Specifies the default stack size of nodes, items and tools. +# Note that mods or games may explicitly set a stack for certain (or all) items. +# type: int +# default_stack_max = 99 + +# Enable players getting damage and dying. +# type: bool +# enable_damage = false + +# Enable creative mode for all players +# type: bool +# creative_mode = false + +# A chosen map seed for a new map, leave empty for random. +# Will be overridden when creating a new world in the main menu. +# type: string +# fixed_map_seed = + +# New users need to input this password. +# type: string +# default_password = + +# The privileges that new users automatically get. +# See /privs in game for a full list on your server and mod configuration. +# type: string +# default_privs = interact, shout + +# Privileges that players with basic_privs can grant +# type: string +# basic_privs = interact, shout + +# Whether players are shown to clients without any range limit. +# Deprecated, use the setting player_transfer_distance instead. +# type: bool +# unlimited_player_transfer_distance = true + +# Defines the maximal player transfer distance in blocks (0 = unlimited). +# type: int +# player_transfer_distance = 0 + +# Whether to allow players to damage and kill each other. +# type: bool +# enable_pvp = true + +# Enable mod channels support. +# type: bool +# enable_mod_channels = false + +# If this is set, players will always (re)spawn at the given position. +# type: string +# static_spawnpoint = + +# If enabled, new players cannot join with an empty password. +# type: bool +disallow_empty_password = true + +# If enabled, disable cheat prevention in multiplayer. +# type: bool +# disable_anticheat = false + +# If enabled, actions are recorded for rollback. +# This option is only read when server starts. +# type: bool +# enable_rollback_recording = false + +# Format of player chat messages. The following strings are valid placeholders: +# @name, @message, @timestamp (optional) +# type: string +# chat_message_format = <@name> @message + +# If the execution of a chat command takes longer than this specified time in +# seconds, add the time information to the chat command message +# type: float +# chatcommand_msg_time_threshold = 0.1 + +# A message to be displayed to all clients when the server shuts down. +# type: string +# kick_msg_shutdown = Server shutting down. + +# A message to be displayed to all clients when the server crashes. +# type: string +# kick_msg_crash = This server has experienced an internal error. You will now be disconnected. + +# Whether to ask clients to reconnect after a (Lua) crash. +# Set this to true if your server is set up to restart automatically. +# type: bool +# ask_reconnect_on_crash = false + +# From how far clients know about objects, stated in mapblocks (16 nodes). +# +# Setting this larger than active_block_range will also cause the server +# to maintain active objects up to this distance in the direction the +# player is looking. (This can avoid mobs suddenly disappearing from view) +# type: int +# active_object_send_range_blocks = 8 + +# The radius of the volume of blocks around every player that is subject to the +# active block stuff, stated in mapblocks (16 nodes). +# In active blocks objects are loaded and ABMs run. +# This is also the minimum range in which active objects (mobs) are maintained. +# This should be configured together with active_object_send_range_blocks. +# type: int +# active_block_range = 4 + +# From how far blocks are sent to clients, stated in mapblocks (16 nodes). +# type: int +# max_block_send_distance = 12 + +# Maximum number of forceloaded mapblocks. +# type: int +# max_forceloaded_blocks = 16 + +# Interval of sending time of day to clients. +# type: int +# time_send_interval = 5 + +# Controls length of day/night cycle. +# Examples: +# 72 = 20min, 360 = 4min, 1 = 24hour, 0 = day/night/whatever stays unchanged. +# type: int +# time_speed = 72 + +# Time of day when a new world is started, in millihours (0-23999). +# type: int min: 0 max: 23999 +# world_start_time = 6125 + +# Interval of saving important changes in the world, stated in seconds. +# type: float +# server_map_save_interval = 5.3 + +# Set the maximum character length of a chat message sent by clients. +# type: int +# chat_message_max_size = 500 + +# Amount of messages a player may send per 10 seconds. +# type: float +# chat_message_limit_per_10sec = 10.0 + +# Kick players who sent more than X messages per 10 seconds. +# type: int +# chat_message_limit_trigger_kick = 50 + +# DEFAULT CONF EXEMPLE: +# This file contains a list of all available settings and their default value for minetest.conf + +# By default, all the settings are commented and not functional. +# Uncomment settings by removing the preceding #. + +# minetest.conf is read by default from: +# ../minetest.conf +# ../../minetest.conf +# Any other path can be chosen by passing the path as a parameter +# to the program, eg. "minetest.exe --config ../minetest.conf.example". + +# Further documentation: +# http://wiki.minetest.net/ + +# +# Controls +# + +# If enabled, you can place blocks at the position (feet + eye level) where you stand. +# This is helpful when working with nodeboxes in small areas. +# type: bool +# enable_build_where_you_stand = false + +# Player is able to fly without being affected by gravity. +# This requires the "fly" privilege on the server. +# type: bool +# free_move = false + +# If enabled, makes move directions relative to the player's pitch when flying or swimming. +# type: bool +# pitch_move = false + +# Fast movement (via the "Aux1" key). +# This requires the "fast" privilege on the server. +# type: bool +# fast_move = false + +# If enabled together with fly mode, player is able to fly through solid nodes. +# This requires the "noclip" privilege on the server. +# type: bool +# noclip = false + +# Smooths camera when looking around. Also called look or mouse smoothing. +# Useful for recording videos. +# type: bool +# cinematic = false + +# Smooths rotation of camera. 0 to disable. +# type: float min: 0 max: 0.99 +# camera_smoothing = 0.0 + +# Smooths rotation of camera in cinematic mode. 0 to disable. +# type: float min: 0 max: 0.99 +# cinematic_camera_smoothing = 0.7 + +# Invert vertical mouse movement. +# type: bool +# invert_mouse = false + +# Mouse sensitivity multiplier. +# type: float +# mouse_sensitivity = 0.2 + +# If enabled, "Aux1" key instead of "Sneak" key is used for climbing down and +# descending. +# type: bool +# aux1_descends = false + +# Double-tapping the jump key toggles fly mode. +# type: bool +# doubletap_jump = false + +# If disabled, "Aux1" key is used to fly fast if both fly and fast mode are +# enabled. +# type: bool +# always_fly_fast = true + +# The time in seconds it takes between repeated node placements when holding +# the place button. +# type: float min: 0.001 +# repeat_place_time = 0.25 + +# Automatically jump up single-node obstacles. +# type: bool +# autojump = false + +# Prevent digging and placing from repeating when holding the mouse buttons. +# Enable this when you dig or place too often by accident. +# type: bool +# safe_dig_and_place = false + +# Enable random user input (only used for testing). +# type: bool +# random_input = false + +# Continuous forward movement, toggled by autoforward key. +# Press the autoforward key again or the backwards movement to disable. +# type: bool +# continuous_forward = false + +# The length in pixels it takes for touch screen interaction to start. +# type: int min: 0 max: 100 +# touchscreen_threshold = 20 + +# (Android) Fixes the position of virtual joystick. +# If disabled, virtual joystick will center to first-touch's position. +# type: bool +# fixed_virtual_joystick = false + +# (Android) Use virtual joystick to trigger "Aux1" button. +# If enabled, virtual joystick will also tap "Aux1" button when out of main circle. +# type: bool +# virtual_joystick_triggers_aux1 = false + +# Enable joysticks +# type: bool +# enable_joysticks = false + +# The identifier of the joystick to use +# type: int +# joystick_id = 0 + +# The type of joystick +# type: enum values: auto, generic, xbox, dragonrise_gamecube +# joystick_type = auto + +# The time in seconds it takes between repeated events +# when holding down a joystick button combination. +# type: float min: 0.001 +# repeat_joystick_button_time = 0.17 + +# The dead zone of the joystick +# type: int +# joystick_deadzone = 2048 + +# The sensitivity of the joystick axes for moving the +# in-game view frustum around. +# type: float +# joystick_frustum_sensitivity = 170 + +# Key for moving the player forward. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_forward = KEY_KEY_W + +# Key for moving the player backward. +# Will also disable autoforward, when active. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_backward = KEY_KEY_S + +# Key for moving the player left. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_left = KEY_KEY_A + +# Key for moving the player right. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_right = KEY_KEY_D + +# Key for jumping. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_jump = KEY_SPACE + +# Key for sneaking. +# Also used for climbing down and descending in water if aux1_descends is disabled. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_sneak = KEY_LSHIFT + +# Key for digging. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_dig = KEY_LBUTTON + +# Key for placing. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_place = KEY_RBUTTON + +# Key for opening the inventory. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_inventory = KEY_KEY_I + +# Key for moving fast in fast mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_aux1 = KEY_KEY_E + +# Key for opening the chat window. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_chat = KEY_KEY_T + +# Key for opening the chat window to type commands. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_cmd = / + +# Key for opening the chat window to type local commands. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_cmd_local = . + +# Key for toggling unlimited view range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_rangeselect = KEY_KEY_R + +# Key for toggling flying. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_freemove = KEY_KEY_K + +# Key for toggling pitch move mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_pitchmove = KEY_KEY_P + +# Key for toggling fast mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_fastmove = KEY_KEY_J + +# Key for toggling noclip mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_noclip = KEY_KEY_H + +# Key for selecting the next item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_hotbar_next = KEY_KEY_N + +# Key for selecting the previous item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_hotbar_previous = KEY_KEY_B + +# Key for muting the game. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_mute = KEY_KEY_M + +# Key for increasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_increase_volume = + +# Key for decreasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_decrease_volume = + +# Key for toggling autoforward. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_autoforward = + +# Key for toggling cinematic mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_cinematic = + +# Key for toggling display of minimap. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_minimap = KEY_KEY_V + +# Key for taking screenshots. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_screenshot = KEY_F12 + +# Key for dropping the currently selected item. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_drop = KEY_KEY_Q + +# Key to use view zoom when possible. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_zoom = KEY_KEY_Z + +# Key for selecting the first hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot1 = KEY_KEY_1 + +# Key for selecting the second hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot2 = KEY_KEY_2 + +# Key for selecting the third hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot3 = KEY_KEY_3 + +# Key for selecting the fourth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot4 = KEY_KEY_4 + +# Key for selecting the fifth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot5 = KEY_KEY_5 + +# Key for selecting the sixth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot6 = KEY_KEY_6 + +# Key for selecting the seventh hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot7 = KEY_KEY_7 + +# Key for selecting the eighth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot8 = KEY_KEY_8 + +# Key for selecting the ninth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot9 = KEY_KEY_9 + +# Key for selecting the tenth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot10 = KEY_KEY_0 + +# Key for selecting the 11th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot11 = + +# Key for selecting the 12th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot12 = + +# Key for selecting the 13th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot13 = + +# Key for selecting the 14th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot14 = + +# Key for selecting the 15th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot15 = + +# Key for selecting the 16th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot16 = + +# Key for selecting the 17th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot17 = + +# Key for selecting the 18th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot18 = + +# Key for selecting the 19th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot19 = + +# Key for selecting the 20th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot20 = + +# Key for selecting the 21st hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot21 = + +# Key for selecting the 22nd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot22 = + +# Key for selecting the 23rd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot23 = + +# Key for selecting the 24th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot24 = + +# Key for selecting the 25th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot25 = + +# Key for selecting the 26th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot26 = + +# Key for selecting the 27th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot27 = + +# Key for selecting the 28th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot28 = + +# Key for selecting the 29th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot29 = + +# Key for selecting the 30th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot30 = + +# Key for selecting the 31st hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot31 = + +# Key for selecting the 32nd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_slot32 = + +# Key for toggling the display of the HUD. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_hud = KEY_F1 + +# Key for toggling the display of chat. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_chat = KEY_F2 + +# Key for toggling the display of the large chat console. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_console = KEY_F10 + +# Key for toggling the display of fog. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_force_fog_off = KEY_F3 + +# Key for toggling the camera update. Only used for development +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_update_camera = + +# Key for toggling the display of debug info. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_debug = KEY_F5 + +# Key for toggling the display of the profiler. Used for development. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_toggle_profiler = KEY_F6 + +# Key for switching between first- and third-person camera. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_camera_mode = KEY_KEY_C + +# Key for increasing the viewing range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_increase_viewing_range_min = + + +# Key for decreasing the viewing range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +# type: key +# keymap_decrease_viewing_range_min = - + +# +# Graphics +# + +## In-Game + +### Basic + +# Whether name tag backgrounds should be shown by default. +# Mods may still set a background. +# type: bool +# show_nametag_backgrounds = true + +# Enable vertex buffer objects. +# This should greatly improve graphics performance. +# type: bool +# enable_vbo = true + +# Whether to fog out the end of the visible area. +# type: bool +# enable_fog = true + +# Leaves style: +# - Fancy: all faces visible +# - Simple: only outer faces, if defined special_tiles are used +# - Opaque: disable transparency +# type: enum values: fancy, simple, opaque +# leaves_style = fancy + +# Connects glass if supported by node. +# type: bool +# connected_glass = false + +# Enable smooth lighting with simple ambient occlusion. +# Disable for speed or for different looks. +# type: bool +# smooth_lighting = true + +# Enables tradeoffs that reduce CPU load or increase rendering performance +# at the expense of minor visual glitches that do not impact game playability. +# type: bool +# performance_tradeoffs = false + +# Clouds are a client side effect. +# type: bool +# enable_clouds = true + +# Use 3D cloud look instead of flat. +# type: bool +# enable_3d_clouds = true + +# Method used to highlight selected object. +# type: enum values: box, halo, none +# node_highlighting = box + +# Adds particles when digging a node. +# type: bool +# enable_particles = true + +### Filtering + +# Use mipmapping to scale textures. May slightly increase performance, +# especially when using a high resolution texture pack. +# Gamma correct downscaling is not supported. +# type: bool +# mip_map = false + +# Use anisotropic filtering when viewing at textures from an angle. +# type: bool +# anisotropic_filter = false + +# Use bilinear filtering when scaling textures. +# type: bool +# bilinear_filter = false + +# Use trilinear filtering when scaling textures. +# type: bool +# trilinear_filter = false + +# Filtered textures can blend RGB values with fully-transparent neighbors, +# which PNG optimizers usually discard, often resulting in dark or +# light edges to transparent textures. Apply a filter to clean that up +# at texture load time. This is automatically enabled if mipmapping is enabled. +# type: bool +# texture_clean_transparent = false + +# When using bilinear/trilinear/anisotropic filters, low-resolution textures +# can be blurred, so automatically upscale them with nearest-neighbor +# interpolation to preserve crisp pixels. This sets the minimum texture size +# for the upscaled textures; higher values look sharper, but require more +# memory. Powers of 2 are recommended. This setting is ONLY applied if +# bilinear/trilinear/anisotropic filtering is enabled. +# This is also used as the base node texture size for world-aligned +# texture autoscaling. +# type: int +# texture_min_size = 64 + +# Use multi-sample antialiasing (MSAA) to smooth out block edges. +# This algorithm smooths out the 3D viewport while keeping the image sharp, +# but it doesn't affect the insides of textures +# (which is especially noticeable with transparent textures). +# Visible spaces appear between nodes when shaders are disabled. +# If set to 0, MSAA is disabled. +# A restart is required after changing this option. +# type: enum values: 0, 1, 2, 4, 8, 16 +# fsaa = 0 + +# Undersampling is similar to using a lower screen resolution, but it applies +# to the game world only, keeping the GUI intact. +# It should give a significant performance boost at the cost of less detailed image. +# Higher values result in a less detailed image. +# type: int min: 1 max: 8 +# undersampling = 1 + +### Shaders + +# Shaders allow advanced visual effects and may increase performance on some video +# cards. +# This only works with the OpenGL video backend. +# type: bool +# enable_shaders = true + +# Path to shader directory. If no path is defined, default location will be used. +# type: path +# shader_path = + +#### Tone Mapping + +# Enables Hable's 'Uncharted 2' filmic tone mapping. +# Simulates the tone curve of photographic film and how this approximates the +# appearance of high dynamic range images. Mid-range contrast is slightly +# enhanced, highlights and shadows are gradually compressed. +# type: bool +# tone_mapping = false + +#### Waving Nodes + +# Set to true to enable waving liquids (like water). +# Requires shaders to be enabled. +# type: bool +# enable_waving_water = false + +# The maximum height of the surface of waving liquids. +# 4.0 = Wave height is two nodes. +# 0.0 = Wave doesn't move at all. +# Default is 1.0 (1/2 node). +# Requires waving liquids to be enabled. +# type: float min: 0 max: 4 +# water_wave_height = 1.0 + +# Length of liquid waves. +# Requires waving liquids to be enabled. +# type: float min: 0.1 +# water_wave_length = 20.0 + +# How fast liquid waves will move. Higher = faster. +# If negative, liquid waves will move backwards. +# Requires waving liquids to be enabled. +# type: float +# water_wave_speed = 5.0 + +# Set to true to enable waving leaves. +# Requires shaders to be enabled. +# type: bool +# enable_waving_leaves = false + +# Set to true to enable waving plants. +# Requires shaders to be enabled. +# type: bool +# enable_waving_plants = false + +#### Dynamic shadows + +# Set to true to enable Shadow Mapping. +# Requires shaders to be enabled. +# type: bool +# enable_dynamic_shadows = false + +# Set the shadow strength gamma. +# Adjusts the intensity of in-game dynamic shadows. +# Lower value means lighter shadows, higher value means darker shadows. +# type: float min: 0.1 max: 10 +# shadow_strength_gamma = 1.0 + +# Maximum distance to render shadows. +# type: float min: 10 max: 1000 +# shadow_map_max_distance = 120.0 + +# Texture size to render the shadow map on. +# This must be a power of two. +# Bigger numbers create better shadows but it is also more expensive. +# type: int min: 128 max: 8192 +# shadow_map_texture_size = 1024 + +# Sets shadow texture quality to 32 bits. +# On false, 16 bits texture will be used. +# This can cause much more artifacts in the shadow. +# type: bool +# shadow_map_texture_32bit = true + +# Enable Poisson disk filtering. +# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering. +# type: bool +# shadow_poisson_filter = true + +# Define shadow filtering quality. +# This simulates the soft shadows effect by applying a PCF or Poisson disk +# but also uses more resources. +# type: enum values: 0, 1, 2 +# shadow_filters = 1 + +# Enable colored shadows. +# On true translucent nodes cast colored shadows. This is expensive. +# type: bool +# shadow_map_color = false + +# Spread a complete update of shadow map over given amount of frames. +# Higher values might make shadows laggy, lower values +# will consume more resources. +# Minimum value: 1; maximum value: 16 +# type: int min: 1 max: 16 +# shadow_update_frames = 8 + +# Set the soft shadow radius size. +# Lower values mean sharper shadows, bigger values mean softer shadows. +# Minimum value: 1.0; maximum value: 10.0 +# type: float min: 1 max: 10 +# shadow_soft_radius = 1.0 + +# Set the tilt of Sun/Moon orbit in degrees. +# Value of 0 means no tilt / vertical orbit. +# Minimum value: 0.0; maximum value: 60.0 +# type: float min: 0 max: 60 +# shadow_sky_body_orbit_tilt = 0.0 + +### Advanced + +# Arm inertia, gives a more realistic movement of +# the arm when the camera moves. +# type: bool +# arm_inertia = true + +# If FPS would go higher than this, limit it by sleeping +# to not waste CPU power for no benefit. +# type: int min: 1 +# fps_max = 60 + +# Maximum FPS when the window is not focused, or when the game is paused. +# type: int min: 1 +# fps_max_unfocused = 20 + +# Open the pause menu when the window's focus is lost. Does not pause if a formspec is +# open. +# type: bool +# pause_on_lost_focus = false + +# View distance in nodes. +# type: int min: 20 max: 4000 +# viewing_range = 190 + +# Camera 'near clipping plane' distance in nodes, between 0 and 0.25 +# Only works on GLES platforms. Most users will not need to change this. +# Increasing can reduce artifacting on weaker GPUs. +# 0.1 = Default, 0.25 = Good value for weaker tablets. +# type: float min: 0 max: 0.25 +# near_plane = 0.1 + +# Width component of the initial window size. Ignored in fullscreen mode. +# type: int min: 1 +# screen_w = 1024 + +# Height component of the initial window size. Ignored in fullscreen mode. +# type: int min: 1 +# screen_h = 600 + +# Save window size automatically when modified. +# type: bool +# autosave_screensize = true + +# Fullscreen mode. +# type: bool +# fullscreen = false + +# Vertical screen synchronization. +# type: bool +# vsync = false + +# Field of view in degrees. +# type: int min: 45 max: 160 +# fov = 72 + +# Alters the light curve by applying 'gamma correction' to it. +# Higher values make middle and lower light levels brighter. +# Value '1.0' leaves the light curve unaltered. +# This only has significant effect on daylight and artificial +# light, it has very little effect on natural night light. +# type: float min: 0.33 max: 3 +# display_gamma = 1.0 + +# Gradient of light curve at minimum light level. +# Controls the contrast of the lowest light levels. +# type: float min: 0 max: 3 +# lighting_alpha = 0.0 + +# Gradient of light curve at maximum light level. +# Controls the contrast of the highest light levels. +# type: float min: 0 max: 3 +# lighting_beta = 1.5 + +# Strength of light curve boost. +# The 3 'boost' parameters define a range of the light +# curve that is boosted in brightness. +# type: float min: 0 max: 0.4 +# lighting_boost = 0.2 + +# Center of light curve boost range. +# Where 0.0 is minimum light level, 1.0 is maximum light level. +# type: float min: 0 max: 1 +# lighting_boost_center = 0.5 + +# Spread of light curve boost range. +# Controls the width of the range to be boosted. +# Standard deviation of the light curve boost Gaussian. +# type: float min: 0 max: 0.4 +# lighting_boost_spread = 0.2 + +# Path to texture directory. All textures are first searched from here. +# type: path +# texture_path = + +# The rendering back-end. +# A restart is required after changing this. +# Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise. +# On other platforms, OpenGL is recommended. +# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental) +# type: enum values: opengl, ogles1, ogles2 +# video_driver = opengl + +# Radius of cloud area stated in number of 64 node cloud squares. +# Values larger than 26 will start to produce sharp cutoffs at cloud area corners. +# type: int +# cloud_radius = 12 + +# Enable view bobbing and amount of view bobbing. +# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. +# type: float +# view_bobbing_amount = 1.0 + +# Multiplier for fall bobbing. +# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. +# type: float +# fall_bobbing_amount = 0.03 + +# 3D support. +# Currently supported: +# - none: no 3d output. +# - anaglyph: cyan/magenta color 3d. +# - interlaced: odd/even line based polarisation screen support. +# - topbottom: split screen top/bottom. +# - sidebyside: split screen side by side. +# - crossview: Cross-eyed 3d +# - pageflip: quadbuffer based 3d. +# Note that the interlaced mode requires shaders to be enabled. +# type: enum values: none, anaglyph, interlaced, topbottom, sidebyside, crossview, pageflip +# 3d_mode = none + +# Strength of 3D mode parallax. +# type: float +# 3d_paralax_strength = 0.025 + +# In-game chat console height, between 0.1 (10%) and 1.0 (100%). +# type: float min: 0.1 max: 1 +# console_height = 0.6 + +# In-game chat console background color (R,G,B). +# type: string +# console_color = (0,0,0) + +# In-game chat console background alpha (opaqueness, between 0 and 255). +# type: int min: 0 max: 255 +# console_alpha = 200 + +# Formspec full-screen background opacity (between 0 and 255). +# type: int min: 0 max: 255 +# formspec_fullscreen_bg_opacity = 140 + +# Formspec full-screen background color (R,G,B). +# type: string +# formspec_fullscreen_bg_color = (0,0,0) + +# Formspec default background opacity (between 0 and 255). +# type: int min: 0 max: 255 +# formspec_default_bg_opacity = 140 + +# Formspec default background color (R,G,B). +# type: string +# formspec_default_bg_color = (0,0,0) + +# Selection box border color (R,G,B). +# type: string +# selectionbox_color = (0,0,0) + +# Width of the selection box lines around nodes. +# type: int min: 1 max: 5 +# selectionbox_width = 2 + +# Crosshair color (R,G,B). +# Also controls the object crosshair color +# type: string +# crosshair_color = (255,255,255) + +# Crosshair alpha (opaqueness, between 0 and 255). +# This also applies to the object crosshair. +# type: int min: 0 max: 255 +# crosshair_alpha = 255 + +# Maximum number of recent chat messages to show +# type: int min: 2 max: 20 +# recent_chat_messages = 6 + +# Whether node texture animations should be desynchronized per mapblock. +# type: bool +# desynchronize_mapblock_texture_animation = true + +# Maximum proportion of current window to be used for hotbar. +# Useful if there's something to be displayed right or left of hotbar. +# type: float +# hud_hotbar_max_width = 1.0 + +# Modifies the size of the HUD elements. +# type: float +# hud_scaling = 1.0 + +# Enables caching of facedir rotated meshes. +# type: bool +# enable_mesh_cache = false + +# Delay between mesh updates on the client in ms. Increasing this will slow +# down the rate of mesh updates, thus reducing jitter on slower clients. +# type: int min: 0 max: 50 +# mesh_generation_interval = 0 + +# Size of the MapBlock cache of the mesh generator. Increasing this will +# increase the cache hit %, reducing the data being copied from the main +# thread, thus reducing jitter. +# type: int min: 0 max: 1000 +# meshgen_block_cache_size = 20 + +# Enables minimap. +# type: bool +# enable_minimap = true + +# Shape of the minimap. Enabled = round, disabled = square. +# type: bool +# minimap_shape_round = true + +# True = 256 +# False = 128 +# Usable to make minimap smoother on slower machines. +# type: bool +# minimap_double_scan_height = true + +# Make fog and sky colors depend on daytime (dawn/sunset) and view direction. +# type: bool +# directional_colored_fog = true + +# The strength (darkness) of node ambient-occlusion shading. +# Lower is darker, Higher is lighter. The valid range of values for this +# setting is 0.25 to 4.0 inclusive. If the value is out of range it will be +# set to the nearest valid value. +# type: float min: 0.25 max: 4 +# ambient_occlusion_gamma = 2.2 + +# Enables animation of inventory items. +# type: bool +# inventory_items_animations = false + +# Fraction of the visible distance at which fog starts to be rendered +# type: float min: 0 max: 0.99 +# fog_start = 0.4 + +# Makes all liquids opaque +# type: bool +# opaque_water = false + +# Textures on a node may be aligned either to the node or to the world. +# The former mode suits better things like machines, furniture, etc., while +# the latter makes stairs and microblocks fit surroundings better. +# However, as this possibility is new, thus may not be used by older servers, +# this option allows enforcing it for certain node types. Note though that +# that is considered EXPERIMENTAL and may not work properly. +# type: enum values: disable, enable, force_solid, force_nodebox +# world_aligned_mode = enable + +# World-aligned textures may be scaled to span several nodes. However, +# the server may not send the scale you want, especially if you use +# a specially-designed texture pack; with this option, the client tries +# to determine the scale automatically basing on the texture size. +# See also texture_min_size. +# Warning: This option is EXPERIMENTAL! +# type: enum values: disable, enable, force +# autoscale_mode = disable + +# Show entity selection boxes +# A restart is required after changing this. +# type: bool +# show_entity_selectionbox = false + +# Distance in nodes at which transparency depth sorting is enabled +# Use this to limit the performance impact of transparency depth sorting +# type: int min: 0 max: 128 +# transparency_sorting_distance = 16 + +## Menus + +# Use a cloud animation for the main menu background. +# type: bool +# menu_clouds = true + +# Scale GUI by a user specified value. +# Use a nearest-neighbor-anti-alias filter to scale the GUI. +# This will smooth over some of the rough edges, and blend +# pixels when scaling down, at the cost of blurring some +# edge pixels when images are scaled by non-integer sizes. +# type: float min: 0.001 +# gui_scaling = 1.0 + +# When gui_scaling_filter is true, all GUI images need to be +# filtered in software, but some images are generated directly +# to hardware (e.g. render-to-texture for nodes in inventory). +# type: bool +# gui_scaling_filter = false + +# When gui_scaling_filter_txr2img is true, copy those images +# from hardware to software for scaling. When false, fall back +# to the old scaling method, for video drivers that don't +# properly support downloading textures back from hardware. +# type: bool +# gui_scaling_filter_txr2img = true + +# Delay showing tooltips, stated in milliseconds. +# type: int +# tooltip_show_delay = 400 + +# Append item name to tooltip. +# type: bool +# tooltip_append_itemname = false + +# type: bool +# font_bold = false + +# type: bool +# font_italic = false + +# Shadow offset (in pixels) of the default font. If 0, then shadow will not be drawn. +# type: int +# font_shadow = 1 + +# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255. +# type: int min: 0 max: 255 +# font_shadow_alpha = 127 + +# Font size of the default font where 1 unit = 1 pixel at 96 DPI +# type: int min: 1 +# font_size = 16 + +# For pixel-style fonts that do not scale well, this ensures that font sizes used +# with this font will always be divisible by this value, in pixels. For instance, +# a pixel font 16 pixels tall should have this set to 16, so it will only ever be +# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. +# type: int min: 1 +# font_size_divisible_by = 1 + +# Path to the default font. Must be a TrueType font. +# The fallback font will be used if the font cannot be loaded. +# type: filepath +# font_path = fonts/Arimo-Regular.ttf + +# type: filepath +# font_path_bold = fonts/Arimo-Bold.ttf + +# type: filepath +# font_path_italic = fonts/Arimo-Italic.ttf + +# type: filepath +# font_path_bold_italic = fonts/Arimo-BoldItalic.ttf + +# Font size of the monospace font where 1 unit = 1 pixel at 96 DPI +# type: int min: 1 +# mono_font_size = 16 + +# For pixel-style fonts that do not scale well, this ensures that font sizes used +# with this font will always be divisible by this value, in pixels. For instance, +# a pixel font 16 pixels tall should have this set to 16, so it will only ever be +# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. +# type: int min: 1 +# mono_font_size_divisible_by = 1 + +# Path to the monospace font. Must be a TrueType font. +# This font is used for e.g. the console and profiler screen. +# type: filepath +# mono_font_path = fonts/Cousine-Regular.ttf + +# type: filepath +# mono_font_path_bold = fonts/Cousine-Bold.ttf + +# type: filepath +# mono_font_path_italic = fonts/Cousine-Italic.ttf + +# type: filepath +# mono_font_path_bold_italic = fonts/Cousine-BoldItalic.ttf + +# Path of the fallback font. Must be a TrueType font. +# This font will be used for certain languages or if the default font is unavailable. +# type: filepath +# fallback_font_path = fonts/DroidSansFallbackFull.ttf + +# Font size of the recent chat text and chat prompt in point (pt). +# Value 0 will use the default font size. +# type: int +# chat_font_size = 0 + +# Path to save screenshots at. Can be an absolute or relative path. +# The folder will be created if it doesn't already exist. +# type: path +# screenshot_path = screenshots + +# Format of screenshots. +# type: enum values: png, jpg +# screenshot_format = png + +# Screenshot quality. Only used for JPEG format. +# 1 means worst quality; 100 means best quality. +# Use 0 for default quality. +# type: int min: 0 max: 100 +# screenshot_quality = 0 + +## Advanced + +# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. +# type: int min: 1 +# screen_dpi = 72 + +# Adjust the detected display density, used for scaling UI elements. +# type: float +# display_density_factor = 1 + +# Windows systems only: Start Minetest with the command line window in the background. +# Contains the same information as the file debug.txt (default name). +# type: bool +# enable_console = false + +# +# Sound +# + +# Enables the sound system. +# If disabled, this completely disables all sounds everywhere and the in-game +# sound controls will be non-functional. +# Changing this setting requires a restart. +# type: bool +# enable_sound = true + +# Volume of all sounds. +# Requires the sound system to be enabled. +# type: float min: 0 max: 1 +# sound_volume = 0.7 + +# Whether to mute sounds. You can unmute sounds at any time, unless the +# sound system is disabled (enable_sound=false). +# In-game, you can toggle the mute state with the mute key or by using the +# pause menu. +# type: bool +# mute_sound = false + +# +# Client +# + +# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output. +# type: bool +# clickable_chat_weblinks = true + +# Optional override for chat weblink color. +# type: string +# chat_weblink_color = + +## Network + +# Address to connect to. +# Leave this blank to start a local server. +# Note that the address field in the main menu overrides this setting. +# type: string +# address = + +# Port to connect to (UDP). +# Note that the port field in the main menu overrides this setting. +# type: int min: 1 max: 65535 +# remote_port = 30000 + +# Prometheus listener address. +# If Minetest is compiled with ENABLE_PROMETHEUS option enabled, +# enable metrics listener for Prometheus on that address. +# Metrics can be fetched on http://127.0.0.1:30000/metrics +# type: string +# prometheus_listener_address = 127.0.0.1:30000 + +# Save the map received by the client on disk. +# type: bool +# enable_local_map_saving = false + +# Enable usage of remote media server (if provided by server). +# Remote servers offer a significantly faster way to download media (e.g. textures) +# when connecting to the server. +# type: bool +# enable_remote_media_server = true + +# Enable Lua modding support on client. +# This support is experimental and API can change. +# type: bool +# enable_client_modding = false + +# URL to the server list displayed in the Multiplayer Tab. +# type: string +# serverlist_url = servers.minetest.net + +# File in client/serverlist/ that contains your favorite servers displayed in the +# Multiplayer Tab. +# type: string +# serverlist_file = favoriteservers.json + +# Maximum size of the out chat queue. +# 0 to disable queueing and -1 to make the queue size unlimited. +# type: int +# max_out_chat_queue_size = 20 + +# Enable register confirmation when connecting to server. +# If disabled, new account will be registered automatically. +# type: bool +# enable_register_confirmation = true + +## Advanced + +# Timeout for client to remove unused map data from memory. +# type: int +# client_unload_unused_data_timeout = 600 + +# Maximum number of mapblocks for client to be kept in memory. +# Set to -1 for unlimited amount. +# type: int +# client_mapblock_limit = 7500 + +# Whether to show the client debug info (has the same effect as hitting F5). +# type: bool +# show_debug = false + +### Physics + +# Horizontal and vertical acceleration on ground or when climbing, +# in nodes per second per second. +# type: float +# movement_acceleration_default = 3 + +# Horizontal acceleration in air when jumping or falling, +# in nodes per second per second. +# type: float +# movement_acceleration_air = 2 + +# Horizontal and vertical acceleration in fast mode, +# in nodes per second per second. +# type: float +# movement_acceleration_fast = 10 + +# Walking and flying speed, in nodes per second. +# type: float +# movement_speed_walk = 4 + +# Sneaking speed, in nodes per second. +# type: float +# movement_speed_crouch = 1.35 + +# Walking, flying and climbing speed in fast mode, in nodes per second. +# type: float +# movement_speed_fast = 20 + +# Vertical climbing speed, in nodes per second. +# type: float +# movement_speed_climb = 3 + +# Initial vertical speed when jumping, in nodes per second. +# type: float +# movement_speed_jump = 6.5 + +# Decrease this to increase liquid resistance to movement. +# type: float +# movement_liquid_fluidity = 1 + +# Maximum liquid resistance. Controls deceleration when entering liquid at +# high speed. +# type: float +# movement_liquid_fluidity_smooth = 0.5 + +# Controls sinking speed in liquid. +# type: float +# movement_liquid_sink = 10 + +# Acceleration of gravity, in nodes per second per second. +# type: float +# movement_gravity = 9.81 + +### Advanced + +# Handling for deprecated Lua API calls: +# - none: Do not log deprecated calls +# - log: mimic and log backtrace of deprecated call (default). +# - error: abort on usage of deprecated call (suggested for mod developers). +# type: enum values: none, log, error +# deprecated_lua_api_handling = log + +# Number of extra blocks that can be loaded by /clearobjects at once. +# This is a trade-off between SQLite transaction overhead and +# memory consumption (4096=100MB, as a rule of thumb). +# type: int +# max_clearobjects_extra_loaded_blocks = 4096 + +# How much the server will wait before unloading unused mapblocks. +# Higher value is smoother, but will use more RAM. +# type: int +# server_unload_unused_data_timeout = 29 + +# Maximum number of statically stored objects in a block. +# type: int +# max_objects_per_block = 256 + +# See https://www.sqlite.org/pragma.html#pragma_synchronous +# type: enum values: 0, 1, 2 +# sqlite_synchronous = 2 + +# Compression level to use when saving mapblocks to disk. +# -1 - use default compression level +# 0 - least compression, fastest +# 9 - best compression, slowest +# type: int min: -1 max: 9 +# map_compression_level_disk = -1 + +# Length of a server tick and the interval at which objects are generally updated over +# network. +# type: float +# dedicated_server_step = 0.09 + +# Length of time between active block management cycles +# type: float +# active_block_mgmt_interval = 2.0 + +# Length of time between Active Block Modifier (ABM) execution cycles +# type: float +# abm_interval = 1.0 + +# The time budget allowed for ABMs to execute on each step +# (as a fraction of the ABM Interval) +# type: float min: 0.1 max: 0.9 +# abm_time_budget = 0.2 + +# Length of time between NodeTimer execution cycles +# type: float +# nodetimer_interval = 0.2 + +# If enabled, invalid world data won't cause the server to shut down. +# Only enable this if you know what you are doing. +# type: bool +# ignore_world_load_errors = false + +# Max liquids processed per step. +# type: int +# liquid_loop_max = 100000 + +# The time (in seconds) that the liquids queue may grow beyond processing +# capacity until an attempt is made to decrease its size by dumping old queue +# items. A value of 0 disables the functionality. +# type: int +# liquid_queue_purge_time = 0 + +# Liquid update interval in seconds. +# type: float +# liquid_update = 1.0 + +# At this distance the server will aggressively optimize which blocks are sent to +# clients. +# Small values potentially improve performance a lot, at the expense of visible +# rendering glitches (some blocks will not be rendered under water and in caves, +# as well as sometimes on land). +# Setting this to a value greater than max_block_send_distance disables this +# optimization. +# Stated in mapblocks (16 nodes). +# type: int min: 2 +# block_send_optimize_distance = 4 + +# If enabled the server will perform map block occlusion culling based on +# on the eye position of the player. This can reduce the number of blocks +# sent to the client 50-80%. The client will not longer receive most invisible +# so that the utility of noclip mode is reduced. +# type: bool +# server_side_occlusion_culling = true + +# Restricts the access of certain client-side functions on servers. +# Combine the byteflags below to restrict client-side features, or set to 0 +# for no restrictions: +# LOAD_CLIENT_MODS: 1 (disable loading client-provided mods) +# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) +# READ_ITEMDEFS: 4 (disable get_item_def call client-side) +# READ_NODEDEFS: 8 (disable get_node_def call client-side) +# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to +# csm_restriction_noderange) +# READ_PLAYERINFO: 32 (disable get_player_names call client-side) +# type: int +# csm_restriction_flags = 62 + +# If the CSM restriction for node range is enabled, get_node calls are limited +# to this distance from the player to the node. +# type: int +# csm_restriction_noderange = 0 + +## Security + +# Prevent mods from doing insecure things like running shell commands. +# type: bool +# secure.enable_security = true + +# Comma-separated list of trusted mods that are allowed to access insecure +# functions even when mod security is on (via request_insecure_environment()). +# type: string +# secure.trusted_mods = + +# Comma-separated list of mods that are allowed to access HTTP APIs, which +# allow them to upload and download data to/from the internet. +# type: string +# secure.http_mods = + +## Advanced + +### Profiling + +# Load the game profiler to collect game profiling data. +# Provides a /profiler command to access the compiled profile. +# Useful for mod developers and server operators. +# type: bool +# profiler.load = false + +# The default format in which profiles are being saved, +# when calling `/profiler save [format]` without format. +# type: enum values: txt, csv, lua, json, json_pretty +# profiler.default_report_format = txt + +# The file path relative to your worldpath in which profiles will be saved to. +# type: string +# profiler.report_path = "" + +#### Instrumentation + +# Instrument the methods of entities on registration. +# type: bool +# instrument.entity = true + +# Instrument the action function of Active Block Modifiers on registration. +# type: bool +# instrument.abm = true + +# Instrument the action function of Loading Block Modifiers on registration. +# type: bool +# instrument.lbm = true + +# Instrument chat commands on registration. +# type: bool +# instrument.chatcommand = true + +# Instrument global callback functions on registration. +# (anything you pass to a minetest.register_*() function) +# type: bool +# instrument.global_callback = true + +##### Advanced + +# Instrument builtin. +# This is usually only needed by core/builtin contributors +# type: bool +# instrument.builtin = false + +# Have the profiler instrument itself: +# * Instrument an empty function. +# This estimates the overhead, that instrumentation is adding (+1 function call). +# * Instrument the sampler being used to update the statistics. +# type: bool +# instrument.profiler = false + +# +# Client and Server +# + +# Name of the player. +# When running a server, clients connecting with this name are admins. +# When starting from the main menu, this is overridden. +# type: string +# name = + +# Set the language. Leave empty to use the system language. +# A restart is required after changing this. +# type: enum values: , be, bg, ca, cs, da, de, el, en, eo, es, et, eu, fi, fr, gd, gl, hu, id, it, ja, jbo, kk, ko, lt, lv, ms, nb, nl, nn, pl, pt, pt_BR, ro, ru, sk, sl, sr_Cyrl, sr_Latn, sv, sw, tr, uk, vi, zh_CN, zh_TW +# language = + +# Level of logging to be written to debug.txt: +# - (no logging) +# - none (messages with no level) +# - error +# - warning +# - action +# - info +# - verbose +# - trace +# type: enum values: , none, error, warning, action, info, verbose, trace +# debug_log_level = action + +# If the file size of debug.txt exceeds the number of megabytes specified in +# this setting when it is opened, the file is moved to debug.txt.1, +# deleting an older debug.txt.1 if it exists. +# debug.txt is only moved if this setting is positive. +# type: int +# debug_log_size_max = 50 + +# Minimal level of logging to be written to chat. +# type: enum values: , none, error, warning, action, info, verbose, trace +# chat_log_level = error + +# Enable IPv6 support (for both client and server). +# Required for IPv6 connections to work at all. +# type: bool +# enable_ipv6 = true + +## Advanced + +# Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds. +# type: int +# curl_timeout = 20000 + +# Limits number of parallel HTTP requests. Affects: +# - Media fetch if server uses remote_media setting. +# - Serverlist download and server announcement. +# - Downloads performed by main menu (e.g. mod manager). +# Only has an effect if compiled with cURL. +# type: int +# curl_parallel_limit = 8 + +# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds. +# type: int +# curl_file_download_timeout = 300000 + +# Replaces the default main menu with a custom one. +# type: string +# main_menu_script = + +# Print the engine's profiling data in regular intervals (in seconds). +# 0 = disable. Useful for developers. +# type: int +# profiler_print_interval = 0 + +# +# Mapgen +# + +# Name of map generator to be used when creating a new world. +# Creating a world in the main menu will override this. +# Current mapgens in a highly unstable state: +# - The optional floatlands of v7 (disabled by default). +# type: enum values: v7, valleys, carpathian, v5, flat, fractal, singlenode, v6 +# mg_name = v7 + +# Water surface level of the world. +# type: int +# water_level = 1 + +# From how far blocks are generated for clients, stated in mapblocks (16 nodes). +# type: int +# max_block_generate_distance = 10 + +# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). +# Only mapchunks completely within the mapgen limit are generated. +# Value is stored per-world. +# type: int min: 0 max: 31007 +# mapgen_limit = 31007 + +# Global map generation attributes. +# In Mapgen v6 the 'decorations' flag controls all decorations except trees +# and jungle grass, in all other mapgens this flag controls all decorations. +# type: flags possible values: caves, dungeons, light, decorations, biomes, ores, nocaves, nodungeons, nolight, nodecorations, nobiomes, noores +# mg_flags = caves,dungeons,light,decorations,biomes,ores + +## Biome API temperature and humidity noise parameters + +# Temperature variation for biomes. +# type: noise_params_2d +# mg_biome_np_heat = { +# offset = 50, +# scale = 50, +# spread = (1000, 1000, 1000), +# seed = 5349, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# Small-scale temperature variation for blending biomes on borders. +# type: noise_params_2d +# mg_biome_np_heat_blend = { +# offset = 0, +# scale = 1.5, +# spread = (8, 8, 8), +# seed = 13, +# octaves = 2, +# persistence = 1.0, +# lacunarity = 2.0, +# flags = eased +# } + +# Humidity variation for biomes. +# type: noise_params_2d +# mg_biome_np_humidity = { +# offset = 50, +# scale = 50, +# spread = (1000, 1000, 1000), +# seed = 842, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# Small-scale humidity variation for blending biomes on borders. +# type: noise_params_2d +# mg_biome_np_humidity_blend = { +# offset = 0, +# scale = 1.5, +# spread = (8, 8, 8), +# seed = 90003, +# octaves = 2, +# persistence = 1.0, +# lacunarity = 2.0, +# flags = eased +# } + +## Mapgen V5 + +# Map generation attributes specific to Mapgen v5. +# type: flags possible values: caverns, nocaverns +# mgv5_spflags = caverns + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgv5_cave_width = 0.09 + +# Y of upper limit of large caves. +# type: int +# mgv5_large_cave_depth = -256 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgv5_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgv5_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgv5_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgv5_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgv5_large_cave_flooded = 0.5 + +# Y-level of cavern upper limit. +# type: int +# mgv5_cavern_limit = -256 + +# Y-distance over which caverns expand to full size. +# type: int +# mgv5_cavern_taper = 256 + +# Defines full size of caverns, smaller values create larger caverns. +# type: float +# mgv5_cavern_threshold = 0.7 + +# Lower Y limit of dungeons. +# type: int +# mgv5_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgv5_dungeon_ymax = 31000 + +### Noises + +# Variation of biome filler depth. +# type: noise_params_2d +# mgv5_np_filler_depth = { +# offset = 0, +# scale = 1, +# spread = (150, 150, 150), +# seed = 261, +# octaves = 4, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of terrain vertical scale. +# When noise is < -0.55 terrain is near-flat. +# type: noise_params_2d +# mgv5_np_factor = { +# offset = 0, +# scale = 1, +# spread = (250, 250, 250), +# seed = 920381, +# octaves = 3, +# persistence = 0.45, +# lacunarity = 2.0, +# flags = eased +# } + +# Y-level of average terrain surface. +# type: noise_params_2d +# mgv5_np_height = { +# offset = 0, +# scale = 10, +# spread = (250, 250, 250), +# seed = 84174, +# octaves = 4, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgv5_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgv5_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining giant caverns. +# type: noise_params_3d +# mgv5_np_cavern = { +# offset = 0, +# scale = 1, +# spread = (384, 128, 384), +# seed = 723, +# octaves = 5, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining terrain. +# type: noise_params_3d +# mgv5_np_ground = { +# offset = 0, +# scale = 40, +# spread = (80, 80, 80), +# seed = 983240, +# octaves = 4, +# persistence = 0.55, +# lacunarity = 2.0, +# flags = eased +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgv5_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Mapgen V6 + +# Map generation attributes specific to Mapgen v6. +# The 'snowbiomes' flag enables the new 5 biome system. +# When the 'snowbiomes' flag is enabled jungles are automatically enabled and +# the 'jungles' flag is ignored. +# type: flags possible values: jungles, biomeblend, mudflow, snowbiomes, flat, trees, nojungles, nobiomeblend, nomudflow, nosnowbiomes, noflat, notrees +# mgv6_spflags = jungles,biomeblend,mudflow,snowbiomes,noflat,trees + +# Deserts occur when np_biome exceeds this value. +# When the 'snowbiomes' flag is enabled, this is ignored. +# type: float +# mgv6_freq_desert = 0.45 + +# Sandy beaches occur when np_beach exceeds this value. +# type: float +# mgv6_freq_beach = 0.15 + +# Lower Y limit of dungeons. +# type: int +# mgv6_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgv6_dungeon_ymax = 31000 + +### Noises + +# Y-level of lower terrain and seabed. +# type: noise_params_2d +# mgv6_np_terrain_base = { +# offset = -4, +# scale = 20, +# spread = (250, 250, 250), +# seed = 82341, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Y-level of higher terrain that creates cliffs. +# type: noise_params_2d +# mgv6_np_terrain_higher = { +# offset = 20, +# scale = 16, +# spread = (500, 500, 500), +# seed = 85039, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Varies steepness of cliffs. +# type: noise_params_2d +# mgv6_np_steepness = { +# offset = 0.85, +# scale = 0.5, +# spread = (125, 125, 125), +# seed = -932, +# octaves = 5, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines distribution of higher terrain. +# type: noise_params_2d +# mgv6_np_height_select = { +# offset = 0.5, +# scale = 1, +# spread = (250, 250, 250), +# seed = 4213, +# octaves = 5, +# persistence = 0.69, +# lacunarity = 2.0, +# flags = eased +# } + +# Varies depth of biome surface nodes. +# type: noise_params_2d +# mgv6_np_mud = { +# offset = 4, +# scale = 2, +# spread = (200, 200, 200), +# seed = 91013, +# octaves = 3, +# persistence = 0.55, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines areas with sandy beaches. +# type: noise_params_2d +# mgv6_np_beach = { +# offset = 0, +# scale = 1, +# spread = (250, 250, 250), +# seed = 59420, +# octaves = 3, +# persistence = 0.50, +# lacunarity = 2.0, +# flags = eased +# } + +# Temperature variation for biomes. +# type: noise_params_2d +# mgv6_np_biome = { +# offset = 0, +# scale = 1, +# spread = (500, 500, 500), +# seed = 9130, +# octaves = 3, +# persistence = 0.50, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of number of caves. +# type: noise_params_2d +# mgv6_np_cave = { +# offset = 6, +# scale = 6, +# spread = (250, 250, 250), +# seed = 34329, +# octaves = 3, +# persistence = 0.50, +# lacunarity = 2.0, +# flags = eased +# } + +# Humidity variation for biomes. +# type: noise_params_2d +# mgv6_np_humidity = { +# offset = 0.5, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 72384, +# octaves = 3, +# persistence = 0.50, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines tree areas and tree density. +# type: noise_params_2d +# mgv6_np_trees = { +# offset = 0, +# scale = 1, +# spread = (125, 125, 125), +# seed = 2, +# octaves = 4, +# persistence = 0.66, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines areas where trees have apples. +# type: noise_params_2d +# mgv6_np_apple_trees = { +# offset = 0, +# scale = 1, +# spread = (100, 100, 100), +# seed = 342902, +# octaves = 3, +# persistence = 0.45, +# lacunarity = 2.0, +# flags = eased +# } + +## Mapgen V7 + +# Map generation attributes specific to Mapgen v7. +# 'ridges': Rivers. +# 'floatlands': Floating land masses in the atmosphere. +# 'caverns': Giant caves deep underground. +# type: flags possible values: mountains, ridges, floatlands, caverns, nomountains, noridges, nofloatlands, nocaverns +# mgv7_spflags = mountains,ridges,nofloatlands,caverns + +# Y of mountain density gradient zero level. Used to shift mountains vertically. +# type: int +# mgv7_mount_zero_level = 0 + +# Lower Y limit of floatlands. +# type: int +# mgv7_floatland_ymin = 1024 + +# Upper Y limit of floatlands. +# type: int +# mgv7_floatland_ymax = 4096 + +# Y-distance over which floatlands taper from full density to nothing. +# Tapering starts at this distance from the Y limit. +# For a solid floatland layer, this controls the height of hills/mountains. +# Must be less than or equal to half the distance between the Y limits. +# type: int +# mgv7_floatland_taper = 256 + +# Exponent of the floatland tapering. Alters the tapering behaviour. +# Value = 1.0 creates a uniform, linear tapering. +# Values > 1.0 create a smooth tapering suitable for the default separated +# floatlands. +# Values < 1.0 (for example 0.25) create a more defined surface level with +# flatter lowlands, suitable for a solid floatland layer. +# type: float +# mgv7_float_taper_exp = 2.0 + +# Adjusts the density of the floatland layer. +# Increase value to increase density. Can be positive or negative. +# Value = 0.0: 50% of volume is floatland. +# Value = 2.0 (can be higher depending on 'mgv7_np_floatland', always test +# to be sure) creates a solid floatland layer. +# type: float +# mgv7_floatland_density = -0.6 + +# Surface level of optional water placed on a solid floatland layer. +# Water is disabled by default and will only be placed if this value is set +# to above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the +# upper tapering). +# ***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***: +# When enabling water placement the floatlands must be configured and tested +# to be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other +# required value depending on 'mgv7_np_floatland'), to avoid +# server-intensive extreme water flow and to avoid vast flooding of the +# world surface below. +# type: int +# mgv7_floatland_ywater = -31000 + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgv7_cave_width = 0.09 + +# Y of upper limit of large caves. +# type: int +# mgv7_large_cave_depth = -33 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgv7_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgv7_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgv7_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgv7_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgv7_large_cave_flooded = 0.5 + +# Y-level of cavern upper limit. +# type: int +# mgv7_cavern_limit = -256 + +# Y-distance over which caverns expand to full size. +# type: int +# mgv7_cavern_taper = 256 + +# Defines full size of caverns, smaller values create larger caverns. +# type: float +# mgv7_cavern_threshold = 0.7 + +# Lower Y limit of dungeons. +# type: int +# mgv7_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgv7_dungeon_ymax = 31000 + +### Noises + +# Y-level of higher terrain that creates cliffs. +# type: noise_params_2d +# mgv7_np_terrain_base = { +# offset = 4, +# scale = 70, +# spread = (600, 600, 600), +# seed = 82341, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Y-level of lower terrain and seabed. +# type: noise_params_2d +# mgv7_np_terrain_alt = { +# offset = 4, +# scale = 25, +# spread = (600, 600, 600), +# seed = 5934, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Varies roughness of terrain. +# Defines the 'persistence' value for terrain_base and terrain_alt noises. +# type: noise_params_2d +# mgv7_np_terrain_persist = { +# offset = 0.6, +# scale = 0.1, +# spread = (2000, 2000, 2000), +# seed = 539, +# octaves = 3, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines distribution of higher terrain and steepness of cliffs. +# type: noise_params_2d +# mgv7_np_height_select = { +# offset = -8, +# scale = 16, +# spread = (500, 500, 500), +# seed = 4213, +# octaves = 6, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of biome filler depth. +# type: noise_params_2d +# mgv7_np_filler_depth = { +# offset = 0, +# scale = 1.2, +# spread = (150, 150, 150), +# seed = 261, +# octaves = 3, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of maximum mountain height (in nodes). +# type: noise_params_2d +# mgv7_np_mount_height = { +# offset = 256, +# scale = 112, +# spread = (1000, 1000, 1000), +# seed = 72449, +# octaves = 3, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Defines large-scale river channel structure. +# type: noise_params_2d +# mgv7_np_ridge_uwater = { +# offset = 0, +# scale = 1, +# spread = (1000, 1000, 1000), +# seed = 85039, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# 3D noise defining mountain structure and height. +# Also defines structure of floatland mountain terrain. +# type: noise_params_3d +# mgv7_np_mountain = { +# offset = -0.6, +# scale = 1, +# spread = (250, 350, 250), +# seed = 5333, +# octaves = 5, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining structure of river canyon walls. +# type: noise_params_3d +# mgv7_np_ridge = { +# offset = 0, +# scale = 1, +# spread = (100, 100, 100), +# seed = 6467, +# octaves = 4, +# persistence = 0.75, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining structure of floatlands. +# If altered from the default, the noise 'scale' (0.7 by default) may need +# to be adjusted, as floatland tapering functions best when this noise has +# a value range of approximately -2.0 to 2.0. +# type: noise_params_3d +# mgv7_np_floatland = { +# offset = 0, +# scale = 0.7, +# spread = (384, 96, 384), +# seed = 1009, +# octaves = 4, +# persistence = 0.75, +# lacunarity = 1.618, +# flags = +# } + +# 3D noise defining giant caverns. +# type: noise_params_3d +# mgv7_np_cavern = { +# offset = 0, +# scale = 1, +# spread = (384, 128, 384), +# seed = 723, +# octaves = 5, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgv7_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgv7_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgv7_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Mapgen Carpathian + +# Map generation attributes specific to Mapgen Carpathian. +# type: flags possible values: caverns, rivers, nocaverns, norivers +# mgcarpathian_spflags = caverns,norivers + +# Defines the base ground level. +# type: float +# mgcarpathian_base_level = 12.0 + +# Defines the width of the river channel. +# type: float +# mgcarpathian_river_width = 0.05 + +# Defines the depth of the river channel. +# type: float +# mgcarpathian_river_depth = 24.0 + +# Defines the width of the river valley. +# type: float +# mgcarpathian_valley_width = 0.25 + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgcarpathian_cave_width = 0.09 + +# Y of upper limit of large caves. +# type: int +# mgcarpathian_large_cave_depth = -33 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgcarpathian_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgcarpathian_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgcarpathian_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgcarpathian_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgcarpathian_large_cave_flooded = 0.5 + +# Y-level of cavern upper limit. +# type: int +# mgcarpathian_cavern_limit = -256 + +# Y-distance over which caverns expand to full size. +# type: int +# mgcarpathian_cavern_taper = 256 + +# Defines full size of caverns, smaller values create larger caverns. +# type: float +# mgcarpathian_cavern_threshold = 0.7 + +# Lower Y limit of dungeons. +# type: int +# mgcarpathian_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgcarpathian_dungeon_ymax = 31000 + +### Noises + +# Variation of biome filler depth. +# type: noise_params_2d +# mgcarpathian_np_filler_depth = { +# offset = 0, +# scale = 1, +# spread = (128, 128, 128), +# seed = 261, +# octaves = 3, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# First of 4 2D noises that together define hill/mountain range height. +# type: noise_params_2d +# mgcarpathian_np_height1 = { +# offset = 0, +# scale = 5, +# spread = (251, 251, 251), +# seed = 9613, +# octaves = 5, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# Second of 4 2D noises that together define hill/mountain range height. +# type: noise_params_2d +# mgcarpathian_np_height2 = { +# offset = 0, +# scale = 5, +# spread = (383, 383, 383), +# seed = 1949, +# octaves = 5, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# Third of 4 2D noises that together define hill/mountain range height. +# type: noise_params_2d +# mgcarpathian_np_height3 = { +# offset = 0, +# scale = 5, +# spread = (509, 509, 509), +# seed = 3211, +# octaves = 5, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# Fourth of 4 2D noises that together define hill/mountain range height. +# type: noise_params_2d +# mgcarpathian_np_height4 = { +# offset = 0, +# scale = 5, +# spread = (631, 631, 631), +# seed = 1583, +# octaves = 5, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the size/occurrence of rolling hills. +# type: noise_params_2d +# mgcarpathian_np_hills_terrain = { +# offset = 1, +# scale = 1, +# spread = (1301, 1301, 1301), +# seed = 1692, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the size/occurrence of ridged mountain ranges. +# type: noise_params_2d +# mgcarpathian_np_ridge_terrain = { +# offset = 1, +# scale = 1, +# spread = (1889, 1889, 1889), +# seed = 3568, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the size/occurrence of step mountain ranges. +# type: noise_params_2d +# mgcarpathian_np_step_terrain = { +# offset = 1, +# scale = 1, +# spread = (1889, 1889, 1889), +# seed = 4157, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the shape/size of rolling hills. +# type: noise_params_2d +# mgcarpathian_np_hills = { +# offset = 0, +# scale = 3, +# spread = (257, 257, 257), +# seed = 6604, +# octaves = 6, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the shape/size of ridged mountains. +# type: noise_params_2d +# mgcarpathian_np_ridge_mnt = { +# offset = 0, +# scale = 12, +# spread = (743, 743, 743), +# seed = 5520, +# octaves = 6, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that controls the shape/size of step mountains. +# type: noise_params_2d +# mgcarpathian_np_step_mnt = { +# offset = 0, +# scale = 8, +# spread = (509, 509, 509), +# seed = 2590, +# octaves = 6, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# 2D noise that locates the river valleys and channels. +# type: noise_params_2d +# mgcarpathian_np_rivers = { +# offset = 0, +# scale = 1, +# spread = (1000, 1000, 1000), +# seed = 85039, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# 3D noise for mountain overhangs, cliffs, etc. Usually small variations. +# type: noise_params_3d +# mgcarpathian_np_mnt_var = { +# offset = 0, +# scale = 1, +# spread = (499, 499, 499), +# seed = 2490, +# octaves = 5, +# persistence = 0.55, +# lacunarity = 2.0, +# flags = +# } + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgcarpathian_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgcarpathian_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining giant caverns. +# type: noise_params_3d +# mgcarpathian_np_cavern = { +# offset = 0, +# scale = 1, +# spread = (384, 128, 384), +# seed = 723, +# octaves = 5, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgcarpathian_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Mapgen Flat + +# Map generation attributes specific to Mapgen Flat. +# Occasional lakes and hills can be added to the flat world. +# type: flags possible values: lakes, hills, caverns, nolakes, nohills, nocaverns +# mgflat_spflags = nolakes,nohills,nocaverns + +# Y of flat ground. +# type: int +# mgflat_ground_level = 8 + +# Y of upper limit of large caves. +# type: int +# mgflat_large_cave_depth = -33 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgflat_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgflat_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgflat_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgflat_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgflat_large_cave_flooded = 0.5 + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgflat_cave_width = 0.09 + +# Terrain noise threshold for lakes. +# Controls proportion of world area covered by lakes. +# Adjust towards 0.0 for a larger proportion. +# type: float +# mgflat_lake_threshold = -0.45 + +# Controls steepness/depth of lake depressions. +# type: float +# mgflat_lake_steepness = 48.0 + +# Terrain noise threshold for hills. +# Controls proportion of world area covered by hills. +# Adjust towards 0.0 for a larger proportion. +# type: float +# mgflat_hill_threshold = 0.45 + +# Controls steepness/height of hills. +# type: float +# mgflat_hill_steepness = 64.0 + +# Y-level of cavern upper limit. +# type: int +# mgflat_cavern_limit = -256 + +# Y-distance over which caverns expand to full size. +# type: int +# mgflat_cavern_taper = 256 + +# Defines full size of caverns, smaller values create larger caverns. +# type: float +# mgflat_cavern_threshold = 0.7 + +# Lower Y limit of dungeons. +# type: int +# mgflat_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgflat_dungeon_ymax = 31000 + +### Noises + +# Defines location and terrain of optional hills and lakes. +# type: noise_params_2d +# mgflat_np_terrain = { +# offset = 0, +# scale = 1, +# spread = (600, 600, 600), +# seed = 7244, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of biome filler depth. +# type: noise_params_2d +# mgflat_np_filler_depth = { +# offset = 0, +# scale = 1.2, +# spread = (150, 150, 150), +# seed = 261, +# octaves = 3, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgflat_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgflat_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise defining giant caverns. +# type: noise_params_3d +# mgflat_np_cavern = { +# offset = 0, +# scale = 1, +# spread = (384, 128, 384), +# seed = 723, +# octaves = 5, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgflat_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Mapgen Fractal + +# Map generation attributes specific to Mapgen Fractal. +# 'terrain' enables the generation of non-fractal terrain: +# ocean, islands and underground. +# type: flags possible values: terrain, noterrain +# mgfractal_spflags = terrain + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgfractal_cave_width = 0.09 + +# Y of upper limit of large caves. +# type: int +# mgfractal_large_cave_depth = -33 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgfractal_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgfractal_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgfractal_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgfractal_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgfractal_large_cave_flooded = 0.5 + +# Lower Y limit of dungeons. +# type: int +# mgfractal_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgfractal_dungeon_ymax = 31000 + +# Selects one of 18 fractal types. +# 1 = 4D "Roundy" Mandelbrot set. +# 2 = 4D "Roundy" Julia set. +# 3 = 4D "Squarry" Mandelbrot set. +# 4 = 4D "Squarry" Julia set. +# 5 = 4D "Mandy Cousin" Mandelbrot set. +# 6 = 4D "Mandy Cousin" Julia set. +# 7 = 4D "Variation" Mandelbrot set. +# 8 = 4D "Variation" Julia set. +# 9 = 3D "Mandelbrot/Mandelbar" Mandelbrot set. +# 10 = 3D "Mandelbrot/Mandelbar" Julia set. +# 11 = 3D "Christmas Tree" Mandelbrot set. +# 12 = 3D "Christmas Tree" Julia set. +# 13 = 3D "Mandelbulb" Mandelbrot set. +# 14 = 3D "Mandelbulb" Julia set. +# 15 = 3D "Cosine Mandelbulb" Mandelbrot set. +# 16 = 3D "Cosine Mandelbulb" Julia set. +# 17 = 4D "Mandelbulb" Mandelbrot set. +# 18 = 4D "Mandelbulb" Julia set. +# type: int min: 1 max: 18 +# mgfractal_fractal = 1 + +# Iterations of the recursive function. +# Increasing this increases the amount of fine detail, but also +# increases processing load. +# At iterations = 20 this mapgen has a similar load to mapgen V7. +# type: int +# mgfractal_iterations = 11 + +# (X,Y,Z) scale of fractal in nodes. +# Actual fractal size will be 2 to 3 times larger. +# These numbers can be made very large, the fractal does +# not have to fit inside the world. +# Increase these to 'zoom' into the detail of the fractal. +# Default is for a vertically-squashed shape suitable for +# an island, set all 3 numbers equal for the raw shape. +# type: v3f +# mgfractal_scale = (4096.0, 1024.0, 4096.0) + +# (X,Y,Z) offset of fractal from world center in units of 'scale'. +# Can be used to move a desired point to (0, 0) to create a +# suitable spawn point, or to allow 'zooming in' on a desired +# point by increasing 'scale'. +# The default is tuned for a suitable spawn point for Mandelbrot +# sets with default parameters, it may need altering in other +# situations. +# Range roughly -2 to 2. Multiply by 'scale' for offset in nodes. +# type: v3f +# mgfractal_offset = (1.79, 0.0, 0.0) + +# W coordinate of the generated 3D slice of a 4D fractal. +# Determines which 3D slice of the 4D shape is generated. +# Alters the shape of the fractal. +# Has no effect on 3D fractals. +# Range roughly -2 to 2. +# type: float +# mgfractal_slice_w = 0.0 + +# Julia set only. +# X component of hypercomplex constant. +# Alters the shape of the fractal. +# Range roughly -2 to 2. +# type: float +# mgfractal_julia_x = 0.33 + +# Julia set only. +# Y component of hypercomplex constant. +# Alters the shape of the fractal. +# Range roughly -2 to 2. +# type: float +# mgfractal_julia_y = 0.33 + +# Julia set only. +# Z component of hypercomplex constant. +# Alters the shape of the fractal. +# Range roughly -2 to 2. +# type: float +# mgfractal_julia_z = 0.33 + +# Julia set only. +# W component of hypercomplex constant. +# Alters the shape of the fractal. +# Has no effect on 3D fractals. +# Range roughly -2 to 2. +# type: float +# mgfractal_julia_w = 0.33 + +### Noises + +# Y-level of seabed. +# type: noise_params_2d +# mgfractal_np_seabed = { +# offset = -14, +# scale = 9, +# spread = (600, 600, 600), +# seed = 41900, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Variation of biome filler depth. +# type: noise_params_2d +# mgfractal_np_filler_depth = { +# offset = 0, +# scale = 1.2, +# spread = (150, 150, 150), +# seed = 261, +# octaves = 3, +# persistence = 0.7, +# lacunarity = 2.0, +# flags = eased +# } + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgfractal_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgfractal_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgfractal_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Mapgen Valleys + +# Map generation attributes specific to Mapgen Valleys. +# 'altitude_chill': Reduces heat with altitude. +# 'humid_rivers': Increases humidity around rivers. +# 'vary_river_depth': If enabled, low humidity and high heat causes rivers +# to become shallower and occasionally dry. +# 'altitude_dry': Reduces humidity with altitude. +# type: flags possible values: altitude_chill, humid_rivers, vary_river_depth, altitude_dry, noaltitude_chill, nohumid_rivers, novary_river_depth, noaltitude_dry +# mgvalleys_spflags = altitude_chill,humid_rivers,vary_river_depth,altitude_dry + +# The vertical distance over which heat drops by 20 if 'altitude_chill' is +# enabled. Also the vertical distance over which humidity drops by 10 if +# 'altitude_dry' is enabled. +# type: int +# mgvalleys_altitude_chill = 90 + +# Depth below which you'll find large caves. +# type: int +# mgvalleys_large_cave_depth = -33 + +# Minimum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgvalleys_small_cave_num_min = 0 + +# Maximum limit of random number of small caves per mapchunk. +# type: int min: 0 max: 256 +# mgvalleys_small_cave_num_max = 0 + +# Minimum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgvalleys_large_cave_num_min = 0 + +# Maximum limit of random number of large caves per mapchunk. +# type: int min: 0 max: 64 +# mgvalleys_large_cave_num_max = 2 + +# Proportion of large caves that contain liquid. +# type: float min: 0 max: 1 +# mgvalleys_large_cave_flooded = 0.5 + +# Depth below which you'll find giant caverns. +# type: int +# mgvalleys_cavern_limit = -256 + +# Y-distance over which caverns expand to full size. +# type: int +# mgvalleys_cavern_taper = 192 + +# Defines full size of caverns, smaller values create larger caverns. +# type: float +# mgvalleys_cavern_threshold = 0.6 + +# How deep to make rivers. +# type: int +# mgvalleys_river_depth = 4 + +# How wide to make rivers. +# type: int +# mgvalleys_river_size = 5 + +# Controls width of tunnels, a smaller value creates wider tunnels. +# Value >= 10.0 completely disables generation of tunnels and avoids the +# intensive noise calculations. +# type: float +# mgvalleys_cave_width = 0.09 + +# Lower Y limit of dungeons. +# type: int +# mgvalleys_dungeon_ymin = -31000 + +# Upper Y limit of dungeons. +# type: int +# mgvalleys_dungeon_ymax = 63 + +### Noises + +# First of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgvalleys_np_cave1 = { +# offset = 0, +# scale = 12, +# spread = (61, 61, 61), +# seed = 52534, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# Second of two 3D noises that together define tunnels. +# type: noise_params_3d +# mgvalleys_np_cave2 = { +# offset = 0, +# scale = 12, +# spread = (67, 67, 67), +# seed = 10325, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = +# } + +# The depth of dirt or other biome filler node. +# type: noise_params_2d +# mgvalleys_np_filler_depth = { +# offset = 0, +# scale = 1.2, +# spread = (256, 256, 256), +# seed = 1605, +# octaves = 3, +# persistence = 0.5, +# lacunarity = 2.0, +# flags = eased +# } + +# 3D noise defining giant caverns. +# type: noise_params_3d +# mgvalleys_np_cavern = { +# offset = 0, +# scale = 1, +# spread = (768, 256, 768), +# seed = 59033, +# octaves = 6, +# persistence = 0.63, +# lacunarity = 2.0, +# flags = +# } + +# Defines large-scale river channel structure. +# type: noise_params_2d +# mgvalleys_np_rivers = { +# offset = 0, +# scale = 1, +# spread = (256, 256, 256), +# seed = -6050, +# octaves = 5, +# persistence = 0.6, +# lacunarity = 2.0, +# flags = eased +# } + +# Base terrain height. +# type: noise_params_2d +# mgvalleys_np_terrain_height = { +# offset = -10, +# scale = 50, +# spread = (1024, 1024, 1024), +# seed = 5202, +# octaves = 6, +# persistence = 0.4, +# lacunarity = 2.0, +# flags = eased +# } + +# Raises terrain to make valleys around the rivers. +# type: noise_params_2d +# mgvalleys_np_valley_depth = { +# offset = 5, +# scale = 4, +# spread = (512, 512, 512), +# seed = -1914, +# octaves = 1, +# persistence = 1.0, +# lacunarity = 2.0, +# flags = eased +# } + +# Slope and fill work together to modify the heights. +# type: noise_params_3d +# mgvalleys_np_inter_valley_fill = { +# offset = 0, +# scale = 1, +# spread = (256, 512, 256), +# seed = 1993, +# octaves = 6, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +# Amplifies the valleys. +# type: noise_params_2d +# mgvalleys_np_valley_profile = { +# offset = 0.6, +# scale = 0.5, +# spread = (512, 512, 512), +# seed = 777, +# octaves = 1, +# persistence = 1.0, +# lacunarity = 2.0, +# flags = eased +# } + +# Slope and fill work together to modify the heights. +# type: noise_params_2d +# mgvalleys_np_inter_valley_slope = { +# offset = 0.5, +# scale = 0.5, +# spread = (128, 128, 128), +# seed = 746, +# octaves = 1, +# persistence = 1.0, +# lacunarity = 2.0, +# flags = eased +# } + +# 3D noise that determines number of dungeons per mapchunk. +# type: noise_params_3d +# mgvalleys_np_dungeons = { +# offset = 0.9, +# scale = 0.5, +# spread = (500, 500, 500), +# seed = 0, +# octaves = 2, +# persistence = 0.8, +# lacunarity = 2.0, +# flags = +# } + +## Advanced + +# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes). +# WARNING!: There is no benefit, and there are several dangers, in +# increasing this value above 5. +# Reducing this value increases cave and dungeon density. +# Altering this value is for special usage, leaving it unchanged is +# recommended. +# type: int +# chunksize = 5 + +# Dump the mapgen debug information. +# type: bool +# enable_mapgen_debug_info = false + +# Maximum number of blocks that can be queued for loading. +# type: int min: 1 max: 1000000 +# emergequeue_limit_total = 1024 + +# Maximum number of blocks to be queued that are to be loaded from file. +# This limit is enforced per player. +# type: int min: 1 max: 1000000 +# emergequeue_limit_diskonly = 128 + +# Maximum number of blocks to be queued that are to be generated. +# This limit is enforced per player. +# type: int min: 1 max: 1000000 +# emergequeue_limit_generate = 128 + +# Number of emerge threads to use. +# Value 0: +# - Automatic selection. The number of emerge threads will be +# - 'number of processors - 2', with a lower limit of 1. +# Any other value: +# - Specifies the number of emerge threads, with a lower limit of 1. +# WARNING: Increasing the number of emerge threads increases engine mapgen +# speed, but this may harm game performance by interfering with other +# processes, especially in singleplayer and/or when running Lua code in +# 'on_generated'. For many users the optimum setting may be '1'. +# type: int +# num_emerge_threads = 1 + +# +# Online Content Repository +# + +# The URL for the content repository +# type: string +# contentdb_url = https://content.minetest.net + +# Comma-separated list of flags to hide in the content repository. +# "nonfree" can be used to hide packages which do not qualify as 'free software', +# as defined by the Free Software Foundation. +# You can also specify content ratings. +# These flags are independent from Minetest versions, +# so see a full list at https://content.minetest.net/help/content_flags/ +# type: string +# contentdb_flag_blacklist = nonfree, desktop_default + +# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued. +# This should be lower than curl_parallel_limit. +# type: int +# contentdb_max_concurrent_downloads = 3 + +#bind_address = 192.168.1.76 +#port = 30000 diff --git a/data/games/garage/LICENSE.txt b/data/games/garage/LICENSE.txt new file mode 100644 index 0000000..a76b0a1 --- /dev/null +++ b/data/games/garage/LICENSE.txt @@ -0,0 +1,4 @@ +License information for Development Test +---------------------------------------- + +The same license as for Luanti applies. diff --git a/data/games/garage/README.md b/data/games/garage/README.md new file mode 100644 index 0000000..4b5fb73 --- /dev/null +++ b/data/games/garage/README.md @@ -0,0 +1,50 @@ +# Development Test (devtest) + +This is a basic testing environment that contains a bunch of things to test the engine, but it could also be used as a minimal testbed for testing out mods. + +## Features + +* Basic nodes for mapgen +* Basic, minimal map generator +* Lots of example nodes for testing drawtypes, param2, light level, and many other node properties +* Example entities +* Other example items +* Formspec test (via `/test_formspec` command) +* Automated unit tests (disabled by default) +* Tools for manipulating nodes and entities, like the "Param2 Tool" + +## Getting started + +Basically, just create a world and start. A few important things to note: + +* Items are gotten from the “Chest of Everything” (`chest_of_everything:chest`) +* When you lost your initial items, type in `/stuff` command to get them back +* By default, Creative Mode activates infinite node placement. This behavior can be changed with the `devtest_infplace` setting +* Use the `/infplace` command to toggle infinite node placement in-game +* Use the Param2 Tool to change the param2 of nodes; it's useful to experiment with the various drawtype test nodes +* Check out the game settings and server commands for additional tests and features + +Confused by a certain node or item? Check out for inline code comments. The usages of most tools are explained in their tooltips. + +### Example tests + +* You can use this to test what happens if a player is simultaneously in 2 nodes with `damage_per_second` but with a different value. +* Or use the Falling Node Tool on various test nodes to see how they behave when falling. +* You could also use this as a testbed for dependency-free mods, e.g. to test out how your formspecs behave without theming. + +## Random notes + +* Textures of drawtype test nodes have a red dot at the top left corner. This is to see whether the textures are oriented properly + +## Design philosophy + +This should loosely follow the following principles: + +* Engine testing: The main focus of this is to aid testing of *engine* features, such as mapgen or node drawtypes +* Mod testing: The secondary focus is to help modders as well, either as a minimal testbed for mods or even as a code example +* Minimal interference: Under default settings, it shall not interfere with APIs except on explicit user wish. Non-trivial tests and features need to be enabled by a setting first +* Convenience: Have various tools to make usage easier and more convenient +* Reproducing engine bugs: When an engine bug was found, consider creating a test case +* Clarity: Textures and names need to be designed to keep different things clearly visually apart at a glance +* Low loading time: It must load blazing-fast so stuff can be tested quickly + diff --git a/data/games/garage/game.conf b/data/games/garage/game.conf new file mode 100644 index 0000000..e79cc94 --- /dev/null +++ b/data/games/garage/game.conf @@ -0,0 +1,4 @@ +title = Development Test +description = Testing environment to help with testing the engine features of Luanti. It can also be helpful in mod development. +first_mod = first_mod +last_mod = last_mod diff --git a/data/games/garage/menu/background.png b/data/games/garage/menu/background.png new file mode 100644 index 0000000..e69c4d0 Binary files /dev/null and b/data/games/garage/menu/background.png differ diff --git a/data/games/garage/menu/header.png b/data/games/garage/menu/header.png new file mode 100644 index 0000000..b47e004 Binary files /dev/null and b/data/games/garage/menu/header.png differ diff --git a/data/games/garage/menu/icon.png b/data/games/garage/menu/icon.png new file mode 100644 index 0000000..f854b9c Binary files /dev/null and b/data/games/garage/menu/icon.png differ diff --git a/data/games/garage/mods/basenodes/init.lua b/data/games/garage/mods/basenodes/init.lua new file mode 100644 index 0000000..a6cc680 --- /dev/null +++ b/data/games/garage/mods/basenodes/init.lua @@ -0,0 +1,361 @@ +local WATER_ALPHA = "^[opacity:" .. 160 +local WATER_VISC = 1 +local LAVA_VISC = 7 + +-- +-- Node definitions +-- + +-- Register nodes + +core.register_node("basenodes:stone", { + description = "Stone", + tiles = {"default_stone.png"}, + groups = {cracky=3}, +}) + +core.register_node("basenodes:desert_stone", { + description = "Desert Stone", + tiles = {"default_desert_stone.png"}, + groups = {cracky=3}, +}) + +core.register_node("basenodes:dirt_with_grass", { + description = "Dirt with Grass", + tiles ={"default_grass.png", + -- a little dot on the bottom to distinguish it from dirt + "default_dirt.png^basenodes_dirt_with_grass_bottom.png", + {name = "default_dirt.png^default_grass_side.png", + tileable_vertical = false}}, + groups = {crumbly=3, soil=1}, +}) + +core.register_node("basenodes:dirt_with_snow", { + description = "Dirt with Snow", + tiles ={"basenodes_dirt_with_snow.png", + -- a little dot on the bottom to distinguish it from dirt + "default_dirt.png^basenodes_dirt_with_snow_bottom.png", + {name = "default_dirt.png^default_snow_side.png", + tileable_vertical = false}}, + groups = {crumbly=3, soil=1}, +}) + +core.register_node("basenodes:dirt", { + description = "Dirt", + tiles ={"default_dirt.png"}, + groups = {crumbly=3, soil=1}, +}) + +core.register_node("basenodes:sand", { + description = "Sand", + tiles ={"default_sand.png"}, + groups = {crumbly=3}, +}) + +core.register_node("basenodes:desert_sand", { + description = "Desert Sand", + tiles ={"default_desert_sand.png"}, + groups = {crumbly=3}, +}) + +core.register_node("basenodes:gravel", { + description = "Gravel", + tiles ={"default_gravel.png"}, + groups = {crumbly=2}, +}) + +core.register_node("basenodes:junglegrass", { + description = "Jungle Grass", + drawtype = "plantlike", + tiles ={"default_junglegrass.png"}, + inventory_image = "default_junglegrass.png", + wield_image = "default_junglegrass.png", + paramtype = "light", + walkable = false, + groups = {snappy=3}, +}) + +core.register_node("basenodes:tree", { + description = "Normal Tree Trunk", + tiles = {"default_tree_top.png", "default_tree_top.png", "default_tree.png"}, + is_ground_content = false, + groups = {choppy=2,oddly_breakable_by_hand=1}, +}) + +core.register_node("basenodes:leaves", { + description = "Normal Leaves", + drawtype = "allfaces_optional", + tiles = {"default_leaves.png"}, + paramtype = "light", + is_ground_content = false, + groups = {snappy=3}, +}) + +core.register_node("basenodes:jungletree", { + description = "Jungle Tree Trunk", + tiles = {"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"}, + is_ground_content = false, + groups = {choppy=2,oddly_breakable_by_hand=1}, +}) + +core.register_node("basenodes:jungleleaves", { + description = "Jungle Leaves", + drawtype = "allfaces_optional", + tiles = {"default_jungleleaves.png"}, + paramtype = "light", + is_ground_content = false, + groups = {snappy=3}, +}) + +core.register_node("basenodes:pine_tree", { + description = "Pine Tree Trunk", + tiles = {"default_pine_tree_top.png", "default_pine_tree_top.png", "default_pine_tree.png"}, + is_ground_content = false, + groups = {choppy=2,oddly_breakable_by_hand=1}, +}) + +core.register_node("basenodes:pine_needles", { + description = "Pine Needles", + drawtype = "allfaces_optional", + tiles = {"default_pine_needles.png"}, + paramtype = "light", + is_ground_content = false, + groups = {snappy=3}, +}) + +core.register_node("basenodes:water_source", { + description = "Water Source".."\n".. + "Swimmable, spreading, renewable liquid".."\n".. + "Drowning damage: 1", + drawtype = "liquid", + waving = 3, + tiles = {"default_water.png"..WATER_ALPHA}, + special_tiles = { + {name = "default_water.png"..WATER_ALPHA, backface_culling = false}, + {name = "default_water.png"..WATER_ALPHA, backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + liquidtype = "source", + liquid_alternative_flowing = "basenodes:water_flowing", + liquid_alternative_source = "basenodes:water_source", + liquid_viscosity = WATER_VISC, + post_effect_color = {a = 64, r = 100, g = 100, b = 200}, + post_effect_color_shaded = true, + groups = {water = 3, liquid = 3}, +}) + +core.register_node("basenodes:water_flowing", { + description = "Flowing Water".."\n".. + "Swimmable, spreading, renewable liquid".."\n".. + "Drowning damage: 1", + drawtype = "flowingliquid", + waving = 3, + tiles = {"default_water_flowing.png"}, + special_tiles = { + {name = "default_water_flowing.png"..WATER_ALPHA, + backface_culling = false}, + {name = "default_water_flowing.png"..WATER_ALPHA, + backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + liquidtype = "flowing", + liquid_alternative_flowing = "basenodes:water_flowing", + liquid_alternative_source = "basenodes:water_source", + liquid_viscosity = WATER_VISC, + post_effect_color = {a = 64, r = 100, g = 100, b = 200}, + post_effect_color_shaded = true, + groups = {water = 3, liquid = 3}, +}) + +core.register_node("basenodes:river_water_source", { + description = "River Water Source".."\n".. + "Swimmable, spreading, non-renewable liquid".."\n".. + "Drowning damage: 1", + drawtype = "liquid", + waving = 3, + tiles = { "default_river_water.png"..WATER_ALPHA }, + special_tiles = { + {name = "default_river_water.png"..WATER_ALPHA, backface_culling = false}, + {name = "default_river_water.png"..WATER_ALPHA, backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + liquidtype = "source", + liquid_alternative_flowing = "basenodes:river_water_flowing", + liquid_alternative_source = "basenodes:river_water_source", + liquid_viscosity = 1, + liquid_renewable = false, + liquid_range = 2, + post_effect_color = {a = 103, r = 30, g = 76, b = 90}, + post_effect_color_shaded = true, + groups = {water = 3, liquid = 3, }, +}) + +core.register_node("basenodes:river_water_flowing", { + description = "Flowing River Water".."\n".. + "Swimmable, spreading, non-renewable liquid".."\n".. + "Drowning damage: 1", + drawtype = "flowingliquid", + waving = 3, + tiles = {"default_river_water_flowing.png"..WATER_ALPHA}, + special_tiles = { + {name = "default_river_water_flowing.png"..WATER_ALPHA, + backface_culling = false}, + {name = "default_river_water_flowing.png"..WATER_ALPHA, + backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + liquidtype = "flowing", + liquid_alternative_flowing = "basenodes:river_water_flowing", + liquid_alternative_source = "basenodes:river_water_source", + liquid_viscosity = 1, + liquid_renewable = false, + liquid_range = 2, + post_effect_color = {a = 103, r = 30, g = 76, b = 90}, + post_effect_color_shaded = true, + groups = {water = 3, liquid = 3, }, +}) + +core.register_node("basenodes:lava_flowing", { + description = "Flowing Lava".."\n".. + "Swimmable, spreading, renewable liquid".."\n".. + "4 damage per second".."\n".. + "Drowning damage: 1", + drawtype = "flowingliquid", + tiles = {"default_lava_flowing.png"}, + special_tiles = { + {name="default_lava_flowing.png", backface_culling = false}, + {name="default_lava_flowing.png", backface_culling = false}, + }, + paramtype = "light", + light_source = core.LIGHT_MAX, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + damage_per_second = 4, + liquidtype = "flowing", + liquid_alternative_flowing = "basenodes:lava_flowing", + liquid_alternative_source = "basenodes:lava_source", + liquid_viscosity = LAVA_VISC, + post_effect_color = {a=192, r=255, g=64, b=0}, + groups = {lava=3, liquid=1}, +}) + +core.register_node("basenodes:lava_source", { + description = "Lava Source".."\n".. + "Swimmable, spreading, renewable liquid".."\n".. + "4 damage per second".."\n".. + "Drowning damage: 1", + drawtype = "liquid", + tiles = { "default_lava.png" }, + special_tiles = { + {name = "default_lava.png", backface_culling = false}, + {name = "default_lava.png", backface_culling = true}, + }, + paramtype = "light", + light_source = core.LIGHT_MAX, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drowning = 1, + damage_per_second = 4, + liquidtype = "source", + liquid_alternative_flowing = "basenodes:lava_flowing", + liquid_alternative_source = "basenodes:lava_source", + liquid_viscosity = LAVA_VISC, + post_effect_color = {a=192, r=255, g=64, b=0}, + groups = {lava=3, liquid=1}, +}) + +core.register_node("basenodes:cobble", { + description = "Cobblestone", + tiles ={"default_cobble.png"}, + is_ground_content = false, + groups = {cracky=3}, +}) + +core.register_node("basenodes:mossycobble", { + description = "Mossy Cobblestone", + tiles ={"default_mossycobble.png"}, + is_ground_content = false, + groups = {cracky=3}, +}) + +core.register_node("basenodes:apple", { + description = "Apple".."\n".. + "Punch: Eat (+2)", + drawtype = "plantlike", + tiles ={"default_apple.png"}, + inventory_image = "default_apple.png", + paramtype = "light", + is_ground_content = false, + sunlight_propagates = true, + walkable = false, + groups = {dig_immediate=3}, + + -- Make eatable because why not? + on_use = core.item_eat(2), +}) + +core.register_node("basenodes:ice", { + description = "Ice", + tiles ={"default_ice.png"}, + groups = {cracky=3}, +}) + +-- The snow nodes intentionally have different tints to make them more +-- distinguishable +core.register_node("basenodes:snow", { + description = "Snow Sheet", + tiles = {"basenodes_snow_sheet.png"}, + groups = {crumbly=3}, + walkable = false, + paramtype = "light", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -0.25, 0.5}, + }, +}) + +core.register_node("basenodes:snowblock", { + description = "Snow Block", + tiles ={"default_snow.png"}, + groups = {crumbly=3}, +}) + + diff --git a/data/games/garage/mods/basenodes/mod.conf b/data/games/garage/mods/basenodes/mod.conf new file mode 100644 index 0000000..25024dc --- /dev/null +++ b/data/games/garage/mods/basenodes/mod.conf @@ -0,0 +1,2 @@ +name = basenodes +description = Contains basic nodes for mapgen diff --git a/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_grass_bottom.png b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_grass_bottom.png new file mode 100644 index 0000000..5e8fc41 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_grass_bottom.png differ diff --git a/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow.png b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow.png new file mode 100644 index 0000000..7ea2d8d Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow.png differ diff --git a/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow_bottom.png b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow_bottom.png new file mode 100644 index 0000000..447c94e Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/basenodes_dirt_with_snow_bottom.png differ diff --git a/data/games/garage/mods/basenodes/textures/basenodes_snow_sheet.png b/data/games/garage/mods/basenodes/textures/basenodes_snow_sheet.png new file mode 100644 index 0000000..4553320 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/basenodes_snow_sheet.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_apple.png b/data/games/garage/mods/basenodes/textures/default_apple.png new file mode 100644 index 0000000..9c115da Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_apple.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_cobble.png b/data/games/garage/mods/basenodes/textures/default_cobble.png new file mode 100644 index 0000000..5b859e9 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_cobble.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_desert_sand.png b/data/games/garage/mods/basenodes/textures/default_desert_sand.png new file mode 100644 index 0000000..19ec87d Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_desert_sand.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_desert_stone.png b/data/games/garage/mods/basenodes/textures/default_desert_stone.png new file mode 100644 index 0000000..5126fb6 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_desert_stone.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_dirt.png b/data/games/garage/mods/basenodes/textures/default_dirt.png new file mode 100644 index 0000000..6ca9588 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_dirt.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_grass.png b/data/games/garage/mods/basenodes/textures/default_grass.png new file mode 100644 index 0000000..3d63971 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_grass.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_gravel.png b/data/games/garage/mods/basenodes/textures/default_gravel.png new file mode 100644 index 0000000..7e5ff61 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_gravel.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_ice.png b/data/games/garage/mods/basenodes/textures/default_ice.png new file mode 100644 index 0000000..c4bddd2 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_ice.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_junglegrass.png b/data/games/garage/mods/basenodes/textures/default_junglegrass.png new file mode 100644 index 0000000..d64e33a Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_junglegrass.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_jungleleaves.png b/data/games/garage/mods/basenodes/textures/default_jungleleaves.png new file mode 100644 index 0000000..1fa67e8 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_jungleleaves.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_jungletree.png b/data/games/garage/mods/basenodes/textures/default_jungletree.png new file mode 100644 index 0000000..053850f Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_jungletree.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_jungletree_top.png b/data/games/garage/mods/basenodes/textures/default_jungletree_top.png new file mode 100644 index 0000000..e80de8a Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_jungletree_top.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_lava.png b/data/games/garage/mods/basenodes/textures/default_lava.png new file mode 100644 index 0000000..a4cf649 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_lava.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_lava_flowing.png b/data/games/garage/mods/basenodes/textures/default_lava_flowing.png new file mode 100644 index 0000000..07066a6 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_lava_flowing.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_leaves.png b/data/games/garage/mods/basenodes/textures/default_leaves.png new file mode 100644 index 0000000..c0475d4 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_leaves.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_mossycobble.png b/data/games/garage/mods/basenodes/textures/default_mossycobble.png new file mode 100644 index 0000000..69585e3 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_mossycobble.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_pine_needles.png b/data/games/garage/mods/basenodes/textures/default_pine_needles.png new file mode 100644 index 0000000..137caa2 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_pine_needles.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_pine_tree.png b/data/games/garage/mods/basenodes/textures/default_pine_tree.png new file mode 100644 index 0000000..5743183 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_pine_tree.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_pine_tree_top.png b/data/games/garage/mods/basenodes/textures/default_pine_tree_top.png new file mode 100644 index 0000000..cc18f34 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_pine_tree_top.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_river_water.png b/data/games/garage/mods/basenodes/textures/default_river_water.png new file mode 100644 index 0000000..e1074d2 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_river_water.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_river_water_flowing.png b/data/games/garage/mods/basenodes/textures/default_river_water_flowing.png new file mode 100644 index 0000000..4a756b2 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_river_water_flowing.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_sand.png b/data/games/garage/mods/basenodes/textures/default_sand.png new file mode 100644 index 0000000..0ed0e4c Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_sand.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_snow.png b/data/games/garage/mods/basenodes/textures/default_snow.png new file mode 100644 index 0000000..c42e0ee Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_snow.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_snow_side.png b/data/games/garage/mods/basenodes/textures/default_snow_side.png new file mode 100644 index 0000000..f34d109 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_snow_side.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_stone.png b/data/games/garage/mods/basenodes/textures/default_stone.png new file mode 100644 index 0000000..763b439 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_stone.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_tree.png b/data/games/garage/mods/basenodes/textures/default_tree.png new file mode 100644 index 0000000..189ec15 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_tree.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_tree_top.png b/data/games/garage/mods/basenodes/textures/default_tree_top.png new file mode 100644 index 0000000..d1a4fa7 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_tree_top.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_water.png b/data/games/garage/mods/basenodes/textures/default_water.png new file mode 100644 index 0000000..3e385ae Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_water.png differ diff --git a/data/games/garage/mods/basenodes/textures/default_water_flowing.png b/data/games/garage/mods/basenodes/textures/default_water_flowing.png new file mode 100644 index 0000000..7cdafd5 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/default_water_flowing.png differ diff --git a/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass.png b/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass.png new file mode 100644 index 0000000..47e50e8 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass.png differ diff --git a/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass_side.png b/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass_side.png new file mode 100644 index 0000000..04770b6 Binary files /dev/null and b/data/games/garage/mods/basenodes/textures/dirt_with_grass/default_grass_side.png differ diff --git a/data/games/garage/mods/basenodes/textures/info.txt b/data/games/garage/mods/basenodes/textures/info.txt new file mode 100644 index 0000000..2d4ef7e --- /dev/null +++ b/data/games/garage/mods/basenodes/textures/info.txt @@ -0,0 +1,7 @@ + +The dirt_with_grass folder is for testing loading textures from subfolders. +If it works correctly, the default_grass_side.png file in the folder is used but +default_grass.png is not overwritten by the file in the folder. + +default_dirt.png should be overwritten by the default_dirt.png in the unittests +mod which depends on basenodes. diff --git a/data/games/garage/mods/basetools/init.lua b/data/games/garage/mods/basetools/init.lua new file mode 100644 index 0000000..92b5888 --- /dev/null +++ b/data/games/garage/mods/basetools/init.lua @@ -0,0 +1,582 @@ +-- +-- Tool definitions +-- + +--[[ TOOLS SUMMARY: + +Tool types: + +* Hand: basic tool/weapon (special capabilities in creative mode) +* Pickaxe: dig cracky +* Axe: dig choppy +* Shovel: dig crumbly +* Shears: dig snappy +* Sword: deal damage +* Dagger: deal damage, but faster + +Tool materials: + +* Wood: dig nodes of rating 3 +* Stone: dig nodes of rating 3 or 2 +* Steel: dig nodes of rating 3, 2 or 1 +* Mese: dig "everything" instantly +* n-Uses: can be used n times before breaking +]] + +-- The hand +if core.settings:get_bool("creative_mode") then + local digtime = 42 + local caps = {times = {digtime, digtime, digtime}, uses = 0, maxlevel = 256} + + core.register_item(":", { + type = "none", + wield_image = "wieldhand.png", + wield_scale = {x = 1, y = 1, z = 2.5}, + range = 10, + tool_capabilities = { + full_punch_interval = 0.5, + max_drop_level = 3, + groupcaps = { + crumbly = caps, + cracky = caps, + snappy = caps, + choppy = caps, + oddly_breakable_by_hand = caps, + -- dig_immediate group doesn't use value 1. Value 3 is instant dig + dig_immediate = + {times = {[2] = digtime, [3] = 0}, uses = 0, maxlevel = 256}, + }, + damage_groups = {fleshy = 10}, + } + }) +else + core.register_item(":", { + type = "none", + wield_image = "wieldhand.png", + wield_scale = {x = 1, y = 1, z = 2.5}, + tool_capabilities = { + full_punch_interval = 0.9, + max_drop_level = 0, + groupcaps = { + crumbly = {times = {[2] = 3.00, [3] = 0.70}, uses = 0, maxlevel = 1}, + snappy = {times = {[3] = 0.40}, uses = 0, maxlevel = 1}, + oddly_breakable_by_hand = + {times = {[1] = 3.50, [2] = 2.00, [3] = 0.70}, uses = 0} + }, + damage_groups = {fleshy = 1}, + } + }) +end + +-- Mese Pickaxe: special tool that digs "everything" instantly +core.register_tool("basetools:pick_mese", { + description = "Mese Pickaxe".."\n".. + "Digs diggable nodes instantly.", + inventory_image = "basetools_mesepick.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=3, + groupcaps={ + cracky={times={[1]=0.0, [2]=0.0, [3]=0.0}, maxlevel=255}, + crumbly={times={[1]=0.0, [2]=0.0, [3]=0.0}, maxlevel=255}, + snappy={times={[1]=0.0, [2]=0.0, [3]=0.0}, maxlevel=255}, + choppy={times={[1]=0.0, [2]=0.0, [3]=0.0}, maxlevel=255}, + dig_immediate={times={[1]=0.0, [2]=0.0, [3]=0.0}, maxlevel=255}, + }, + damage_groups = {fleshy=100}, + }, +}) + + +-- A variant of the mese pickaxe that is not affected by the 0.15s digging delay +core.register_tool("basetools:pick_mese_no_delay", { + description = "Mese Pickaxe (no delay)".."\n".. + "Digs diggable nodes instantly.".."\n".. + "There is no delay between digging each node,\n".. + 'but the "repeat_dig_time" setting is still respected.', + inventory_image = "basetools_mesepick_no_delay.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=3, + groupcaps={ + cracky={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255}, + crumbly={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255}, + snappy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255}, + choppy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255}, + dig_immediate={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255}, + }, + damage_groups = {fleshy=100}, + }, +}) + + +-- +-- Pickaxes: Dig cracky +-- + +core.register_tool("basetools:pick_wood", { + description = "Wooden Pickaxe".."\n".. + "Digs cracky=3", + inventory_image = "basetools_woodpick.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[3]=2.00}, uses=30, maxlevel=0} + }, + }, +}) +core.register_tool("basetools:pick_stone", { + description = "Stone Pickaxe".."\n".. + "Digs cracky=2..3", + inventory_image = "basetools_stonepick.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[2]=1.20, [3]=0.80}, uses=60, maxlevel=0} + }, + }, +}) +core.register_tool("basetools:pick_steel", { + description = "Steel Pickaxe".."\n".. + "Digs cracky=1..3", + inventory_image = "basetools_steelpick.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=90, maxlevel=0} + }, + }, +}) +core.register_tool("basetools:pick_steel_l1", { + description = "Steel Pickaxe Level 1".."\n".. + "Digs cracky=1..3".."\n".. + "maxlevel=1", + inventory_image = "basetools_steelpick_l1.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=90, maxlevel=1} + }, + }, +}) +core.register_tool("basetools:pick_steel_l2", { + description = "Steel Pickaxe Level 2".."\n".. + "Digs cracky=1..3".."\n".. + "maxlevel=2", + inventory_image = "basetools_steelpick_l2.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=90, maxlevel=2} + }, + }, +}) + +-- +-- Shovels (dig crumbly) +-- + +core.register_tool("basetools:shovel_wood", { + description = "Wooden Shovel".."\n".. + "Digs crumbly=3", + inventory_image = "basetools_woodshovel.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + crumbly={times={[3]=0.50}, uses=30, maxlevel=0} + }, + }, +}) +core.register_tool("basetools:shovel_stone", { + description = "Stone Shovel".."\n".. + "Digs crumbly=2..3", + inventory_image = "basetools_stoneshovel.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + crumbly={times={[2]=0.50, [3]=0.30}, uses=60, maxlevel=0} + }, + }, +}) +core.register_tool("basetools:shovel_steel", { + description = "Steel Shovel".."\n".. + "Digs crumbly=1..3", + inventory_image = "basetools_steelshovel.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + crumbly={times={[1]=1.00, [2]=0.70, [3]=0.60}, uses=90, maxlevel=0} + }, + }, +}) + +-- +-- Axes (dig choppy) +-- + +core.register_tool("basetools:axe_wood", { + description = "Wooden Axe".."\n".. + "Digs choppy=3", + inventory_image = "basetools_woodaxe.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + choppy={times={[3]=0.80}, uses=30, maxlevel=0}, + }, + }, +}) +core.register_tool("basetools:axe_stone", { + description = "Stone Axe".."\n".. + "Digs choppy=2..3", + inventory_image = "basetools_stoneaxe.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + choppy={times={[2]=1.00, [3]=0.60}, uses=60, maxlevel=0}, + }, + }, +}) +core.register_tool("basetools:axe_steel", { + description = "Steel Axe".."\n".. + "Digs choppy=1..3", + inventory_image = "basetools_steelaxe.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + choppy={times={[1]=2.00, [2]=0.80, [3]=0.40}, uses=90, maxlevel=0}, + }, + }, +}) + +-- +-- Shears (dig snappy) +-- + +core.register_tool("basetools:shears_wood", { + description = "Wooden Shears".."\n".. + "Digs snappy=3", + inventory_image = "basetools_woodshears.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + snappy={times={[3]=1.00}, uses=30, maxlevel=0}, + }, + }, +}) +core.register_tool("basetools:shears_stone", { + description = "Stone Shears".."\n".. + "Digs snappy=2..3", + inventory_image = "basetools_stoneshears.png", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + snappy={times={[2]=1.00, [3]=0.50}, uses=60, maxlevel=0}, + }, + }, +}) +core.register_tool("basetools:shears_steel", { + description = "Steel Shears".."\n".. + "Digs snappy=1..3", + inventory_image = "basetools_steelshears.png", + tool_capabilities = { + max_drop_level=1, + groupcaps={ + snappy={times={[1]=1.00, [2]=0.50, [3]=0.25}, uses=90, maxlevel=0}, + }, + }, +}) + +-- +-- Swords (deal damage) +-- + +core.register_tool("basetools:sword_wood", { + description = "Wooden Sword".."\n".. + "Damage: fleshy=2", + inventory_image = "basetools_woodsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + damage_groups = {fleshy=2}, + } +}) +core.register_tool("basetools:sword_stone", { + description = "Stone Sword".."\n".. + "Damage: fleshy=5", + inventory_image = "basetools_stonesword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=0, + damage_groups = {fleshy=5}, + } +}) +core.register_tool("basetools:sword_steel", { + description = "Steel Sword".."\n".. + "Damage: fleshy=10", + inventory_image = "basetools_steelsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=1, + damage_groups = {fleshy=10}, + } +}) +core.register_tool("basetools:sword_titanium", { + description = "Titanium Sword".."\n".. + "Damage: fleshy=100", + inventory_image = "basetools_titaniumsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=1, + damage_groups = {fleshy=100}, + } +}) +core.register_tool("basetools:sword_blood", { + description = "Blood Sword".."\n".. + "Damage: fleshy=1000", + inventory_image = "basetools_bloodsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=1, + damage_groups = {fleshy=1000}, + } +}) + +-- Max. damage sword +core.register_tool("basetools:sword_mese", { + description = "Mese Sword".."\n".. + "Damage: fleshy=32767, fiery=32767, icy=32767".."\n".. + "Full Punch Interval: 0.0s", + inventory_image = "basetools_mesesword.png", + tool_capabilities = { + full_punch_interval = 0.0, + max_drop_level=1, + damage_groups = {fleshy=32767, fiery=32767, icy=32767}, + } +}) + +-- Fire/Ice sword: Deal damage to non-fleshy damage groups +core.register_tool("basetools:sword_fire", { + description = "Fire Sword".."\n".. + "Damage: icy=10", + inventory_image = "basetools_firesword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=0, + damage_groups = {icy=10}, + } +}) +core.register_tool("basetools:sword_ice", { + description = "Ice Sword".."\n".. + "Damage: fiery=10", + inventory_image = "basetools_icesword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=0, + damage_groups = {fiery=10}, + } +}) +core.register_tool("basetools:sword_elemental", { + description = "Elemental Sword".."\n".. + "Damage: fiery=10, icy=10", + inventory_image = "basetools_elementalsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=0, + damage_groups = {fiery=10, icy=10}, + } +}) + +-- Healing weapons: heal HP +core.register_tool("basetools:dagger_heal", { + description = "Healing Dagger".."\n".. + "Heal: fleshy=1".."\n".. + "Full Punch Interval: 0.5s", + inventory_image = "basetools_healdagger.png", + tool_capabilities = { + full_punch_interval = 0.5, + damage_groups = {fleshy=-1}, + } +}) +core.register_tool("basetools:sword_heal", { + description = "Healing Sword".."\n".. + "Heal: fleshy=10", + inventory_image = "basetools_healsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + damage_groups = {fleshy=-10}, + } +}) +core.register_tool("basetools:sword_heal_super", { + description = "Super Healing Sword".."\n".. + "Heal: fleshy=32768, fiery=32768, icy=32768", + inventory_image = "basetools_superhealsword.png", + tool_capabilities = { + full_punch_interval = 1.0, + damage_groups = {fleshy=-32768, fiery=-32768, icy=-32768}, + } +}) + + +-- +-- Dagger: Low damage, fast punch interval +-- +core.register_tool("basetools:dagger_wood", { + description = "Wooden Dagger".."\n".. + "Damage: fleshy=1".."\n".. + "Full Punch Interval: 0.5s", + inventory_image = "basetools_wooddagger.png", + tool_capabilities = { + full_punch_interval = 0.5, + max_drop_level=0, + damage_groups = {fleshy=1}, + } +}) +core.register_tool("basetools:dagger_steel", { + description = "Steel Dagger".."\n".. + "Damage: fleshy=2".."\n".. + "Full Punch Interval: 0.5s", + inventory_image = "basetools_steeldagger.png", + tool_capabilities = { + full_punch_interval = 0.5, + max_drop_level=0, + damage_groups = {fleshy=2}, + } +}) + +-- Test tool uses, punch_attack_uses, and wear bar coloring +local tool_params = { + {uses = 1}, + {uses = 2}, + {uses = 3}, + { + uses = 5, + wear_color = "#5865f2", + wear_description = "Solid color: #5865f2", + }, + { + uses = 10, + wear_color = "slateblue", + wear_description = "Solid color: slateblue", + }, + { + uses = 50, + wear_color = { + color_stops = { + [0] = "red", + [0.5] = "yellow", + [1.0] = "blue" + }, + blend = "linear" + }, + wear_description = "Ranges from blue to yellow to red", + }, + { + uses = 100, + wear_color = { + color_stops = { + [0] = "#ffff00", + [0.2] = "#ff00ff", + [0.3] = "#ffff00", + [0.45] = "#c0ffee", + [0.6] = {r=255, g=255, b=0, a=100}, -- continues until the end + }, + blend = "constant" + }, + wear_description = "Misc. colors, constant interpolation", + }, + {uses = 1e3}, + {uses = 1e4}, + {uses = 65535}, +} + +for i, params in ipairs(tool_params) do + local uses = params.uses + local ustring = uses.."-Use"..(uses == 1 and "" or "s") + local color = string.format("#FF00%02X", math.floor(((i-1)/#tool_params) * 255)) + core.register_tool("basetools:pick_uses_"..string.format("%05d", uses), { + description = ustring.." Pickaxe".."\n".. + "Digs cracky=3".. + (params.wear_description and "\n".."Wear bar: " .. params.wear_description or ""), + inventory_image = "basetools_usespick.png^[colorize:"..color..":127", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=uses, maxlevel=0} + }, + }, + wear_color = params.wear_color + }) + + core.register_tool("basetools:sword_uses_"..string.format("%05d", uses), { + description = ustring.." Sword".."\n".. + "Damage: fleshy=1", + inventory_image = "basetools_usessword.png^[colorize:"..color..":127", + tool_capabilities = { + damage_groups = {fleshy=1}, + punch_attack_uses = uses, + }, + }) +end + +core.register_chatcommand("wear_color", { + params = "[idx]", + description = "Set wear bar color override", + func = function(player_name, param) + local player = core.get_player_by_name(player_name) + if not player then return end + + local wear_color = nil + local wear_desc = "Reset override" + + if param ~= "" then + local params = tool_params[tonumber(param)] + if not params then + return false, "idx out of bounds" + end + wear_color = params.wear_color + wear_desc = "Set override: "..(params.wear_description or "Default behavior") + end + local tool = player:get_wielded_item() + if tool:get_count() == 0 then + return false, "Tool not found" + end + tool:get_meta():set_wear_bar_params(wear_color) + player:set_wielded_item(tool) + return true, wear_desc + end +}) + +-- Punch handler to set random color & wear +local wear_on_use = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + local color = math.random(0, 0xFFFFFF) + local colorstr = string.format("#%06x", color) + meta:set_wear_bar_params(colorstr) + core.log("action", "[basetool] Wear bar color of "..itemstack:get_name().." changed to "..colorstr) + itemstack:set_wear(math.random(0, 65535)) + return itemstack +end + +-- Place handler to clear item metadata color +local wear_on_place = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + meta:set_wear_bar_params(nil) + return itemstack +end + +core.register_tool("basetools:random_wear_bar", { + description = "Wear Bar Color Test\n" .. + "Punch: Set random color & wear\n" .. + "Place: Clear color", + -- Base texture: A grayscale square (can be colorized) + inventory_image = "basetools_usespick.png^[colorize:#FFFFFF:127", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=1000, maxlevel=0} + }, + }, + + on_use = wear_on_use, + on_place = wear_on_place, + on_secondary_use = wear_on_place, +}) diff --git a/data/games/garage/mods/basetools/mod.conf b/data/games/garage/mods/basetools/mod.conf new file mode 100644 index 0000000..f0d9f65 --- /dev/null +++ b/data/games/garage/mods/basetools/mod.conf @@ -0,0 +1,2 @@ +name = basetools +description = Contains basic digging tools diff --git a/data/games/garage/mods/basetools/textures/basetools_bloodsword.png b/data/games/garage/mods/basetools/textures/basetools_bloodsword.png new file mode 100644 index 0000000..a521ba4 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_bloodsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_elementalsword.png b/data/games/garage/mods/basetools/textures/basetools_elementalsword.png new file mode 100644 index 0000000..d007217 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_elementalsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_firesword.png b/data/games/garage/mods/basetools/textures/basetools_firesword.png new file mode 100644 index 0000000..eca999b Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_firesword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_healdagger.png b/data/games/garage/mods/basetools/textures/basetools_healdagger.png new file mode 100644 index 0000000..3e6eb9c Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_healdagger.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_healsword.png b/data/games/garage/mods/basetools/textures/basetools_healsword.png new file mode 100644 index 0000000..f93fddf Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_healsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_icesword.png b/data/games/garage/mods/basetools/textures/basetools_icesword.png new file mode 100644 index 0000000..55a8d60 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_icesword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_mesepick.png b/data/games/garage/mods/basetools/textures/basetools_mesepick.png new file mode 100644 index 0000000..2993b47 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_mesepick.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_mesepick_no_delay.png b/data/games/garage/mods/basetools/textures/basetools_mesepick_no_delay.png new file mode 100644 index 0000000..5a7db7e Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_mesepick_no_delay.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_mesesword.png b/data/games/garage/mods/basetools/textures/basetools_mesesword.png new file mode 100644 index 0000000..bc82769 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_mesesword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelaxe.png b/data/games/garage/mods/basetools/textures/basetools_steelaxe.png new file mode 100644 index 0000000..aac594d Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelaxe.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steeldagger.png b/data/games/garage/mods/basetools/textures/basetools_steeldagger.png new file mode 100644 index 0000000..4c91730 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steeldagger.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelpick.png b/data/games/garage/mods/basetools/textures/basetools_steelpick.png new file mode 100644 index 0000000..bc02aac Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelpick.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelpick_l1.png b/data/games/garage/mods/basetools/textures/basetools_steelpick_l1.png new file mode 100644 index 0000000..dc03f3f Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelpick_l1.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelpick_l2.png b/data/games/garage/mods/basetools/textures/basetools_steelpick_l2.png new file mode 100644 index 0000000..011df45 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelpick_l2.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelshears.png b/data/games/garage/mods/basetools/textures/basetools_steelshears.png new file mode 100644 index 0000000..04c86c3 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelshears.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelshovel.png b/data/games/garage/mods/basetools/textures/basetools_steelshovel.png new file mode 100644 index 0000000..8cab607 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelshovel.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_steelsword.png b/data/games/garage/mods/basetools/textures/basetools_steelsword.png new file mode 100644 index 0000000..9909365 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_steelsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_stoneaxe.png b/data/games/garage/mods/basetools/textures/basetools_stoneaxe.png new file mode 100644 index 0000000..a374c54 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_stoneaxe.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_stonepick.png b/data/games/garage/mods/basetools/textures/basetools_stonepick.png new file mode 100644 index 0000000..d9156ee Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_stonepick.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_stoneshears.png b/data/games/garage/mods/basetools/textures/basetools_stoneshears.png new file mode 100644 index 0000000..0b4bd3b Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_stoneshears.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_stoneshovel.png b/data/games/garage/mods/basetools/textures/basetools_stoneshovel.png new file mode 100644 index 0000000..3c1bb48 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_stoneshovel.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_stonesword.png b/data/games/garage/mods/basetools/textures/basetools_stonesword.png new file mode 100644 index 0000000..6f3e94c Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_stonesword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_superhealsword.png b/data/games/garage/mods/basetools/textures/basetools_superhealsword.png new file mode 100644 index 0000000..4175a09 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_superhealsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_titaniumsword.png b/data/games/garage/mods/basetools/textures/basetools_titaniumsword.png new file mode 100644 index 0000000..55e22c7 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_titaniumsword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_usespick.png b/data/games/garage/mods/basetools/textures/basetools_usespick.png new file mode 100644 index 0000000..27850f9 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_usespick.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_usessword.png b/data/games/garage/mods/basetools/textures/basetools_usessword.png new file mode 100644 index 0000000..0eaf4cf Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_usessword.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_woodaxe.png b/data/games/garage/mods/basetools/textures/basetools_woodaxe.png new file mode 100644 index 0000000..4015e91 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_woodaxe.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_wooddagger.png b/data/games/garage/mods/basetools/textures/basetools_wooddagger.png new file mode 100644 index 0000000..6e5ab0f Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_wooddagger.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_woodpick.png b/data/games/garage/mods/basetools/textures/basetools_woodpick.png new file mode 100644 index 0000000..15c61f4 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_woodpick.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_woodshears.png b/data/games/garage/mods/basetools/textures/basetools_woodshears.png new file mode 100644 index 0000000..4ff92fd Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_woodshears.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_woodshovel.png b/data/games/garage/mods/basetools/textures/basetools_woodshovel.png new file mode 100644 index 0000000..6cc52f8 Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_woodshovel.png differ diff --git a/data/games/garage/mods/basetools/textures/basetools_woodsword.png b/data/games/garage/mods/basetools/textures/basetools_woodsword.png new file mode 100644 index 0000000..364016e Binary files /dev/null and b/data/games/garage/mods/basetools/textures/basetools_woodsword.png differ diff --git a/data/games/garage/mods/benchmarks/init.lua b/data/games/garage/mods/benchmarks/init.lua new file mode 100644 index 0000000..8f6bb1e --- /dev/null +++ b/data/games/garage/mods/benchmarks/init.lua @@ -0,0 +1,189 @@ +-- Safeguard against too much optimization. This way the results cannot be optimized +-- away, but they can be garbage collected (due to __mode = "k"). +_G._bench_content_ids_data = setmetatable({}, {__mode = "k"}) + +local function bench_name2content() + local t = {} + _G._bench_content_ids_data[t] = true + + local get_content_id = core.get_content_id + + local start = core.get_us_time() + + for i = 1, 200 do + for name in pairs(core.registered_nodes) do + t[#t + 1] = get_content_id(name) + end + end + + local finish = core.get_us_time() + + return (finish - start) / 1000 +end + +local function bench_content2name() + local t = {} + _G._bench_content_ids_data[t] = true + + -- Try to estimate the highest content ID that's used + -- (not accurate but good enough for this test) + local n = 0 + for _ in pairs(core.registered_nodes) do + n = n + 1 + end + + local get_name_from_content_id = core.get_name_from_content_id + + local start = core.get_us_time() + + for i = 1, 200 do + for j = 0, n do + t[#t + 1] = get_name_from_content_id(j) + end + end + + local finish = core.get_us_time() + + return (finish - start) / 1000 +end + +core.register_chatcommand("bench_name2content", { + params = "", + description = "Benchmark: Conversion from node names to content IDs", + func = function(name, param) + core.chat_send_player(name, "Benchmarking core.get_content_id. Warming up ...") + bench_name2content() + core.chat_send_player(name, "Warming up finished, now benchmarking ...") + local time = bench_name2content() + return true, ("Time: %.2f ms"):format(time) + end, +}) + +core.register_chatcommand("bench_content2name", { + params = "", + description = "Benchmark: Conversion from content IDs to node names", + func = function(name, param) + core.chat_send_player(name, "Benchmarking core.get_name_from_content_id. Warming up ...") + bench_content2name() + core.chat_send_player(name, "Warming up finished, now benchmarking ...") + local time = bench_content2name() + return true, ("Time: %.2f ms"):format(time) + end, +}) + +local function get_positions_cube(ppos) + local pos_list = {} + + local i = 1 + for x=2,100 do + for y=2,100 do + for z=2,100 do + pos_list[i] = ppos:offset(x, y, z) + i = i + 1 + end + end + end + + return pos_list +end + +core.register_chatcommand("bench_bulk_set_node", { + params = "", + description = "Benchmark: Bulk-set 99×99×99 stone nodes", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos_list = get_positions_cube(player:get_pos()) + + core.chat_send_player(name, "Benchmarking core.bulk_set_node. Warming up ...") + + -- warm up with stone to prevent having different callbacks + -- due to different node topology + core.bulk_set_node(pos_list, {name = "mapgen_stone"}) + + core.chat_send_player(name, "Warming up finished, now benchmarking ...") + + local start_time = core.get_us_time() + for i=1,#pos_list do + core.set_node(pos_list[i], {name = "mapgen_stone"}) + end + local middle_time = core.get_us_time() + core.bulk_set_node(pos_list, {name = "mapgen_stone"}) + local end_time = core.get_us_time() + local msg = string.format("Benchmark results: core.set_node loop: %.2f ms; core.bulk_set_node: %.2f ms", + ((middle_time - start_time)) / 1000, + ((end_time - middle_time)) / 1000 + ) + return true, msg + end, +}) + +core.register_chatcommand("bench_bulk_get_node", { + params = "", + description = "Benchmark: Bulk-get 99×99×99 nodes", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos_list = get_positions_cube(player:get_pos()) + local function bench() + local start_time = core.get_us_time() + for i=1,#pos_list do + local n = core.get_node(pos_list[i]) + -- Make sure the name lookup is never optimized away. + -- Table allocation might still be omitted. But only accessing + -- the name of a node is a common pattern anyways. + if n.name == "benchmarks:nonexistent_node" then + error("should never happen") + end + end + return core.get_us_time() - start_time + end + + core.chat_send_player(name, "Benchmarking core.get_node. Warming up ...") + bench() + + core.chat_send_player(name, "Warming up finished, now benchmarking ...") + local result_us = bench() + + local msg = string.format("Benchmark results: core.get_node loop 1: %.2f ms", + result_us / 1000) + return true, msg + end, +}) + +core.register_chatcommand("bench_bulk_swap_node", { + params = "", + description = "Benchmark: Bulk-swap 99×99×99 stone nodes", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos_list = get_positions_cube(player:get_pos()) + + core.chat_send_player(name, "Benchmarking core.bulk_swap_node. Warming up ...") + + -- warm up because first execution otherwise becomes + -- significantly slower + core.bulk_swap_node(pos_list, {name = "mapgen_stone"}) + + core.chat_send_player(name, "Warming up finished, now benchmarking ...") + + local start_time = core.get_us_time() + for i=1,#pos_list do + core.swap_node(pos_list[i], {name = "mapgen_stone"}) + end + local middle_time = core.get_us_time() + core.bulk_swap_node(pos_list, {name = "mapgen_stone"}) + local end_time = core.get_us_time() + local msg = string.format("Benchmark results: core.swap_node loop: %.2f ms; core.bulk_swap_node: %.2f ms", + ((middle_time - start_time)) / 1000, + ((end_time - middle_time)) / 1000 + ) + return true, msg + end, +}) diff --git a/data/games/garage/mods/benchmarks/mod.conf b/data/games/garage/mods/benchmarks/mod.conf new file mode 100644 index 0000000..fd8d039 --- /dev/null +++ b/data/games/garage/mods/benchmarks/mod.conf @@ -0,0 +1,2 @@ +name = benchmarks +description = Adds some benchmark chat commands diff --git a/data/games/garage/mods/broken/init.lua b/data/games/garage/mods/broken/init.lua new file mode 100644 index 0000000..dcbc47d --- /dev/null +++ b/data/games/garage/mods/broken/init.lua @@ -0,0 +1,11 @@ +-- Register stuff with empty definitions to test if Luanti fallback options +-- for these things work properly. + +-- The itemstrings are deliberately kept descriptive to keep them easy to +-- recognize. + +core.register_node("broken:node_with_empty_definition", {}) +core.register_tool("broken:tool_with_empty_definition", {}) +core.register_craftitem("broken:craftitem_with_empty_definition", {}) + +core.register_entity("broken:entity_with_empty_definition", {}) diff --git a/data/games/garage/mods/broken/mod.conf b/data/games/garage/mods/broken/mod.conf new file mode 100644 index 0000000..a24378a --- /dev/null +++ b/data/games/garage/mods/broken/mod.conf @@ -0,0 +1,2 @@ +name = broken +description = Register items and an entity with empty definitions to test fallback diff --git a/data/games/garage/mods/bucket/init.lua b/data/games/garage/mods/bucket/init.lua new file mode 100644 index 0000000..bce5843 --- /dev/null +++ b/data/games/garage/mods/bucket/init.lua @@ -0,0 +1,27 @@ +-- Bucket: Punch liquid source or flowing liquid to collect it + +core.register_tool("bucket:bucket", { + description = "Bucket".."\n".. + "Picks up liquid nodes", + inventory_image = "bucket.png", + stack_max = 1, + liquids_pointable = true, + groups = { disable_repair = 1 }, + on_use = function(itemstack, user, pointed_thing) + -- Must be pointing to node + if pointed_thing.type ~= "node" then + return + end + -- Check if pointing to a liquid + local n = core.get_node(pointed_thing.under) + local def = core.registered_nodes[n.name] + if def ~= nil and (def.liquidtype == "source" or def.liquidtype == "flowing") then + core.add_node(pointed_thing.under, {name="air"}) + local inv = user:get_inventory() + if inv then + inv:add_item("main", ItemStack(n.name)) + end + end + end, +}) + diff --git a/data/games/garage/mods/bucket/mod.conf b/data/games/garage/mods/bucket/mod.conf new file mode 100644 index 0000000..d14deb4 --- /dev/null +++ b/data/games/garage/mods/bucket/mod.conf @@ -0,0 +1,2 @@ +name = bucket +description = Minimal bucket to pick up liquids diff --git a/data/games/garage/mods/bucket/textures/bucket.png b/data/games/garage/mods/bucket/textures/bucket.png new file mode 100644 index 0000000..6779528 Binary files /dev/null and b/data/games/garage/mods/bucket/textures/bucket.png differ diff --git a/data/games/garage/mods/bucket/textures/bucket_lava.png b/data/games/garage/mods/bucket/textures/bucket_lava.png new file mode 100644 index 0000000..dfcae65 Binary files /dev/null and b/data/games/garage/mods/bucket/textures/bucket_lava.png differ diff --git a/data/games/garage/mods/bucket/textures/bucket_water.png b/data/games/garage/mods/bucket/textures/bucket_water.png new file mode 100644 index 0000000..e164b0a Binary files /dev/null and b/data/games/garage/mods/bucket/textures/bucket_water.png differ diff --git a/data/games/garage/mods/callbacks/entities.lua b/data/games/garage/mods/callbacks/entities.lua new file mode 100644 index 0000000..340af17 --- /dev/null +++ b/data/games/garage/mods/callbacks/entities.lua @@ -0,0 +1,101 @@ +-- Entities that test their callbacks + +local message = function(msg) + core.log("action", "[callbacks] "..msg) + core.chat_send_all(msg) +end + +local get_object_name = function(obj) + local name = "" + if obj then + if obj:is_player() then + name = obj:get_player_name() + else + name = "" + end + end + return name +end + +local spos = function(self) + return core.pos_to_string(vector.round(self.object:get_pos())) +end + +-- Callback test entity (all callbacks except on_step) +core.register_entity("callbacks:callback", { + initial_properties = { + visual = "upright_sprite", + textures = { "callbacks_callback_entity.png" }, + }, + + on_activate = function(self, staticdata, dtime_s) + message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s) + end, + on_deactivate = function(self, removal) + message("Callback entity: on_deactivate! pos="..spos(self) .. "; removal=" .. tostring(removal)) + end, + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + local name = get_object_name(puncher) + message( + "Callback entity: on_punch! ".. + "pos="..spos(self).."; puncher="..name.."; ".. + "time_from_last_punch="..time_from_last_punch.."; ".. + "tool_capabilities="..tostring(dump(tool_capabilities)).."; ".. + "dir="..tostring(dump(dir)).."; damage="..damage) + end, + on_rightclick = function(self, clicker) + local name = get_object_name(clicker) + message("Callback entity: on_rightclick! pos="..spos(self).."; clicker="..name) + end, + on_death = function(self, killer) + local name = get_object_name(killer) + message("Callback entity: on_death! pos="..spos(self).."; killer="..name) + end, + on_attach_child = function(self, child) + local name = get_object_name(child) + message("Callback entity: on_attach_child! pos="..spos(self).."; child="..name) + end, + on_detach_child = function(self, child) + local name = get_object_name(child) + message("Callback entity: on_detach_child! pos="..spos(self).."; child="..name) + end, + on_detach = function(self, parent) + local name = get_object_name(parent) + message("Callback entity: on_detach! pos="..spos(self).."; parent="..name) + end, + get_staticdata = function(self) + message("Callback entity: get_staticdata! pos="..spos(self)) + end, +}) + +-- Only test on_step callback +core.register_entity("callbacks:callback_step", { + visual = "upright_sprite", + textures = { "callbacks_callback_entity_step.png" }, + on_step = function(self, dtime) + message("on_step callback entity: on_step! pos="..spos(self).."; dtime="..dtime) + end, +}) + +-- Callback punch with nil puncher +core.register_entity("callbacks:callback_puncher", { + initial_properties = { + visual = "upright_sprite", + textures = { "callbacks_callback_entity.png" }, + infotext = "Callback entity for nil puncher test.", + }, + + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + if puncher then + puncher:punch(nil, time_from_last_punch, tool_capabilities, dir) + self.object:punch(nil, time_from_last_punch, tool_capabilities, dir) + else + message( + "Callback entity: on_punch with nil puncher ".. + "pos="..spos(self).."; ".. + "time_from_last_punch="..time_from_last_punch.."; ".. + "tool_capabilities="..dump(tool_capabilities).."; ".. + "dir="..dump(dir).."; damage="..damage) + end + end, +}) diff --git a/data/games/garage/mods/callbacks/init.lua b/data/games/garage/mods/callbacks/init.lua new file mode 100644 index 0000000..ff2da97 --- /dev/null +++ b/data/games/garage/mods/callbacks/init.lua @@ -0,0 +1,4 @@ +dofile(core.get_modpath("callbacks").."/items.lua") +dofile(core.get_modpath("callbacks").."/nodes.lua") +dofile(core.get_modpath("callbacks").."/entities.lua") +dofile(core.get_modpath("callbacks").."/players.lua") diff --git a/data/games/garage/mods/callbacks/items.lua b/data/games/garage/mods/callbacks/items.lua new file mode 100644 index 0000000..880f393 --- /dev/null +++ b/data/games/garage/mods/callbacks/items.lua @@ -0,0 +1,120 @@ +-- +-- Item callbacks +-- + +local function print_to_everything(msg) + core.log("action", "[callbacks] " .. msg) + core.chat_send_all(msg) +end + +core.register_craftitem("callbacks:callback_item_1", { + description = "Callback Test Item 1".."\n".. + "Tests callbacks: on_secondary_use, on_drop, on_pickup, on_use, after_use".."\n".. + "Punch/Drop + Sneak: Switch to Callback Test Item 2".."\n".. + "Aux1 + pickup item: Print additional on_pickup arguments", + inventory_image = "callbacks_callback_item_1.png", + wield_image = "callbacks_callback_item_1.png", + groups = { callback_test = 1 }, + + on_secondary_use = function(itemstack, user, pointed_thing) + print_to_everything("[callbacks:callback_item_1 on_secondary_use] " .. itemstack:get_name()) + local ctrl = user and user:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("callbacks:callback_item_2") + return itemstack + end + end, + + on_drop = function(itemstack, dropper, pos) + print_to_everything("[callbacks:callback_item_1 on_drop] " .. itemstack:get_name()) + local ctrl = dropper and dropper:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("callbacks:callback_item_2") + end + + return core.item_drop(itemstack, dropper, pos) + end, + + on_pickup = function(itemstack, picker, pointed_thing, ...) + print_to_everything("[callbacks:callback_item_1 on_pickup]") + assert(pointed_thing.ref:get_luaentity().name == "__builtin:item") + local ctrl = picker and picker:get_player_control() or {} + if ctrl.aux1 then + -- Debug message + print_to_everything("on_pickup dump:") + print_to_everything(dump({...})) + end + if ctrl.sneak then + -- Pick up one item of the other kind at once + local taken = itemstack:take_item() + taken:set_name("callbacks:callback_item_2") + local leftover = core.item_pickup(taken, picker, pointed_thing, ...) + leftover:set_name("callbacks:callback_item_1") + itemstack:add_item(leftover) + return itemstack + elseif ctrl.up then + -- Don't pick up + return + elseif ctrl.left then + -- Eat it + return core.do_item_eat(2, nil, itemstack, picker, pointed_thing) + else + -- Normal: pick up everything + return core.item_pickup(itemstack, picker, pointed_thing, ...) + end + end, + + on_use = function(itemstack, user, pointed_thing) + print_to_everything("[callbacks:callback_item_1 on_use] " .. itemstack:get_name()) + local ctrl = user and user:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("callbacks:callback_item_2") + return itemstack + end + end, + + after_use = function(itemstack, user, node, digparams) -- never called + print_to_everything("[callbacks:callback_item_1 after_use]") + local ctrl = user and user:get_player_control() or {} + if ctrl.up then + itemstack = ItemStack(itemstack) + itemstack:set_name("callbacks:callback_item_2") + return itemstack + end + end, +}) + +core.register_craftitem("callbacks:callback_item_2", { + description = "Callback Test Item 2".."\n".. + "Punch to switch to Callback Test Item 1", + inventory_image = "callbacks_callback_item_2.png", + wield_image = "callbacks_callback_item_2.png", + groups = { callback_test = 1 }, + + on_use = function(itemstack, user, pointed_thing) + print_to_everything("[callbacks:callback_item_2 on_use]") + itemstack = ItemStack(itemstack) + itemstack:set_name("callbacks:callback_item_1") + return itemstack + end, +}) + +core.register_on_item_pickup(function(itemstack, picker, pointed_thing, time_from_last_punch, ...) + assert(not pointed_thing or pointed_thing.ref:get_luaentity().name == "__builtin:item") + + local item_name = itemstack:get_name() + if item_name ~= "callbacks:callback_item_1" and item_name ~= "callbacks:callback_item_2" then + return + end + print_to_everything("["..item_name.." register_on_item_pickup]") + + local ctrl = picker and picker:get_player_control() or {} + if item_name == "callbacks:callback_item_2" and not ctrl.sneak then + -- Same here. Pick up the other item type. + itemstack:set_name("callbacks:callback_item_1") + return picker:get_inventory():add_item("main", itemstack) + end +end) diff --git a/data/games/garage/mods/callbacks/mod.conf b/data/games/garage/mods/callbacks/mod.conf new file mode 100644 index 0000000..7f7b6b8 --- /dev/null +++ b/data/games/garage/mods/callbacks/mod.conf @@ -0,0 +1,2 @@ +name = callbacks +description = Adds various callback-related stuff diff --git a/data/games/garage/mods/callbacks/nodes.lua b/data/games/garage/mods/callbacks/nodes.lua new file mode 100644 index 0000000..67f4be4 --- /dev/null +++ b/data/games/garage/mods/callbacks/nodes.lua @@ -0,0 +1,51 @@ +local function print_to_everything(msg) + core.log("action", "[callbacks] " .. msg) + core.chat_send_all(msg) +end + +core.register_node("callbacks:callback_node", { + description = "Callback Test Node (construct/destruct/timer)".."\n".. + "Tests callbacks: on_construct, after_place_node, on_destruct, after_destruct, after_dig_node, on_timer", + tiles = {"callbacks_callback_node.png"}, + groups = {callback_test=1, dig_immediate=3}, + -- This was known to cause a bug in core.item_place_node() when used + -- via core.place_node(), causing a placer with no position + paramtype2 = "facedir", + drop = "", + + on_construct = function(pos) + print_to_everything("callbacks:callback_node:on_construct("..core.pos_to_string(pos)..")") + local meta = core.get_meta(pos) + meta:set_string("mine", "test") + local timer = core.get_node_timer(pos) + timer:start(4, 3) + end, + + after_place_node = function(pos, placer) + print_to_everything("callbacks:callback_node:after_place_node("..core.pos_to_string(pos)..")") + local meta = core.get_meta(pos) + if meta:get_string("mine") == "test" then + print_to_everything("correct metadata found") + else + print_to_everything("incorrect metadata found") + end + end, + + on_destruct = function(pos) + print_to_everything("callbacks:callback_node:on_destruct("..core.pos_to_string(pos)..")") + end, + + after_destruct = function(pos) + print_to_everything("callbacks:callback_node:after_destruct("..core.pos_to_string(pos)..")") + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + print_to_everything("callbacks:callback_node:after_dig_node("..core.pos_to_string(pos)..")") + end, + + on_timer = function(pos, elapsed) + print_to_everything("callbacks:callback_node:on_timer(): elapsed="..dump(elapsed)) + return true + end, +}) + diff --git a/data/games/garage/mods/callbacks/players.lua b/data/games/garage/mods/callbacks/players.lua new file mode 100644 index 0000000..2e9b2fa --- /dev/null +++ b/data/games/garage/mods/callbacks/players.lua @@ -0,0 +1,11 @@ + +local message = function(msg) + core.log("action", "[callbacks] "..msg) + core.chat_send_all(msg) +end + +core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + if not hitter then + message("Player "..player:get_player_name().." punched without hitter.") + end +end) diff --git a/data/games/garage/mods/callbacks/textures/callbacks_callback_entity.png b/data/games/garage/mods/callbacks/textures/callbacks_callback_entity.png new file mode 100644 index 0000000..5125cc6 Binary files /dev/null and b/data/games/garage/mods/callbacks/textures/callbacks_callback_entity.png differ diff --git a/data/games/garage/mods/callbacks/textures/callbacks_callback_entity_step.png b/data/games/garage/mods/callbacks/textures/callbacks_callback_entity_step.png new file mode 100644 index 0000000..7676150 Binary files /dev/null and b/data/games/garage/mods/callbacks/textures/callbacks_callback_entity_step.png differ diff --git a/data/games/garage/mods/callbacks/textures/callbacks_callback_item_1.png b/data/games/garage/mods/callbacks/textures/callbacks_callback_item_1.png new file mode 100644 index 0000000..070b534 Binary files /dev/null and b/data/games/garage/mods/callbacks/textures/callbacks_callback_item_1.png differ diff --git a/data/games/garage/mods/callbacks/textures/callbacks_callback_item_2.png b/data/games/garage/mods/callbacks/textures/callbacks_callback_item_2.png new file mode 100644 index 0000000..d90903a Binary files /dev/null and b/data/games/garage/mods/callbacks/textures/callbacks_callback_item_2.png differ diff --git a/data/games/garage/mods/callbacks/textures/callbacks_callback_node.png b/data/games/garage/mods/callbacks/textures/callbacks_callback_node.png new file mode 100644 index 0000000..e9d8743 Binary files /dev/null and b/data/games/garage/mods/callbacks/textures/callbacks_callback_node.png differ diff --git a/data/games/garage/mods/chest/chest.lua b/data/games/garage/mods/chest/chest.lua new file mode 100644 index 0000000..251133a --- /dev/null +++ b/data/games/garage/mods/chest/chest.lua @@ -0,0 +1,52 @@ +local function print_to_everything(msg) + core.log("action", "[chest] " .. msg) + core.chat_send_all(msg) +end + +core.register_node("chest:chest", { + description = "Chest" .. "\n" .. + "32 inventory slots", + tiles ={"chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:0,0", + "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0", + "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:0,1"}, + paramtype2 = "4dir", + groups = {dig_immediate=2,choppy=3,meta_is_privatizable=1}, + is_ground_content = false, + on_construct = function(pos) + local meta = core.get_meta(pos) + meta:set_string("formspec", + "size[8,9]".. + "list[current_name;main;0,0;8,4;]".. + "list[current_player;main;0,5;8,4;]" .. + "listring[]") + meta:set_string("infotext", "Chest") + local inv = meta:get_inventory() + inv:set_size("main", 8*4) + end, + can_dig = function(pos,player) + local meta = core.get_meta(pos); + local inv = meta:get_inventory() + return inv:is_empty("main") + end, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + print_to_everything("Chest: ".. player:get_player_name() .. " triggered 'allow put' (10) event for " .. stack:to_string()) + return 10 + end, + allow_metadata_inventory_take = function(pos, listname, index, stack, player) + print_to_everything("Chest: ".. player:get_player_name() .. " triggered 'allow take' (20) event for " .. stack:to_string()) + return 20 + end, + allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + print_to_everything("Chest: ".. player:get_player_name() .. " triggered 'allow move' (30) event") + return 30 + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + print_to_everything("Chest: ".. player:get_player_name() .. " put " .. stack:to_string()) + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + print_to_everything("Chest: ".. player:get_player_name() .. " took " .. stack:to_string()) + end, + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + print_to_everything("Chest: ".. player:get_player_name() .. " moved " .. count) + end, +}) diff --git a/data/games/garage/mods/chest/detached.lua b/data/games/garage/mods/chest/detached.lua new file mode 100644 index 0000000..bf6eea9 --- /dev/null +++ b/data/games/garage/mods/chest/detached.lua @@ -0,0 +1,58 @@ +local ALLOW_PUT_MAX = 1 +local ALLOW_TAKE_MAX = 4 + +local function print_to_everything(msg) + core.log("action", "[chest] " .. msg) + core.chat_send_all(msg) +end + +-- Create a detached inventory +local inv = core.create_detached_inventory("detached_inventory", { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + print_to_everything("Detached inventory: "..player:get_player_name().." triggered allow_move") + return count -- Allow all + end, + allow_put = function(inv, listname, index, stack, player) + print_to_everything("Detached inventory: "..player:get_player_name().." triggered allow_put for "..stack:to_string().." (max. allowed: "..ALLOW_PUT_MAX..")") + return ALLOW_PUT_MAX -- Allow to put a limited amount of items + end, + allow_take = function(inv, listname, index, stack, player) + print_to_everything("Detached inventory: "..player:get_player_name().." triggered allow_take for "..stack:to_string().." (max. allowed: "..ALLOW_TAKE_MAX..")") + return ALLOW_TAKE_MAX -- Allow to take a limited amount of items + end, + on_move = function(inv, from_list, from_index, to_list, to_index, count, player) + print_to_everything("Detached inventory: " .. player:get_player_name().." moved item(s)") + end, + on_put = function(inv, listname, index, stack, player) + print_to_everything("Detached inventory: " .. player:get_player_name().." put "..stack:to_string()) + end, + on_take = function(inv, listname, index, stack, player) + print_to_everything("Detached inventory: " .. player:get_player_name().." took "..stack:to_string()) + end, +}) +inv:set_size("main", 8*3) + + +-- Add a special chest to grant access to this inventory +core.register_node("chest:detached_chest", { + description = "Detached Chest" .. "\n" .. + "Grants access to a shared detached inventory" .."\n" .. + "Max. item put count: "..ALLOW_PUT_MAX .."\n".. + "Max. item take count: "..ALLOW_TAKE_MAX, + tiles = {"chest_detached_chest.png^[sheet:2x2:0,0", "chest_detached_chest.png^[sheet:2x2:0,0", + "chest_detached_chest.png^[sheet:2x2:1,0", "chest_detached_chest.png^[sheet:2x2:1,0", + "chest_detached_chest.png^[sheet:2x2:1,0", "chest_detached_chest.png^[sheet:2x2:0,1"}, + paramtype2 = "4dir", + groups = {dig_immediate=2,choppy=3,meta_is_privatizable=1}, + is_ground_content = false, + on_construct = function(pos) + local meta = core.get_meta(pos) + meta:set_string("formspec", + "size[8,9]".. + "list[detached:detached_inventory;main;0,0;8,3;0]".. + "list[current_player;main;0,5;8,4;]".. + "listring[]") + meta:set_string("infotext", "Detached Chest") + end, +}) + diff --git a/data/games/garage/mods/chest/init.lua b/data/games/garage/mods/chest/init.lua new file mode 100644 index 0000000..fad6cfd --- /dev/null +++ b/data/games/garage/mods/chest/init.lua @@ -0,0 +1,2 @@ +dofile(core.get_modpath("chest").."/chest.lua") +dofile(core.get_modpath("chest").."/detached.lua") diff --git a/data/games/garage/mods/chest/mod.conf b/data/games/garage/mods/chest/mod.conf new file mode 100644 index 0000000..7661297 --- /dev/null +++ b/data/games/garage/mods/chest/mod.conf @@ -0,0 +1,2 @@ +name = chest +description = A simple chest to store items. Also adds a detached inventory test diff --git a/data/games/garage/mods/chest/textures/chest_chest.png b/data/games/garage/mods/chest/textures/chest_chest.png new file mode 100644 index 0000000..824b4d5 Binary files /dev/null and b/data/games/garage/mods/chest/textures/chest_chest.png differ diff --git a/data/games/garage/mods/chest/textures/chest_detached_chest.png b/data/games/garage/mods/chest/textures/chest_detached_chest.png new file mode 100644 index 0000000..8e5dafe Binary files /dev/null and b/data/games/garage/mods/chest/textures/chest_detached_chest.png differ diff --git a/data/games/garage/mods/chest_of_everything/init.lua b/data/games/garage/mods/chest_of_everything/init.lua new file mode 100644 index 0000000..e8a1728 --- /dev/null +++ b/data/games/garage/mods/chest_of_everything/init.lua @@ -0,0 +1,353 @@ +local F = core.formspec_escape +local S = core.get_translator("chest_of_everything") + +local detached_inventories = {} + +-- Per-player lists (indexed by player name) +local current_pages = {} -- current page number +local current_max_pages = {} -- current max. page number +local current_searches = {} -- current search string + +local SLOTS_W = 10 +local SLOTS_H = 5 +local SLOTS = SLOTS_W * SLOTS_H + +-- This determines how the items are sorted +-- "by_type": Sort by item type (tool/craftitem/node/"chest_of_everything" items), then alphabetically by itemstring +-- "abc": Alphabetically by itemstring +local SORT_MODE = "by_type" + +local all_items_list -- cached list of all items + +-- Create detached inventories +local function add_detached_inventories(player) + local name = player:get_player_name() + local inv_items = core.create_detached_inventory("chest_of_everything_items_"..name, { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + allow_put = function(inv, listname, index, stack, player) + return 0 + end, + allow_take = function(inv, listname, index, stack, player) + return -1 + end, + }, name) + local inv_trash = core.create_detached_inventory("chest_of_everything_trash_"..name, { + allow_take = function(inv, listname, index, stack, player) + return 0 + end, + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + on_put = function(inv, listname, index, stack, player) + inv:set_list(listname, {}) + end, + }, name) + inv_trash:set_size("main", 1) + detached_inventories[name] = { items = inv_items, trash = inv_trash } +end + +local sort_items_by_type = function(item1, item2) + --[[ Sort items in this order: + * Bag of Everything + * Chest of Everything + * Test tools + * Other tools + * Craftitems + * Other items + * Items from the 'broken' mod + * Dummy items ]] + local def1 = core.registered_items[item1] + local def2 = core.registered_items[item2] + local tool1 = def1.type == "tool" + local tool2 = def2.type == "tool" + local testtool1 = core.get_item_group(item1, "testtool") == 1 + local testtool2 = core.get_item_group(item2, "testtool") == 1 + local dummy1 = core.get_item_group(item1, "dummy") == 1 + local dummy2 = core.get_item_group(item2, "dummy") == 1 + local broken1 = def1.mod_origin == "broken" + local broken2 = def2.mod_origin == "broken" + local craftitem1 = def1.type == "craft" + local craftitem2 = def2.type == "craft" + if item1 == "chest_of_everything:bag" then + return true + elseif item2 == "chest_of_everything:bag" then + return false + elseif item1 == "chest_of_everything:chest" then + return true + elseif item2 == "chest_of_everything:chest" then + return false + elseif dummy1 and not dummy2 then + return false + elseif not dummy1 and dummy2 then + return true + elseif broken1 and not broken2 then + return false + elseif not broken1 and broken2 then + return true + elseif testtool1 and not testtool2 then + return true + elseif not testtool1 and testtool2 then + return false + elseif tool1 and not tool2 then + return true + elseif not tool1 and tool2 then + return false + elseif craftitem1 and not craftitem2 then + return true + elseif not craftitem1 and craftitem2 then + return false + else + return item1 < item2 + end +end + +local sort_items_alphabetically = function(item1, item2) + return item1 < item2 +end + +local collect_items = function(filter, lang_code) + local items = {} + if filter then + filter = string.trim(filter) + filter = string.lower(filter) -- to make sure the search is case-insensitive + end + for itemstring, def in pairs(core.registered_items) do + if itemstring ~= "" and itemstring ~= "unknown" and itemstring ~= "ignore" then + if filter and lang_code then + local desc = ItemStack(itemstring):get_description() + local matches + -- First, try to match original description + if desc ~= "" then + local ldesc = string.lower(desc) + matches = string.match(ldesc, filter) ~= nil + -- Second, try to match translated description + if not matches then + local tdesc = core.get_translated_string(lang_code, desc) + if tdesc ~= "" then + tdesc = string.lower(tdesc) + matches = string.match(tdesc, filter) ~= nil + end + end + -- Third, try to match translated short description + if not matches then + local sdesc = ItemStack(itemstring):get_short_description() + if sdesc ~= "" then + sdesc = core.get_translated_string(lang_code, sdesc) + sdesc = string.lower(sdesc) + matches = string.match(sdesc, filter) ~= nil + end + end + + end + -- Fourth, try to match itemstring + if not matches then + matches = string.match(itemstring, filter) ~= nil + end + + -- If item was matched, add to item list + if matches then + table.insert(items, itemstring) + end + else + table.insert(items, itemstring) + end + end + end + local compare + if SORT_MODE == "by_type" then + compare = sort_items_by_type + elseif SORT_MODE == "abc" then + compare = sort_items_alphabetically + end + table.sort(items, compare) + + return items +end + +local function update_inventory(name) + local search = current_searches[name] or "" + local items + if search == "" then + items = all_items_list + else + local lang_code = core.get_player_information(name).lang_code + items = collect_items(search, lang_code) + end + local max_page = math.ceil(#items / SLOTS) + current_max_pages[name] = max_page + + local inv = detached_inventories[name].items + inv:set_size("main", #items) + inv:set_list("main", items) + if not current_pages[name] then + current_pages[name] = 1 + end + if current_pages[name] > max_page then + current_pages[name] = max_page + end + if current_pages[name] < 1 then + current_pages[name] = 1 + end +end + +local function get_formspec(page, name) + local start = 0 + (page-1)*SLOTS + if not name then + return "" + end + local player = core.get_player_by_name(name) + local playerinvsize = player:get_inventory():get_size("main") + local hotbarsize = player:hud_get_hotbar_itemcount() + local pinv_w, pinv_h, pinv_x + pinv_w = hotbarsize + pinv_h = math.ceil(playerinvsize / pinv_w) + pinv_w = math.min(pinv_w, 10) + pinv_h = math.min(pinv_w, 4) + pinv_x = 0 + if pinv_w < 9 then + pinv_x = 1 + end + + local pagestr = "" + local max_page = current_max_pages[name] + if max_page > 1 then + pagestr = "button[0,5.45;1,1;chest_of_everything_prev;"..F(S("<")).."]".. + "button[1,5.45;1,1;chest_of_everything_next;"..F(S(">")).."]".. + "label[0,5.1;"..F(S("Page: @1/@2", page, max_page)).."]" + end + + local search_text = current_searches[name] or "" + + local inventory_list + if current_max_pages[name] > 0 then + inventory_list = "list[detached:chest_of_everything_items_"..name..";main;0,0;"..SLOTS_W..","..SLOTS_H..";"..start.."]" + else + inventory_list = "label[2.5,2.5;"..F(S("No items found.")).."]" + if search_text ~= "" then + inventory_list = inventory_list .. "button[2.5,3.25;3,0.8;search_button_reset_big;"..F(S("Reset search")).."]" + end + end + + return "size[10,10.5]".. + inventory_list .. + "list[current_player;main;"..pinv_x..",6.75;"..pinv_w..","..pinv_h..";]" .. + "label[9,5.1;"..F(S("Trash:")).."]" .. + "list[detached:chest_of_everything_trash_"..name..";main;9,5.5;1,1]" .. + "field[2.2,5.75;4,1;search;;"..F(search_text).."]" .. + "field_enter_after_edit[search;true]" .. + "field_close_on_enter[search;false]" .. + "button[6,5.45;1.6,1;search_button_start;"..F(S("Search")).."]" .. + "button[7.6,5.45;0.8,1;search_button_reset;"..F(S("X")).."]" .. + "tooltip[search_button_reset;"..F(S("Reset search")).."]" .. + pagestr .. + "listring[detached:chest_of_everything_items_"..name..";main]".. + "listring[current_player;main]".. + "listring[detached:chest_of_everything_trash_"..name..";main]" +end + +local show_formspec = function(name) + local page = current_pages[name] + local form = get_formspec(page, name) + core.show_formspec(name, "chest_of_everything:getitem", form) + return true +end + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "chest_of_everything:getitem" then + return + end + local name = player:get_player_name() + local page = current_pages[name] + local old_page = page + -- Next page or previous page + if fields.chest_of_everything_next or fields.chest_of_everything_prev then + if fields.chest_of_everything_next then + page = page + 1 + elseif fields.chest_of_everything_prev then + page = page - 1 + end + -- Handle page change + if page < 1 then + page = 1 + end + local max_page = current_max_pages[name] + if page > max_page then + page = max_page + end + if page ~= old_page then + current_pages[name] = page + show_formspec(name) + end + return + -- Search + elseif (fields.search_button_start or (fields.key_enter and fields.key_enter_field == "search")) and fields.search then + current_searches[name] = fields.search + update_inventory(name) + show_formspec(name, fields.search) + return + -- Reset search + elseif (fields.search_button_reset or fields.search_button_reset_big) then + current_searches[name] = "" + update_inventory(name) + show_formspec(name) + return + end +end) + +core.register_tool("chest_of_everything:bag", { + description = S("Bag of Everything") .. "\n" .. + S("Grants access to all items"), + inventory_image = "chest_of_everything_bag.png", + wield_image = "chest_of_everything_bag.png", + groups = { disable_repair = 1 }, + on_use = function(itemstack, user) + if user and user:is_player() then + local name = user:get_player_name() + show_formspec(name) + end + end, +}) + +core.register_node("chest_of_everything:chest", { + description = S("Chest of Everything") .. "\n" .. + S("Grants access to all items"), + tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0", + "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", + "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"}, + paramtype2 = "4dir", + groups = { dig_immediate=2, choppy=3 }, + is_ground_content = false, + on_construct = function(pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", S("Chest of Everything")) + end, + on_rightclick = function(pos, node, clicker) + if clicker and clicker:is_player() then + local name = clicker:get_player_name() + show_formspec(name) + end + end, +}) + + +core.register_on_mods_loaded(function() + all_items_list = collect_items() +end) + +core.register_on_joinplayer(function(player) + local name = player:get_player_name() + current_searches[name] = "" + current_pages[name] = 1 + current_max_pages[name] = 0 + add_detached_inventories(player) + update_inventory(name) +end) + +core.register_on_leaveplayer(function(player) + local name = player:get_player_name() + current_pages[name] = nil + current_max_pages[name] = nil + current_searches[name] = nil +end) diff --git a/data/games/garage/mods/chest_of_everything/mod.conf b/data/games/garage/mods/chest_of_everything/mod.conf new file mode 100644 index 0000000..50dc2d5 --- /dev/null +++ b/data/games/garage/mods/chest_of_everything/mod.conf @@ -0,0 +1,2 @@ +name = chest_of_everything +description = Adds the chest of everything and bag of everything from which you can take all items diff --git a/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_bag.png b/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_bag.png new file mode 100644 index 0000000..7c18907 Binary files /dev/null and b/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_bag.png differ diff --git a/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_chest.png b/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_chest.png new file mode 100644 index 0000000..6b2fd58 Binary files /dev/null and b/data/games/garage/mods/chest_of_everything/textures/chest_of_everything_chest.png differ diff --git a/data/games/garage/mods/dignodes/init.lua b/data/games/garage/mods/dignodes/init.lua new file mode 100644 index 0000000..312207e --- /dev/null +++ b/data/games/garage/mods/dignodes/init.lua @@ -0,0 +1,39 @@ +local groups = { + "cracky", "dig_immediate" +} + +-- Register dig nodes with 1 digging group, a rating between 1-3 and a level between 0-2 +for g=1, #groups do + local gr = groups[g] + for r=1, 3 do + for l=0, 2 do + if not (gr=="dig_immediate" and (l>0 or r==1)) then + local d + if l > 0 then + d = string.format("Dig Test Node: %s=%d, level=%d", gr, r, l) + else + d = string.format("Dig Test Node: %s=%d", gr, r) + end + local tile = "dignodes_"..gr..".png^dignodes_rating"..r..".png" + if l==1 then + tile = tile .. "^[colorize:#FFFF00:127" + elseif l==2 then + tile = tile .. "^[colorize:#FF0000:127" + end + core.register_node("dignodes:"..gr.."_"..r.."_"..l, { + description = d, + tiles = { tile }, + groups = { [gr] = r, level = l }, + }) + end + end + end +end + +-- Node without any digging groups +core.register_node("dignodes:none", { + description = "Dig Test Node: groupless".."\n".. + "Can't be dug by normal digging tools".."\n".. + "(use the Remover tool to remove)", + tiles = {"dignodes_none.png"}, +}) diff --git a/data/games/garage/mods/dignodes/mod.conf b/data/games/garage/mods/dignodes/mod.conf new file mode 100644 index 0000000..52a80d6 --- /dev/null +++ b/data/games/garage/mods/dignodes/mod.conf @@ -0,0 +1,2 @@ +name = dignodes +description = Nodes with different digging groups diff --git a/data/games/garage/mods/dignodes/textures/dignodes_choppy.png b/data/games/garage/mods/dignodes/textures/dignodes_choppy.png new file mode 100644 index 0000000..a73fc24 Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_choppy.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_cracky.png b/data/games/garage/mods/dignodes/textures/dignodes_cracky.png new file mode 100644 index 0000000..eb84e30 Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_cracky.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_crumbly.png b/data/games/garage/mods/dignodes/textures/dignodes_crumbly.png new file mode 100644 index 0000000..23f2f7c Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_crumbly.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_dig_immediate.png b/data/games/garage/mods/dignodes/textures/dignodes_dig_immediate.png new file mode 100644 index 0000000..a532ad9 Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_dig_immediate.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_none.png b/data/games/garage/mods/dignodes/textures/dignodes_none.png new file mode 100644 index 0000000..60f1365 Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_none.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_rating1.png b/data/games/garage/mods/dignodes/textures/dignodes_rating1.png new file mode 100644 index 0000000..d2fee3a Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_rating1.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_rating2.png b/data/games/garage/mods/dignodes/textures/dignodes_rating2.png new file mode 100644 index 0000000..15329b9 Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_rating2.png differ diff --git a/data/games/garage/mods/dignodes/textures/dignodes_rating3.png b/data/games/garage/mods/dignodes/textures/dignodes_rating3.png new file mode 100644 index 0000000..37216bf Binary files /dev/null and b/data/games/garage/mods/dignodes/textures/dignodes_rating3.png differ diff --git a/data/games/garage/mods/first_mod/init.lua b/data/games/garage/mods/first_mod/init.lua new file mode 100644 index 0000000..3909a39 --- /dev/null +++ b/data/games/garage/mods/first_mod/init.lua @@ -0,0 +1 @@ +-- Nothing to do here, loading order is tested in C++ unittests. diff --git a/data/games/garage/mods/first_mod/mod.conf b/data/games/garage/mods/first_mod/mod.conf new file mode 100644 index 0000000..d35fa8d --- /dev/null +++ b/data/games/garage/mods/first_mod/mod.conf @@ -0,0 +1,2 @@ +name = first_mod +description = Mod which should be loaded before every other mod. diff --git a/data/games/garage/mods/give_initial_stuff/init.lua b/data/games/garage/mods/give_initial_stuff/init.lua new file mode 100644 index 0000000..4b31b52 --- /dev/null +++ b/data/games/garage/mods/give_initial_stuff/init.lua @@ -0,0 +1,37 @@ +local give_if_not_gotten_already = function(inv, list, item) + if not inv:contains_item(list, item) then + inv:add_item(list, item) + end +end + +local give_initial_stuff = function(player) + local inv = player:get_inventory() + give_if_not_gotten_already(inv, "main", "basetools:pick_mese") + give_if_not_gotten_already(inv, "main", "basetools:axe_steel") + give_if_not_gotten_already(inv, "main", "basetools:shovel_steel") + give_if_not_gotten_already(inv, "main", "bucket:bucket") + give_if_not_gotten_already(inv, "main", "testnodes:light14") + give_if_not_gotten_already(inv, "main", "chest_of_everything:bag") + core.log("action", "[give_initial_stuff] Giving initial stuff to "..player:get_player_name()) +end + +core.register_on_newplayer(function(player) + if core.settings:get_bool("give_initial_stuff", true) then + give_initial_stuff(player) + end +end) + +core.register_chatcommand("stuff", { + params = "", + privs = { give = true }, + description = "Give yourself initial items", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player or not player:is_player() then + return false, "No player." + end + give_initial_stuff(player) + return true + end, +}) + diff --git a/data/games/garage/mods/give_initial_stuff/mod.conf b/data/games/garage/mods/give_initial_stuff/mod.conf new file mode 100644 index 0000000..1ba49f5 --- /dev/null +++ b/data/games/garage/mods/give_initial_stuff/mod.conf @@ -0,0 +1,3 @@ +name = give_initial_stuff +description = Gives items to players on join +depends = basetools, bucket, chest_of_everything, testnodes diff --git a/data/games/garage/mods/gltf/LICENSE.md b/data/games/garage/mods/gltf/LICENSE.md new file mode 100644 index 0000000..6c3828a --- /dev/null +++ b/data/games/garage/mods/gltf/LICENSE.md @@ -0,0 +1,14 @@ +The glTF test models (and corresponding textures) in this mod are all licensed freely: + +* Spider (`gltf_spider.gltf`, `gltf_spider.png`): + * By [archfan7411](https://github.com/archfan7411) + * Licensed under CC0, public domain "wherever public domain carries fewer rights or legal protections" +* Frog (`gltf_frog.gltf`, `gltf_frog.png`): + * By [Susybaka1234](https://sketchfab.com/3d-models/african-clawed-frog-v2-c81152c93948480c931c280d18957358) + * Licensed under CC-BY 4.0 +* Snow Man (`gltf_snow_man.gltf`, `gltf_snow_man.png`): + * By [jordan4ibanez](https://github.com/jordan4ibanez) + * Licensed under CC0 +* Minimal triangle, triangle without indices (`gltf_minimal_triangle.gltf`, `gltf_triangle_without_indices.gltf`) + * From [the glTF sample model collection](https://github.com/KhronosGroup/glTF-Sample-Models) + * Licensed under CC0 / public domain diff --git a/data/games/garage/mods/gltf/init.lua b/data/games/garage/mods/gltf/init.lua new file mode 100644 index 0000000..fd4d13b --- /dev/null +++ b/data/games/garage/mods/gltf/init.lua @@ -0,0 +1,95 @@ +local function register_entity(name, textures, backface_culling) + core.register_entity("gltf:" .. name, { + initial_properties = { + visual = "mesh", + mesh = "gltf_" .. name .. ".gltf", + textures = textures, + backface_culling = backface_culling, + }, + }) +end + +-- These do not have texture coordinates; they simple render as black surfaces. +register_entity("minimal_triangle", {}, false) +register_entity("triangle_with_vertex_stride", {}, false) +register_entity("triangle_without_indices", {}, false) +do + local cube_textures = {"gltf_cube.png"} + register_entity("blender_cube", cube_textures) + register_entity("blender_cube_scaled", cube_textures) + register_entity("blender_cube_matrix_transform", cube_textures) + core.register_entity("gltf:blender_cube_glb", { + initial_properties = { + visual = "mesh", + mesh = "gltf_blender_cube.glb", + textures = cube_textures, + backface_culling = true, + }, + }) +end + +register_entity("snow_man", {"gltf_snow_man.png"}) +register_entity("spider", {"gltf_spider.png"}) + +core.register_entity("gltf:spider_animated", { + initial_properties = { + visual = "mesh", + mesh = "gltf_spider_animated.gltf", + textures = {"gltf_spider.png"}, + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 140}, 1) + end +}) + +core.register_entity("gltf:simple_skin", { + initial_properties = { + visual = "mesh", + visual_size = vector.new(5, 5, 5), + mesh = "gltf_simple_skin.gltf", + textures = {}, + backface_culling = false + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 5.5}, 1) + end +}) + +-- The claws rendering incorrectly from one side is expected behavior: +-- They use an unsupported double-sided material. +core.register_entity("gltf:frog", { + initial_properties = { + visual = "mesh", + mesh = "gltf_frog.gltf", + textures = {"gltf_frog.png"}, + backface_culling = false + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 0.75}, 1) + end +}) + + +core.register_node("gltf:frog", { + description = "glTF frog, but it's a node", + tiles = {{name = "gltf_frog.png", backface_culling = false}}, + drawtype = "mesh", + mesh = "gltf_frog.gltf", +}) + +core.register_chatcommand("show_model", { + params = " [textures]", + description = "Show a model (defaults to gltf models, for example '/show_model frog').", + func = function(name, param) + local model, textures = param:match"^(.-)%s+(.+)$" + if not model then + model = "gltf_" .. param .. ".gltf" + textures = "gltf_" .. param .. ".png" + end + core.show_formspec(name, "gltf:model", table.concat{ + "formspec_version[7]", + "size[10,10]", + "model[0,0;10,10;model;", model, ";", textures, ";0,0;true;true;0,0;0]", + }) + end, +}) diff --git a/data/games/garage/mods/gltf/invalid/empty.gltf b/data/games/garage/mods/gltf/invalid/empty.gltf new file mode 100644 index 0000000..e69de29 diff --git a/data/games/garage/mods/gltf/invalid/invalid_bufferview_bounds.gltf b/data/games/garage/mods/gltf/invalid/invalid_bufferview_bounds.gltf new file mode 100644 index 0000000..2182861 --- /dev/null +++ b/data/games/garage/mods/gltf/invalid/invalid_bufferview_bounds.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0}}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA","byteLength":36}],"bufferViews":[{"buffer":0,"byteOffset":1,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/invalid/json_missing_brace.gltf b/data/games/garage/mods/gltf/invalid/json_missing_brace.gltf new file mode 100644 index 0000000..98232c6 --- /dev/null +++ b/data/games/garage/mods/gltf/invalid/json_missing_brace.gltf @@ -0,0 +1 @@ +{ diff --git a/data/games/garage/mods/gltf/mod.conf b/data/games/garage/mods/gltf/mod.conf new file mode 100644 index 0000000..3ec50d2 --- /dev/null +++ b/data/games/garage/mods/gltf/mod.conf @@ -0,0 +1,2 @@ +name = gltf +description = Hosts gltf test models, both for the C++ unit tests and for in-game viewing diff --git a/data/games/garage/mods/gltf/models/gltf_blender_cube.glb b/data/games/garage/mods/gltf/models/gltf_blender_cube.glb new file mode 100644 index 0000000..b1894fc Binary files /dev/null and b/data/games/garage/mods/gltf/models/gltf_blender_cube.glb differ diff --git a/data/games/garage/mods/gltf/models/gltf_blender_cube.gltf b/data/games/garage/mods/gltf/models/gltf_blender_cube.gltf new file mode 100644 index 0000000..041b4a1 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_blender_cube.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","scale":[10,10,10]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/data/games/garage/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf b/data/games/garage/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf new file mode 100644 index 0000000..50235ce --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","matrix":[1,0,0,0,0,2,0,0,0,0,3,0,4,5,6,1]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/data/games/garage/mods/gltf/models/gltf_blender_cube_scaled.gltf b/data/games/garage/mods/gltf/models/gltf_blender_cube_scaled.gltf new file mode 100644 index 0000000..3b626b3 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_blender_cube_scaled.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","scale":[150,1,21.5]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/data/games/garage/mods/gltf/models/gltf_frog.gltf b/data/games/garage/mods/gltf/models/gltf_frog.gltf new file mode 100644 index 0000000..a41e9de --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_frog.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.9.4 glTF exporter"},"scenes":[{"nodes":[20],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"cube","mesh":1},{"name":"cube","mesh":2},{"name":"body","children":[0,1,2]},{"translation":[0,0,-0.0625],"name":"cube","mesh":3},{"translation":[0.03125,0,-0.3125],"name":"cube","mesh":4},{"rotation":[0,-0.19509032201612825,0,0.9807852804032304],"translation":[0.01812248876854733,-0.0625,-0.25194388507103505],"name":"cube","mesh":5},{"translation":[0.0625,0,0.3125],"name":"leftleg","children":[4,5,6]},{"translation":[0.0625,0,-0.3125],"name":"cube","mesh":6},{"translation":[-0.03125,0,-0.3125],"name":"cube","mesh":7},{"rotation":[0,0.19509032201612825,0,0.9807852804032304],"translation":[-0.01812248876854733,-0.0625,-0.25194388507103505],"name":"cube","mesh":8},{"translation":[-0.0625,0,0.3125],"name":"rightleg","children":[8,9,10]},{"translation":[-0.125,-0.0625,0.125],"name":"cube","mesh":9},{"rotation":[0,0.5372996083468239,0,0.8433914458128857],"translation":[0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":10},{"rotation":[0,0.5372996083468239,0,0.8433914458128857],"translation":[0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":11},{"translation":[0.125,0.0625,-0.125],"name":"leftarm","children":[12,13,14]},{"translation":[0.125,-0.0625,0.125],"name":"cube","mesh":12},{"rotation":[0,-0.5372996083468239,0,0.8433914458128857],"translation":[-0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":13},{"rotation":[0,-0.5372996083468239,0,0.8433914458128857],"translation":[-0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":14},{"translation":[-0.125,0.0625,-0.125],"name":"rightarm","children":[16,17,18]},{"children":[3,7,11,15,19]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":3360,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3648,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3936,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4128,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":4200,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4488,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4776,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4968,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5040,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5328,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5616,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":5808,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5880,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6168,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6456,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":6648,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":6720,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7008,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7296,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":7488,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":7560,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7848,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8136,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":8328,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":8400,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8688,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8976,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":9168,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":9240,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9528,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9816,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10008,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10080,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10368,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10656,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10848,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10920,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11208,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11496,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":11688,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":11760,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":12048,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":12336,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":12528,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":12600,"byteLength":12},{"buffer":0,"byteOffset":12612,"byteLength":48},{"buffer":0,"byteOffset":12660,"byteLength":12},{"buffer":0,"byteOffset":12672,"byteLength":48},{"buffer":0,"byteOffset":12720,"byteLength":12},{"buffer":0,"byteOffset":12732,"byteLength":48},{"buffer":0,"byteOffset":12780,"byteLength":12},{"buffer":0,"byteOffset":12792,"byteLength":48},{"buffer":0,"byteOffset":12840,"byteLength":12},{"buffer":0,"byteOffset":12852,"byteLength":48},{"buffer":0,"byteOffset":12900,"byteLength":12},{"buffer":0,"byteOffset":12912,"byteLength":48},{"buffer":0,"byteOffset":12960,"byteLength":12},{"buffer":0,"byteOffset":12972,"byteLength":48},{"buffer":0,"byteOffset":13020,"byteLength":12},{"buffer":0,"byteOffset":13032,"byteLength":48},{"buffer":0,"byteOffset":13080,"byteLength":12},{"buffer":0,"byteOffset":13092,"byteLength":48},{"buffer":0,"byteOffset":13140,"byteLength":4},{"buffer":0,"byteOffset":13144,"byteLength":16},{"buffer":0,"byteOffset":13160,"byteLength":4},{"buffer":0,"byteOffset":13164,"byteLength":16}],"buffers":[{"byteLength":13180,"uri":"data:application/octet-stream;base64,AAAgPgAAAD4AAIA+AAAgPgAAAD4AAIC9AAAgPgAAAAAAAIA+AAAgPgAAAAAAAIC9AAAgvgAAAD4AAIC9AAAgvgAAAD4AAIA+AAAgvgAAAAAAAIC9AAAgvgAAAAAAAIA+AAAgvgAAAD4AAIC9AAAgPgAAAD4AAIC9AAAgvgAAAD4AAIA+AAAgPgAAAD4AAIA+AAAgvgAAAAAAAIA+AAAgPgAAAAAAAIA+AAAgvgAAAAAAAIC9AAAgPgAAAAAAAIC9AAAgvgAAAD4AAIA+AAAgPgAAAD4AAIA+AAAgvgAAAAAAAIA+AAAgPgAAAAAAAIA+AAAgPgAAAD4AAIC9AAAgvgAAAD4AAIC9AAAgPgAAAAAAAIC9AAAgvgAAAAAAAIC9AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAID4AgB8+AIAgPgAAADoAgF8+AIAfPgCAXz4AQKA+AIAgPgDA7z4AgCA+AECgPgCAXz4AwO8+AIBfPgDAnz4AgB8+AIAgPgCAHz4AwJ8+AAAAOgCAID4AAAA6AMDvPgAAADoAQKA+AAAAOgDA7z4AgB8+AECgPgCAHz4AQPA+AIAgPgDgHz8AgCA+AEDwPgCAXz4A4B8/AIBfPgCAID4AgCA+AMCfPgCAID4AgCA+AIBfPgDAnz4AgF8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAPgAAAD4AAIC9AAAAPgAAAD4AAKC+AAAAPgAAAAAAAIC9AAAAPgAAAAAAAKC+AAAAvgAAAD4AAKC+AAAAvgAAAD4AAIC9AAAAvgAAAAAAAKC+AAAAvgAAAAAAAIC9AAAAvgAAAD4AAKC+AAAAPgAAAD4AAKC+AAAAvgAAAD4AAIC9AAAAPgAAAD4AAIC9AAAAvgAAAAAAAIC9AAAAPgAAAAAAAIC9AAAAvgAAAAAAAKC+AAAAPgAAAAAAAKC+AAAAvgAAAD4AAIC9AAAAPgAAAD4AAIC9AAAAvgAAAAAAAIC9AAAAPgAAAAAAAIC9AAAAPgAAAD4AAKC+AAAAvgAAAD4AAKC+AAAAPgAAAAAAAKC+AAAAvgAAAAAAAKC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgBAsD4AAP89AECwPgAAADoAwM8+AAD/PQDAzz4AQIA+AECwPgDAvz4AQLA+AECAPgDAzz4AwL8+AMDPPgCAfz4AwK8+AIAAPgDArz4AgH8+AIBgPgCAAD4AgGA+AMC/PgCAYD4AQIA+AIBgPgDAvz4AwK8+AECAPgDArz4AQMA+AECwPgDA/z4AQLA+AEDAPgDAzz4AwP8+AMDPPgCAAD4AQLA+AIB/PgBAsD4AgAA+AMDPPgCAfz4AwM8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAPQAAAD4AAKA+AAAAPQAAAD4AAIA+AAAAPQAAAAAAAKA+AAAAPQAAAAAAAIA+AAAAvQAAAD4AAIA+AAAAvQAAAD4AAKA+AAAAvQAAAAAAAIA+AAAAvQAAAAAAAKA+AAAAvQAAAD4AAIA+AAAAPQAAAD4AAIA+AAAAvQAAAD4AAKA+AAAAPQAAAD4AAKA+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAAAAAAIA+AAAAPQAAAAAAAIA+AAAAvQAAAD4AAKA+AAAAPQAAAD4AAKA+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAPQAAAD4AAIA+AAAAvQAAAD4AAIA+AAAAPQAAAAAAAIA+AAAAvQAAAAAAAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDwPgAAAj0AwP8+AAACPQBA8D4AAL89AMD/PgAAvz0AIAg/AAACPQDgDz8AAAI9ACAIPwAAvz0A4A8/AAC/PQDgBz8AAPw8ACAAPwAA/DwA4Ac/AAAAOgAgAD8AAAA6AOAPPwAAADoAIAg/AAAAOgDgDz8AAPw8ACAIPwAA/DwAIBA/AAACPQDgFz8AAAI9ACAQPwAAvz0A4Bc/AAC/PQAgAD8AAAI9AOAHPwAAAj0AIAA/AAC/PQDgBz8AAL89AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgPgAAAD4AAEA+AABgPgAAAD4AAAAAAABgPgAAAAAAAEA+AABgPgAAAAAAAAAAAAAAvQAAAD4AAAAAAAAAvQAAAD4AAEA+AAAAvQAAAAAAAAAAAAAAvQAAAAAAAEA+AAAAvQAAAD4AAAAAAABgPgAAAD4AAAAAAAAAvQAAAD4AAEA+AABgPgAAAD4AAEA+AAAAvQAAAAAAAEA+AABgPgAAAAAAAEA+AAAAvQAAAAAAAAAAAABgPgAAAAAAAAAAAAAAvQAAAD4AAEA+AABgPgAAAD4AAEA+AAAAvQAAAAAAAEA+AABgPgAAAAAAAEA+AABgPgAAAD4AAAAAAAAAvQAAAD4AAAAAAABgPgAAAAAAAAAAAAAAvQAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDQPgBA0D4AwP8+AEDQPgBA0D4AwO8+AMD/PgDA7z4AICA/AEDQPgDgNz8AQNA+ACAgPwDA7z4A4Dc/AMDvPgDgHz8AwM8+ACAAPwDAzz4A4B8/AECgPgAgAD8AQKA+AOA/PwBAoD4AICA/AECgPgDgPz8AwM8+ACAgPwDAzz4AIDg/AEDQPgDgVz8AQNA+ACA4PwDA7z4A4Fc/AMDvPgAgAD8AQNA+AOAfPwBA0D4AIAA/AMDvPgDgHz8AwO8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgPgAAwD0AABA/AABgPgAAwD0AAKA+AABgPgAAAD0AABA/AABgPgAAAD0AAKA+AADAPQAAwD0AAKA+AADAPQAAwD0AABA/AADAPQAAAD0AAKA+AADAPQAAAD0AABA/AADAPQAAwD0AAKA+AABgPgAAwD0AAKA+AADAPQAAwD0AABA/AABgPgAAwD0AABA/AADAPQAAAD0AABA/AABgPgAAAD0AABA/AADAPQAAAD0AAKA+AABgPgAAAD0AAKA+AADAPQAAwD0AABA/AABgPgAAwD0AABA/AADAPQAAAD0AABA/AABgPgAAAD0AABA/AABgPgAAwD0AAKA+AADAPQAAwD0AAKA+AABgPgAAAD0AAKA+AADAPQAAAD0AAKA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AECgPgAgGD8AwN8+ACAYPwBAoD4A4B8/AMDfPgDgHz8AIAA/ACAYPwDgHz8AIBg/ACAAPwDgHz8A4B8/AOAfPwDA/z4A4Bc/AEDgPgDgFz8AwP8+AEDwPgBA4D4AQPA+AOAPPwBA8D4AIAA/AEDwPgDgDz8A4Bc/ACAAPwDgFz8AICA/ACAYPwDgLz8AIBg/ACAgPwDgHz8A4C8/AOAfPwBA4D4AIBg/AMD/PgAgGD8AQOA+AOAfPwDA/z4A4B8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAMQjQPgAAQD4AAMA+MQjQPgAAQD4AAIA+MQjQPgAAgD0AAMA+MQjQPgAAgD0AAIA+AADQPgAAQD4AAIA+AADQPgAAQD4AAMA+AADQPgAAgD0AAIA+AADQPgAAgD0AAMA+AADQPgAAQD4AAIA+MQjQPgAAQD4AAIA+AADQPgAAQD4AAMA+MQjQPgAAQD4AAMA+AADQPgAAgD0AAMA+MQjQPgAAgD0AAMA+AADQPgAAgD0AAIA+MQjQPgAAgD0AAIA+AADQPgAAQD4AAMA+MQjQPgAAQD4AAMA+AADQPgAAgD0AAMA+MQjQPgAAgD0AAMA+MQjQPgAAQD4AAIA+AADQPgAAQD4AAIA+MQjQPgAAgD0AAIA+AADQPgAAgD0AAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAYD4AAH49AIBgPgAAADoAwI8+AAB+PQDAjz4AAIE9AIBgPgAA/z0AgGA+AACBPQDAjz4AAP89AMCPPgAAgT0AgF8+AAB+PQCAXz4AAIE9AIAgPgAAfj0AgCA+AACBPQCAID4AAH49AIAgPgAAgT0AgF8+AAB+PQCAXz4AgAA+AIBgPgAA/z0AgGA+AIAAPgDAjz4AAP89AMCPPgAAgT0AgGA+AAB+PQCAYD4AAIE9AMCPPgAAfj0AwI8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAvQAAAD4AAOA+AAAAvQAAAD4AAIA+AAAAvQAAAAAAAOA+AAAAvQAAAAAAAIA+AACQvgAAAD4AAIA+AACQvgAAAD4AAOA+AACQvgAAAAAAAIA+AACQvgAAAAAAAOA+AACQvgAAAD4AAIA+AAAAvQAAAD4AAIA+AACQvgAAAD4AAOA+AAAAvQAAAD4AAOA+AACQvgAAAAAAAOA+AAAAvQAAAAAAAOA+AACQvgAAAAAAAIA+AAAAvQAAAAAAAIA+AACQvgAAAD4AAOA+AAAAvQAAAD4AAOA+AACQvgAAAAAAAOA+AAAAvQAAAAAAAOA+AAAAvQAAAD4AAIA+AACQvgAAAD4AAIA+AAAAvQAAAAAAAIA+AACQvgAAAAAAAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAgAD8AAL89ACAAPwAAADoA4A8/AAC/PQDgDz8AgGA+ACAAPwDAnz4AIAA/AIBgPgDgDz8AwJ8+AOAPPwCAXz4AwP8+AADBPQDA/z4AgF8+AEDQPgAAwT0AQNA+AMCvPgBA0D4AgGA+AEDQPgDArz4AwP8+AIBgPgDA/z4AQKA+ACAAPwDA3z4AIAA/AECgPgDgDz8AwN8+AOAPPwAAwT0AIAA/AIBfPgAgAD8AAME9AOAPPwCAXz4A4A8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAADAvQAAwD0AABA/AADAvQAAwD0AAKA+AADAvQAAAD0AABA/AADAvQAAAD0AAKA+AABgvgAAwD0AAKA+AABgvgAAwD0AABA/AABgvgAAAD0AAKA+AABgvgAAAD0AABA/AABgvgAAwD0AAKA+AADAvQAAwD0AAKA+AABgvgAAwD0AABA/AADAvQAAwD0AABA/AABgvgAAAD0AABA/AADAvQAAAD0AABA/AABgvgAAAD0AAKA+AADAvQAAAD0AAKA+AABgvgAAwD0AABA/AADAvQAAwD0AABA/AABgvgAAAD0AABA/AADAvQAAAD0AABA/AADAvQAAwD0AAKA+AABgvgAAwD0AAKA+AADAvQAAAD0AAKA+AABgvgAAAD0AAKA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDwPgCAAD4A4Bc/AIAAPgBA8D4AgB8+AOAXPwCAHz4AICg/AIAAPgDgRz8AgAA+ACAoPwCAHz4A4Ec/AIAfPgDgJz8AAP89ACAYPwAA/z0A4Cc/AAAAOgAgGD8AAAA6AOA3PwAAADoAICg/AAAAOgDgNz8AAP89ACAoPwAA/z0AIEg/AIAAPgDgVz8AgAA+ACBIPwCAHz4A4Fc/AIAfPgAgGD8AgAA+AOAnPwCAAD4AIBg/AIAfPgDgJz8AgB8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAz/fPvgAAQD4AAMA+z/fPvgAAQD4AAIA+z/fPvgAAgD0AAMA+z/fPvgAAgD0AAIA+AADQvgAAQD4AAIA+AADQvgAAQD4AAMA+AADQvgAAgD0AAIA+AADQvgAAgD0AAMA+AADQvgAAQD4AAIA+z/fPvgAAQD4AAIA+AADQvgAAQD4AAMA+z/fPvgAAQD4AAMA+AADQvgAAgD0AAMA+z/fPvgAAgD0AAMA+AADQvgAAgD0AAIA+z/fPvgAAgD0AAIA+AADQvgAAQD4AAMA+z/fPvgAAQD4AAMA+AADQvgAAgD0AAMA+z/fPvgAAgD0AAMA+z/fPvgAAQD4AAIA+AADQvgAAQD4AAIA+z/fPvgAAgD0AAIA+AADQvgAAgD0AAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAAgT0AAH49AACBPQAAADoAAP89AAB+PQAA/z0AAIE9AACBPQAA/z0AAIE9AACBPQAA/z0AAP89AAD/PQAAgT0AAH49AAB+PQAAfj0AAIE9AAAAOgAAfj0AAAA6AACBPQAAADoAAH49AAAAOgAAgT0AAH49AAB+PQAAfj0AgAA+AACBPQAA/z0AAIE9AIAAPgAA/z0AAP89AAD/PQAAgT0AAIE9AAB+PQAAgT0AAIE9AAD/PQAAfj0AAP89AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACAPgAAwD0AAMC9AACAPgAAwD0AACC+AACAPgAAAD0AAMC9AACAPgAAAD0AACC+AAAAPgAAwD0AACC+AAAAPgAAwD0AAMC9AAAAPgAAAD0AACC+AAAAPgAAAD0AAMC9AAAAPgAAwD0AACC+AACAPgAAwD0AACC+AAAAPgAAwD0AAMC9AACAPgAAwD0AAMC9AAAAPgAAAD0AAMC9AACAPgAAAD0AAMC9AAAAPgAAAD0AACC+AACAPgAAAD0AACC+AAAAPgAAwD0AAMC9AACAPgAAwD0AAMC9AAAAPgAAAD0AAMC9AACAPgAAAD0AAMC9AACAPgAAwD0AACC+AAAAPgAAwD0AACC+AACAPgAAAD0AACC+AAAAPgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ACAIPwBAkD4A4A8/AECQPgAgCD8AwJ8+AOAPPwDAnz4AICA/AECQPgDgJz8AQJA+ACAgPwDAnz4A4Cc/AMCfPgDgHz8AwI8+ACAQPwDAjz4A4B8/AECAPgAgED8AQIA+AOAvPwBAgD4AICA/AECAPgDgLz8AwI8+ACAgPwDAjz4AICg/AECQPgDgNz8AQJA+ACAoPwDAnz4A4Dc/AMCfPgAgED8AQJA+AOAfPwBAkD4AIBA/AMCfPgDgHz8AwJ8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACQPgAAwD0AAMC9AACQPgAAwD0AACC+AACQPgAAAD0AAMC9AACQPgAAAD0AACC+AABgPgAAwD0AACC+AABgPgAAwD0AAMC9AABgPgAAAD0AACC+AABgPgAAAD0AAMC9AABgPgAAwD0AACC+AACQPgAAwD0AACC+AABgPgAAwD0AAMC9AACQPgAAwD0AAMC9AABgPgAAAD0AAMC9AACQPgAAAD0AAMC9AABgPgAAAD0AACC+AACQPgAAAD0AACC+AABgPgAAwD0AAMC9AACQPgAAwD0AAMC9AABgPgAAAD0AAMC9AACQPgAAAD0AAMC9AACQPgAAwD0AACC+AABgPgAAwD0AACC+AACQPgAAAD0AACC+AABgPgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgBAoD4AAPw8AECgPgAAADoAwK8+AAD8PADArz4AAIE9AECgPgAAvz0AQKA+AACBPQDArz4AAL89AMCvPgAAfj0AwJ8+AAACPQDAnz4AAH49AECQPgAAAj0AQJA+AAC/PQBAkD4AAIE9AECQPgAAvz0AwJ8+AACBPQDAnz4AAME9AECgPgAA/z0AQKA+AADBPQDArz4AAP89AMCvPgAAAj0AQKA+AAB+PQBAoD4AAAI9AMCvPgAAfj0AwK8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACwPsUggD0AAMC9AACwPsUggD0AACC+AACwPgAAgD0AAMC9AACwPgAAgD0AACC+AACQPsUggD0AACC+AACQPsUggD0AAMC9AACQPgAAgD0AACC+AACQPgAAgD0AAMC9AACQPsUggD0AACC+AACwPsUggD0AACC+AACQPsUggD0AAMC9AACwPsUggD0AAMC9AACQPgAAgD0AAMC9AACwPgAAgD0AAMC9AACQPgAAgD0AACC+AACwPgAAgD0AACC+AACQPsUggD0AAMC9AACwPsUggD0AAMC9AACQPgAAgD0AAMC9AACwPgAAgD0AAMC9AACwPsUggD0AACC+AACQPsUggD0AACC+AACwPgAAgD0AACC+AACQPgAAgD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAID4AAPw8AIAgPgAAADoAgB8+AAD8PACAHz4AAIE9AIAgPgAAvz0AgCA+AACBPQCAHz4AAL89AIAfPgAAfj0AgB8+AAACPQCAHz4AAH49AIAAPgAAAj0AgAA+AAC/PQCAAD4AAIE9AIAAPgAAvz0AgB8+AACBPQCAHz4AAME9AIAgPgAA/z0AgCA+AADBPQCAHz4AAP89AIAfPgAAAj0AgCA+AAB+PQCAID4AAAI9AIAfPgAAfj0AgB8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAvgAAwD0AAMC9AAAAvgAAwD0AACC+AAAAvgAAAD0AAMC9AAAAvgAAAD0AACC+AACAvgAAwD0AACC+AACAvgAAwD0AAMC9AACAvgAAAD0AACC+AACAvgAAAD0AAMC9AACAvgAAwD0AACC+AAAAvgAAwD0AACC+AACAvgAAwD0AAMC9AAAAvgAAwD0AAMC9AACAvgAAAD0AAMC9AAAAvgAAAD0AAMC9AACAvgAAAD0AACC+AAAAvgAAAD0AACC+AACAvgAAwD0AAMC9AAAAvgAAwD0AAMC9AACAvgAAAD0AAMC9AAAAvgAAAD0AAMC9AAAAvgAAwD0AACC+AACAvgAAwD0AACC+AAAAvgAAAD0AACC+AACAvgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDAPgBAgD4AwM8+AECAPgBAwD4AwI8+AMDPPgDAjz4AQPA+AECAPgDA/z4AQIA+AEDwPgDAjz4AwP8+AMCPPgDA7z4AgH8+AEDQPgCAfz4AwO8+AIBgPgBA0D4AgGA+AOAHPwCAYD4AQPA+AIBgPgDgBz8AgH8+AEDwPgCAfz4AIAA/AECAPgDgDz8AQIA+ACAAPwDAjz4A4A8/AMCPPgBA0D4AQIA+AMDvPgBAgD4AQNA+AMCPPgDA7z4AwI8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgvgAAwD0AAMC9AABgvgAAwD0AACC+AABgvgAAAD0AAMC9AABgvgAAAD0AACC+AACQvgAAwD0AACC+AACQvgAAwD0AAMC9AACQvgAAAD0AACC+AACQvgAAAD0AAMC9AACQvgAAwD0AACC+AABgvgAAwD0AACC+AACQvgAAwD0AAMC9AABgvgAAwD0AAMC9AACQvgAAAD0AAMC9AABgvgAAAD0AAMC9AACQvgAAAD0AACC+AABgvgAAAD0AACC+AACQvgAAwD0AAMC9AABgvgAAwD0AAMC9AACQvgAAAD0AAMC9AABgvgAAAD0AAMC9AABgvgAAwD0AACC+AACQvgAAwD0AACC+AABgvgAAAD0AACC+AACQvgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAAAj0AAPw8AAACPQAAADoAAH49AAD8PAAAfj0AAIE9AAACPQAAvz0AAAI9AACBPQAAfj0AAL89AAB+PQAAfj0AAPw8AAACPQAA/DwAAH49AAAAOgAAAj0AAAA6AAC/PQAAADoAAIE9AAAAOgAAvz0AAPw8AACBPQAA/DwAAME9AAACPQAA/z0AAAI9AADBPQAAfj0AAP89AAB+PQAAAj0AAAI9AAB+PQAAAj0AAAI9AAB+PQAAfj0AAH49AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACQvsUggD0AAMC9AACQvsUggD0AACC+AACQvgAAgD0AAMC9AACQvgAAgD0AACC+AACwvsUggD0AACC+AACwvsUggD0AAMC9AACwvgAAgD0AACC+AACwvgAAgD0AAMC9AACwvsUggD0AACC+AACQvsUggD0AACC+AACwvsUggD0AAMC9AACQvsUggD0AAMC9AACwvgAAgD0AAMC9AACQvgAAgD0AAMC9AACwvgAAgD0AACC+AACQvgAAgD0AACC+AACwvsUggD0AAMC9AACQvsUggD0AAMC9AACwvgAAgD0AAMC9AACQvgAAgD0AAMC9AACQvsUggD0AACC+AACwvsUggD0AACC+AACQvgAAgD0AACC+AACwvgAAgD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACBPQAAAj0AAL89AAACPQAAgT0AAPw8AAC/PQAA/DwAgAA+AAACPQCAHz4AAAI9AIAAPgAA/DwAgB8+AAD8PAAA/z0AAPw8AADBPQAA/DwAAP89AAAAOgAAwT0AAAA6AIAfPgAAADoAgAA+AAAAOgCAHz4AAPw8AIAAPgAA/DwAgCA+AAACPQCAPz4AAAI9AIAgPgAA/DwAgD8+AAD8PAAAwT0AAAI9AAD/PQAAAj0AAME9AAD8PAAA/z0AAPw8AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPxPyhT0AAAAAAAAAAK9zfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAADug4Q+AAAAAOpGdz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAADug4S+AAAAAOpGdz8AAAAAIbWyvAAAAABn8H8/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAACoqAU+AAAAAFXPfT8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAACoqAW+AAAAAFXPfT8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAD+rqko/AAAAAAAAAAAAAAAAAACAPwAAAAAhtbI8AAAAAGfwfz8AAAAAx71QPAAAAACu+n8/AAAAAAAAAD+rqko/AAAAAAAAAAAAAAAAAACAPwAAAAAhtbK8AAAAAGfwfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAQD8AAMA/AAAAAAAAAAAAAAAAAACAPwAAAAC2frK9AAAAAJ4Gfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAQD8AAMA/AAAAAAAAAAAAAAAAAACAPwAAAAC2frI9AAAAAJ4Gfz8AAAAAAAAAAAAAAAAAAIA/AAAAAKioBb4AAAAAAAAAAFXPfT8AAIA+qKgFvgAAAAAAAAAAVc99Pw=="}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[0.15625,0.125,0.25],"min":[-0.15625,0,-0.0625],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.62451171875,0.21826171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[0.125,0.125,-0.0625],"min":[-0.125,0,-0.3125],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.49951171875,0.40576171875],"min":[0.00048828125,0.21923828125],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[0.03125,0.125,0.3125],"min":[-0.03125,0,0.25],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.59326171875,0.09326171875],"min":[0.46923828125,0.00048828125],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":12,"componentType":5126,"count":24,"max":[0.21875,0.125,0.1875],"min":[-0.03125,0,0],"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":14,"componentType":5126,"count":24,"max":[0.84326171875,0.46826171875],"min":[0.40673828125,0.31298828125],"type":"VEC2"},{"bufferView":15,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":16,"componentType":5126,"count":24,"max":[0.21875,0.09375,0.5625],"min":[0.09375,0.03125,0.3125],"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":18,"componentType":5126,"count":24,"max":[0.68701171875,0.62451171875],"min":[0.31298828125,0.46923828125],"type":"VEC2"},{"bufferView":19,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":20,"componentType":5126,"count":24,"max":[0.406312495470047,0.1875,0.375],"min":[0.40625,0.0625,0.25],"type":"VEC3"},{"bufferView":21,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":22,"componentType":5126,"count":24,"max":[0.12548828125,0.28076171875],"min":[0.00048828125,0.15673828125],"type":"VEC2"},{"bufferView":23,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":24,"componentType":5126,"count":24,"max":[-0.03125,0.125,0.4375],"min":[-0.28125,0,0.25],"type":"VEC3"},{"bufferView":25,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":26,"componentType":5126,"count":24,"max":[0.43701171875,0.56201171875],"min":[0.00048828125,0.40673828125],"type":"VEC2"},{"bufferView":27,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":28,"componentType":5126,"count":24,"max":[-0.09375,0.09375,0.5625],"min":[-0.21875,0.03125,0.3125],"type":"VEC3"},{"bufferView":29,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":30,"componentType":5126,"count":24,"max":[0.84326171875,0.15576171875],"min":[0.46923828125,0.00048828125],"type":"VEC2"},{"bufferView":31,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":32,"componentType":5126,"count":24,"max":[-0.406187504529953,0.1875,0.375],"min":[-0.40625,0.0625,0.25],"type":"VEC3"},{"bufferView":33,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":34,"componentType":5126,"count":24,"max":[0.12548828125,0.12451171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":35,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":36,"componentType":5126,"count":24,"max":[0.25,0.09375,-0.09375],"min":[0.125,0.03125,-0.15625],"type":"VEC3"},{"bufferView":37,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":38,"componentType":5126,"count":24,"max":[0.71826171875,0.31201171875],"min":[0.53173828125,0.25048828125],"type":"VEC2"},{"bufferView":39,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":40,"componentType":5126,"count":24,"max":[0.28125,0.09375,-0.09375],"min":[0.21875,0.03125,-0.15625],"type":"VEC3"},{"bufferView":41,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":42,"componentType":5126,"count":24,"max":[0.12451171875,0.34326171875],"min":[0.00048828125,0.28173828125],"type":"VEC2"},{"bufferView":43,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":44,"componentType":5126,"count":24,"max":[0.34375,0.0625625029206276,-0.09375],"min":[0.28125,0.0625,-0.15625],"type":"VEC3"},{"bufferView":45,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":46,"componentType":5126,"count":24,"max":[0.12451171875,0.15673828125],"min":[0.00048828125,0.12548828125],"type":"VEC2"},{"bufferView":47,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":48,"componentType":5126,"count":24,"max":[-0.125,0.09375,-0.09375],"min":[-0.25,0.03125,-0.15625],"type":"VEC3"},{"bufferView":49,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":50,"componentType":5126,"count":24,"max":[0.56201171875,0.28076171875],"min":[0.37548828125,0.21923828125],"type":"VEC2"},{"bufferView":51,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":52,"componentType":5126,"count":24,"max":[-0.21875,0.09375,-0.09375],"min":[-0.28125,0.03125,-0.15625],"type":"VEC3"},{"bufferView":53,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":54,"componentType":5126,"count":24,"max":[0.12451171875,0.06201171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":55,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":56,"componentType":5126,"count":24,"max":[-0.28125,0.0625625029206276,-0.09375],"min":[-0.34375,0.0625,-0.15625],"type":"VEC3"},{"bufferView":57,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":58,"componentType":5126,"count":24,"max":[0.18701171875,0.03173828125],"min":[0.06298828125,0.00048828125],"type":"VEC2"},{"bufferView":59,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":60,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":61,"componentType":5126,"count":3,"max":[0.06540312618017197,0,0,1],"min":[0,0,0,0.9978589415550232],"type":"VEC4"},{"bufferView":62,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":63,"componentType":5126,"count":3,"max":[0,0.258819043636322,0,1],"min":[0,0,0,0.9659258127212524],"type":"VEC4"},{"bufferView":64,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":65,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.258819043636322,0,0.9659258127212524],"type":"VEC4"},{"bufferView":66,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":67,"componentType":5126,"count":3,"max":[0,0.13052618503570557,0,1],"min":[0,0,0,0.9914448857307434],"type":"VEC4"},{"bufferView":68,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":69,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.13052618503570557,0,0.9914448857307434],"type":"VEC4"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":12,"NORMAL":13,"TEXCOORD_0":14},"indices":15,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":16,"NORMAL":17,"TEXCOORD_0":18},"indices":19,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":20,"NORMAL":21,"TEXCOORD_0":22},"indices":23,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":24,"NORMAL":25,"TEXCOORD_0":26},"indices":27,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":28,"NORMAL":29,"TEXCOORD_0":30},"indices":31,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":32,"NORMAL":33,"TEXCOORD_0":34},"indices":35,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":36,"NORMAL":37,"TEXCOORD_0":38},"indices":39,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":40,"NORMAL":41,"TEXCOORD_0":42},"indices":43,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":44,"NORMAL":45,"TEXCOORD_0":46},"indices":47,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":48,"NORMAL":49,"TEXCOORD_0":50},"indices":51,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":52,"NORMAL":53,"TEXCOORD_0":54},"indices":55,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":56,"NORMAL":57,"TEXCOORD_0":58},"indices":59,"material":0}]}],"animations":[{"name":"animation.model.walk","samplers":[{"input":60,"output":61,"interpolation":"LINEAR"},{"input":62,"output":63,"interpolation":"LINEAR"},{"input":64,"output":65,"interpolation":"LINEAR"},{"input":66,"output":67,"interpolation":"LINEAR"},{"input":68,"output":69,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":3,"path":"rotation"}},{"sampler":1,"target":{"node":7,"path":"rotation"}},{"sampler":2,"target":{"node":11,"path":"rotation"}},{"sampler":3,"target":{"node":15,"path":"rotation"}},{"sampler":4,"target":{"node":19,"path":"rotation"}}]}]} \ No newline at end of file diff --git a/data/games/garage/mods/gltf/models/gltf_minimal_triangle.gltf b/data/games/garage/mods/gltf/models/gltf_minimal_triangle.gltf new file mode 100644 index 0000000..9a624f0 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_minimal_triangle.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=","byteLength":44}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":6,"target":34963},{"buffer":0,"byteOffset":8,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":3,"type":"SCALAR","max":[2],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/models/gltf_simple_skin.gltf b/data/games/garage/mods/gltf/models/gltf_simple_skin.gltf new file mode 100644 index 0000000..3d6c24a --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_simple_skin.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0,1]}],"nodes":[{"skin":0,"mesh":0},{"children":[2]},{"translation":[0.0,1.0,0.0],"rotation":[0.0,0.0,0.0,1.0]}],"meshes":[{"primitives":[{"attributes":{"POSITION":1,"JOINTS_0":2,"WEIGHTS_0":3},"indices":0}]}],"skins":[{"inverseBindMatrices":4,"joints":[1,2]}],"animations":[{"channels":[{"sampler":0,"target":{"node":2,"path":"rotation"}}],"samplers":[{"input":5,"interpolation":"LINEAR","output":6}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAvwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAvwAAAD8AAAAAAAAAPwAAAD8AAAAAAAAAvwAAgD8AAAAAAAAAPwAAgD8AAAAAAAAAvwAAwD8AAAAAAAAAPwAAwD8AAAAAAAAAvwAAAEAAAAAAAAAAPwAAAEAAAAAA","byteLength":168},{"uri":"data:application/gltf-buffer;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=","byteLength":320},{"uri":"data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8=","byteLength":128},{"uri":"data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/","byteLength":240}],"bufferViews":[{"buffer":0,"byteLength":48,"target":34963},{"buffer":0,"byteOffset":48,"byteLength":120,"target":34962},{"buffer":1,"byteLength":320,"byteStride":16},{"buffer":2,"byteLength":128},{"buffer":3,"byteLength":240}],"accessors":[{"bufferView":0,"componentType":5123,"count":24,"type":"SCALAR"},{"bufferView":1,"componentType":5126,"count":10,"type":"VEC3","max":[0.5,2.0,0.0],"min":[-0.5,0.0,0.0]},{"bufferView":2,"componentType":5123,"count":10,"type":"VEC4"},{"bufferView":2,"byteOffset":160,"componentType":5126,"count":10,"type":"VEC4"},{"bufferView":3,"componentType":5126,"count":2,"type":"MAT4"},{"bufferView":4,"componentType":5126,"count":12,"type":"SCALAR","max":[5.5],"min":[0.0]},{"bufferView":4,"byteOffset":48,"componentType":5126,"count":12,"type":"VEC4","max":[0.0,0.0,0.707,1.0],"min":[0.0,0.0,-0.707,0.707]}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/models/gltf_simple_sparse_accessor.gltf b/data/games/garage/mods/gltf/models/gltf_simple_sparse_accessor.gltf new file mode 100644 index 0000000..9798968 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_simple_sparse_accessor.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=","byteLength":284}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":72,"byteLength":168},{"buffer":0,"byteOffset":240,"byteLength":6},{"buffer":0,"byteOffset":248,"byteLength":36}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":36,"type":"SCALAR","max":[13],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":14,"type":"VEC3","max":[6,4,0],"min":[0,0,0],"sparse":{"count":3,"indices":{"bufferView":2,"byteOffset":0,"componentType":5123},"values":{"bufferView":3,"byteOffset":0}}}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/models/gltf_snow_man.gltf b/data/games/garage/mods/gltf/models/gltf_snow_man.gltf new file mode 100644 index 0000000..cd8c347 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_snow_man.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.6.0 glTF exporter"},"scenes":[{"nodes":[3],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"cube","mesh":1},{"name":"cube","mesh":2},{"children":[0,1,2]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963}],"buffers":[{"byteLength":2520,"uri":"data:application/octet-stream;base64,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"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[3,24,3],"min":[-3,18,-3],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.8333333134651184,1],"min":[0,0.25],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[5,10,5],"min":[-5,0,-5],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.625,1],"min":[0,0.375],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[4,18,4],"min":[-4,10,-4],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.7083333134651184,0.7916666865348816],"min":[0.2083333283662796,0.25],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0,"texCoord":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]}]} diff --git a/data/games/garage/mods/gltf/models/gltf_spider.gltf b/data/games/garage/mods/gltf/models/gltf_spider.gltf new file mode 100644 index 0000000..6698b6b --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_spider.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Spider"}],"materials":[{"doubleSided":true,"name":"Material.001","pbrMetallicRoughness":{}}],"meshes":[{"name":"Cube","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"max":[2.742279291152954,1.4045029878616333,2.0192716121673584],"min":[-2.742279291152954,-0.6434623599052429,-3.534085512161255],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":1500,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":12000,"byteOffset":12000},{"buffer":0,"byteLength":8000,"byteOffset":24000},{"buffer":0,"byteLength":3000,"byteOffset":32000}],"buffers":[{"byteLength":35000,"uri":"data:application/octet-stream;base64,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dQCGAHUAgACIAIsAkQCIAJEAjgCQAJIAnQCQAJ0AmwCcAJ8AmQCcAJkAlgCVAJcAjACVAIwAigCPAJoAlACPAJQAiQCeAJMAjQCeAI0AmACiAKgAqwCiAKsApQCnALQAtwCnALcAqgCyAKwArwCyAK8AtQCtAKAAowCtAKMAsACmAKEArgCmAK4AswC2ALEApAC2AKQAqQC6AMAAwwC6AMMAvQC/AMwAzgC/AM4AwQDKAMQAxwDKAMcAzQDGALkAuwDGALsAyAC+ALgAxQC+AMUAywDPAMkAvADPALwAwgDSANgA2wDSANsA1QDXAOQA5gDXAOYA2QDiANwA3wDiAN8A5QDeANEA0wDeANMA4ADWANAA3QDWAN0A4wDnAOEA1ADnANQA2gDpAOsA8QDpAPEA7wDwAPIA/QDwAP0A+wD8AP4A+AD8APgA9gD1APcA7AD1AOwA6gDuAPoA9ADuAPQA6AD/APMA7QD/AO0A+QAAAQMBCQEAAQkBBgEHAQoBFQEHARUBEgEUARcBEQEUAREBDgENARABBQENAQUBAgEIARMBDAEIAQwBAQEWAQsBBAEWAQQBDwEpAR0BIwEpASMBLwEoASYBGQEoARkBGwEtASsBJQEtASUBJwEhAR8BLAEhASwBLgEYASQBKgEYASoBHgEcARoBIAEcASABIgExATMBOgExAToBOAE3ATkBRQE3AUUBQwFEAUYBPwFEAT8BPQE+AUABNAE+ATQBMgE2AUIBPAE2ATwBMAFHATsBNQFHATUBQQFJAUsBUQFJAVEBTwFQAVIBXQFQAV0BWwFcAV4BWAFcAVgBVgFVAVcBTAFVAUwBSgFOAVoBVAFOAVQBSAFfAVMBTQFfAU0BWQFgAWMBaQFgAWkBZgFoAWoBdQFoAXUBcwF0AXcBcQF0AXEBbgFtAW8BZAFtAWQBYgFnAXIBbAFnAWwBYQF2AWsBZQF2AWUBcAGJAX0BgwGJAYMBjwGIAYYBeQGIAXkBewGNAYsBhQGNAYUBhwGBAX8BjAGBAYwBjgF4AYQBigF4AYoBfgF8AXoBgAF8AYABggGRAZMBmQGRAZkBlwGYAZoBpQGYAaUBowGkAaYBoAGkAaABngGdAZ8BlAGdAZQBkgGWAaIBnAGWAZwBkAGnAZsBlQGnAZUBoQGpAasBsQGpAbEBrwGwAbIBvQGwAb0BuwG8Ab4BuAG8AbgBtgG1AbcBrAG1AawBqgGuAboBtAGuAbQBqAG/AbMBrQG/Aa0BuQHAAcMByQHAAckBxgHIAcoB1QHIAdUB0wHUAdcB0QHUAdEBzgHNAc8BxAHNAcQBwgHHAdIBzAHHAcwBwQHWAcsBxQHWAcUB0AHpAd0B4wHpAeMB7wHoAeYB2QHoAdkB2wHtAesB5QHtAeUB5wHhAd8B7AHhAewB7gHYAeQB6gHYAeoB3gHcAdoB4AHcAeAB4gHxAfMB+QHxAfkB9wH4AfoBBQL4AQUCAwIEAgYCAAIEAgAC/gH9Af8B9AH9AfQB8gH2AQIC/AH2AfwB8AEHAvsB9QEHAvUBAQIJAgsCEQIJAhECDwIQAhICHQIQAh0CGwIcAh4CGAIcAhgCFgIVAhcCDAIVAgwCCgIOAhoCFAIOAhQCCAIfAhMCDQIfAg0CGQIgAiMCKQIgAikCJgIoAisCNgIoAjYCMwI0AjcCMQI0AjECLgIsAi8CJAIsAiQCIQInAjICLQInAi0CIgI1AioCJQI1AiUCMAJJAj0CQwJJAkMCTwJIAkYCOQJIAjkCOwJNAksCRQJNAkUCRwJBAj8CTAJBAkwCTgI4AkQCSgI4AkoCPgI8AjoCQAI8AkACQgJSAlQCWQJSAlkCVwJYAloCZgJYAmYCZAJjAmUCYAJjAmACXgJdAl8CUwJdAlMCUQJWAmICXAJWAlwCUAJnAlsCVQJnAlUCYQJpAm8CcwJpAnMCbQJuAnsCfwJuAn8CcgJ6AnQCeAJ6AngCfgJ1AmgCbAJ1AmwCeQJwAmoCdgJwAnYCfAJ9AncCawJ9AmsCcQKCAogCiwKCAosChQKHApQClwKHApcCigKSAowCjwKSAo8ClQKNAoACgwKNAoMCkAKGAoECjgKGAo4CkwKWApEChAKWAoQCiQKnAq0CoQKnAqECmwKoAp0CmQKoApkCpAKvAqkCpQKvAqUCqwKjAq4CqgKjAqoCnwKaAqACrAKaAqwCpgKcAqICngKcAp4CmAKxArYCugKxAroCtQK3AsMCxwK3AscCuwLCAr0CwQLCAsECxgK8ArACtAK8ArQCwAK4ArICvgK4Ar4CxALFAr8CswLFArMCuQLJAs8C0wLJAtMCzQLOAtsC3wLOAt8C0gLaAtQC2ALaAtgC3gLVAsgCzALVAswC2QLQAsoC1gLQAtYC3ALdAtcCywLdAssC0QLiAugC6wLiAusC5QLmAvMC9wLmAvcC6gLyAuwC7wLyAu8C9QLtAuAC5ALtAuQC8QLnAuEC7gLnAu4C9AL2AvAC4wL2AuMC6QIHAw0DAQMHAwED+wIIA/0C+QIIA/kCBAMPAwkDBQMPAwUDCwMDAw4DCgMDAwoD/wL6AgADDAP6AgwDBgP8AgID/gL8Av4C+AIRAxcDGwMRAxsDFQMWAyMDJwMWAycDGgMiAxwDIAMiAyADJgMdAxADFAMdAxQDIQMYAxIDHgMYAx4DJAMlAx8DEwMlAxMDGQMpAy8DMwMpAzMDLQMuAzsDPwMuAz8DMgM6AzQDOAM6AzgDPgM1AygDLAM1AywDOQMwAyoDNgMwAzYDPAM9AzcDKwM9AysDMQNCA0gDSwNCA0sDRQNGA1MDVwNGA1cDSgNSA0wDTwNSA08DVQNNA0ADRANNA0QDUQNHA0EDTgNHA04DVANWA1ADQwNWA0MDSQNnA20DYQNnA2EDWwNoA10DWQNoA1kDZANvA2kDZQNvA2UDawNjA24DagNjA2oDXwNaA2ADbANaA2wDZgNcA2IDXgNcA14DWANxA3cDewNxA3sDdQN2A4MDhwN2A4cDegOCA3wDgAOCA4ADhgN9A3ADdAN9A3QDgQN4A3IDfgN4A34DhAOFA38DcwOFA3MDeQOJA48DkwOJA5MDjQOOA5sDnwOOA58DkgOaA5QDmAOaA5gDngOVA4gDjAOVA4wDmQOQA4oDlgOQA5YDnAOdA5cDiwOdA4sDkQOiA6gDqwOiA6sDpQOmA7MDtgOmA7YDqQOyA6wDrwOyA68DtQOuA6EDpAOuA6QDsQOnA6ADrQOnA60DtAO3A7ADowO3A6MDqgPHA80DwQPHA8EDuwPIA70DuQPIA7kDxAPPA8kDxQPPA8UDywPDA84DygPDA8oDvwO6A8ADzAO6A8wDxgO8A8IDvgO8A74DuAPQA9cD2wPQA9sD1APWA+ID5gPWA+YD2gPjA9wD4APjA+AD5wPdA9ED1QPdA9UD4QPYA9ID3gPYA94D5APlA98D0wPlA9MD2QM="}]} diff --git a/data/games/garage/mods/gltf/models/gltf_spider_animated.gltf b/data/games/garage/mods/gltf/models/gltf_spider_animated.gltf new file mode 100644 index 0000000..79221b0 --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_spider_animated.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[58]}],"nodes":[{"name":"Pincer.L","rotation":[0.03853772580623627,0.09671717882156372,0.5138389468193054,0.8515457510948181],"translation":[-2.2351741790771484e-08,0.2836739718914032,-2.2351741790771484e-08]},{"children":[0],"name":"JawBase.L","rotation":[-0.23922589421272278,-9.208349638356594e-08,-0.38811206817626953,0.8900224566459656],"scale":[1,1,0.9999999403953552],"translation":[8.097286041675034e-08,0.7702280879020691,-1.169656727029178e-07]},{"name":"Pincer.R","rotation":[0.038537755608558655,-0.09671713411808014,-0.5138388872146606,0.8515458106994629],"scale":[0.9999997615814209,0.9999999403953552,1],"translation":[2.9802322387695312e-08,0.2836737036705017,-2.9802322387695312e-08]},{"children":[2],"name":"JawBase.R","rotation":[-0.2392251342535019,1.9714243535418063e-06,0.3881126046180725,0.8900225758552551],"translation":[1.3833086898173974e-09,0.7702280282974243,-6.620245329713725e-08]},{"children":[1,3],"name":"Head","rotation":[0.4052415192127228,-3.4197712478652165e-13,-8.695541282577324e-07,0.9142096638679504],"translation":[-2.0781445141115906e-16,0.6883190274238586,-1.4901161193847656e-08]},{"children":[4],"name":"NeckBase","rotation":[-0.778048574924469,7.488795716881214e-08,1.7622618315726868e-06,0.6282041072845459],"translation":[-3.399441372928941e-14,0.3915250301361084,-2.3283064365386963e-09]},{"name":"Body.002","rotation":[0.17414046823978424,0,-4.151832513343834e-07,0.9847208261489868],"translation":[5.897654597759178e-14,1.0079095363616943,-1.2134763416327132e-08]},{"children":[6],"name":"Body.001","rotation":[0.6673352122306824,-7.632472941426077e-14,-1.5910509318928234e-06,0.7447575330734253],"scale":[1,0.9999999403953552,0.9999999403953552],"translation":[-3.962037268363458e-15,0.3915250301361084,-3.1428597502269895e-09]},{"name":"Leg4Fore.L","rotation":[-0.021953541785478592,0.030033688992261887,-0.4378480017185211,0.8982790112495422],"scale":[1,0.9999998211860657,1.0000001192092896],"translation":[1.9202238377147296e-07,0.8228543996810913,-1.749940707895803e-07]},{"children":[8],"name":"Leg4Lower.L","rotation":[-0.11090508848428726,0.11991499364376068,-0.48737218976020813,0.8577813506126404],"scale":[0.9999995231628418,0.9999997615814209,1.000000238418579],"translation":[1.9631791303709178e-07,0.8208085298538208,-4.769351491518137e-08]},{"children":[9],"name":"Leg4Mid.L","rotation":[-0.21032677590847015,0.09273893386125565,-0.42330121994018555,0.8763437271118164],"scale":[1,0.9999996423721313,0.999999463558197],"translation":[-1.720833893159579e-07,1.5146127939224243,1.4611718768264836e-07]},{"children":[10],"name":"Leg4Upper.L","rotation":[0.581340491771698,-0.03387186676263809,0.4926694631576538,0.6466628909111023],"scale":[1.0000003576278687,1.000000238418579,1.0000003576278687],"translation":[-2.6137989550534257e-09,0.8996680974960327,-2.8558396536482178e-08]},{"children":[11],"name":"Leg4Base.L","rotation":[0.4988132119178772,0.67340087890625,0.0026363276410847902,0.5456278324127197],"scale":[0.9999998807907104,0.9999998807907104,0.9999999403953552],"translation":[6.932457941033476e-10,0.3915250301361084,-7.783789612858527e-09]},{"name":"Leg3Fore.L","rotation":[-0.040254246443510056,0.0051941643469035625,-0.3734953701496124,0.9267436861991882],"scale":[1.0000001192092896,1,1.0000003576278687],"translation":[-7.186849302343035e-07,0.761729896068573,1.4940267689667053e-08]},{"children":[13],"name":"Leg3Lower.L","rotation":[-0.02293548174202442,0.03108014352619648,-0.5376279950141907,0.8422969579696655],"scale":[1,0.9999998807907104,1],"translation":[2.6879865799855907e-07,0.890315592288971,-2.3254589365251377e-08]},{"children":[14],"name":"Leg3Mid.L","rotation":[-0.10393687337636948,0.026799378916621208,-0.47722530364990234,0.8722012042999268],"scale":[1,1.0000001192092896,1],"translation":[-4.5783519908582093e-07,1.5058820247650146,6.428529530921878e-08]},{"children":[15],"name":"Leg3Upper.L","rotation":[0.22388437390327454,0.00046301534166559577,0.7424523234367371,0.6313795447349548],"scale":[0.9999996423721313,0.9999999403953552,0.9999995231628418],"translation":[8.173065424443848e-08,0.8363674879074097,-3.891337030381692e-09]},{"children":[16],"name":"Leg3Base.L","rotation":[0.48101410269737244,0.8565962910652161,-0.006458853371441364,0.18661867082118988],"scale":[0.9999999403953552,0.9999998807907104,0.9999999403953552],"translation":[1.1383551878907383e-08,0.3915250301361084,2.5693926986036786e-09]},{"name":"Leg2Fore.L","rotation":[0.04987334460020065,-0.01207074522972107,-0.4028705060482025,0.9138174653053284],"scale":[0.999999463558197,1.0000004768371582,1.0000001192092896],"translation":[2.9161077463868423e-07,0.7777483463287354,1.7455030842938868e-07]},{"children":[18],"name":"Leg2Lower.L","rotation":[0.08352424204349518,-0.04269447177648544,-0.518085777759552,0.8501694202423096],"scale":[1.0000001192092896,1.0000004768371582,1.0000004768371582],"translation":[-1.5067358560827415e-07,0.9397417306900024,-4.163759115272114e-08]},{"children":[19],"name":"Leg2Mid.L","rotation":[0.14706559479236603,-0.028868243098258972,-0.47296836972236633,0.868239164352417],"scale":[1.0000004768371582,0.9999999403953552,0.9999999403953552],"translation":[-2.2217867012841452e-07,1.5058820247650146,-6.989571943449846e-08]},{"children":[20],"name":"Leg2Upper.L","rotation":[-0.42424774169921875,-0.0005238422891125083,0.6472405791282654,0.6333192586898804],"scale":[1.000000238418579,1.0000003576278687,1.0000005960464478],"translation":[9.311328597050306e-08,0.881853461265564,-1.8038990745594674e-08]},{"children":[21],"name":"Leg2Base.L","rotation":[-0.4972459375858307,-0.7882749438285828,0.006057440303266048,0.362398236989975],"scale":[0.9999997615814209,0.9999998807907104,0.9999999403953552],"translation":[7.375939858889069e-10,0.3915250301361084,4.028271050060539e-09]},{"name":"Leg1Fore.L","rotation":[-0.01934647001326084,-0.04218549281358719,-0.4403696358203888,0.8966162800788879],"scale":[1,1,0.9999997615814209],"translation":[2.0805721590022586e-07,0.815664529800415,4.0515438115562574e-08]},{"children":[23],"name":"Leg1Lower.L","rotation":[0.15678077936172485,-0.1661715805530548,-0.47995010018348694,0.8470270037651062],"scale":[0.999999463558197,1,0.9999999403953552],"translation":[3.670676562705921e-08,0.8788074851036072,8.29251618483795e-08]},{"children":[24],"name":"Leg1Mid.L","rotation":[0.26206591725349426,-0.11672191321849823,-0.4046621024608612,0.8683006763458252],"scale":[1.0000001192092896,0.9999999403953552,0.9999995231628418],"translation":[3.601947184961318e-08,1.5125981569290161,-1.6144279868512967e-07]},{"children":[25],"name":"Leg1Upper.L","rotation":[-0.62815922498703,0.04343283176422119,0.39305803179740906,0.6701006889343262],"translation":[-1.0171092412747385e-07,1.043814778327942,1.114601104745816e-07]},{"children":[26],"name":"Leg1Base.L","rotation":[-0.536352813243866,-0.596045732498169,-0.006935927551239729,0.5975006818771362],"scale":[0.9999998211860657,0.9999998211860657,1],"translation":[7.451212979958655e-09,0.3915250301361084,-5.977072614626877e-09]},{"name":"Leg4Fore.R","rotation":[-0.0219536405056715,-0.030033595860004425,0.43784812092781067,0.8982789516448975],"scale":[1.000000238418579,0.9999998807907104,1.0000001192092896],"translation":[4.575199454848189e-07,0.82285475730896,1.3987688873839943e-07]},{"children":[28],"name":"Leg4Lower.R","rotation":[-0.11090517044067383,-0.11991491913795471,0.48737218976020813,0.8577813506126404],"scale":[1.0000001192092896,0.9999999403953552,1.0000001192092896],"translation":[5.0247152216797986e-08,0.8208085894584656,1.2523592829438712e-07]},{"children":[29],"name":"Leg4Mid.R","rotation":[-0.21032673120498657,-0.09273889660835266,0.42330119013786316,0.876343846321106],"scale":[0.9999998211860657,0.9999995231628418,1.0000001192092896],"translation":[-1.2884336797469587e-07,1.514613151550293,6.563716681284859e-08]},{"children":[30],"name":"Leg4Upper.R","rotation":[0.5813404321670532,0.03387187048792839,-0.4926694333553314,0.6466629505157471],"scale":[1,1.000000238418579,0.9999997019767761],"translation":[-3.940737158814045e-08,0.8996680974960327,1.9567494291550247e-09]},{"children":[31],"name":"Leg4Base.R","rotation":[0.4988132119178772,-0.6733996272087097,-0.0026374668814241886,0.5456294417381287],"scale":[1,1.0000001192092896,1],"translation":[-1.1682686817948706e-08,0.3915250301361084,-1.3812247345867945e-08]},{"name":"Leg3Fore.R","rotation":[-0.04025428742170334,-0.005194155499339104,0.3734953999519348,0.9267436861991882],"scale":[0.9999998211860657,1.0000001192092896,1.0000001192092896],"translation":[-7.285660217348777e-07,0.7617300748825073,-4.0205627271916455e-08]},{"children":[33],"name":"Leg3Lower.R","rotation":[-0.02293553575873375,-0.03108006715774536,0.5376282930374146,0.8422967791557312],"scale":[1.0000001192092896,0.9999996423721313,0.9999999403953552],"translation":[7.143101754536474e-08,0.8903149366378784,6.888667769544554e-08]},{"children":[34],"name":"Leg3Mid.R","rotation":[-0.10393673926591873,-0.026799339801073074,0.47722548246383667,0.872201144695282],"scale":[1.0000003576278687,0.9999998807907104,0.9999998807907104],"translation":[1.4287303429227904e-07,1.5058823823928833,9.578651827268914e-08]},{"children":[35],"name":"Leg3Upper.R","rotation":[0.2238844484090805,-0.00046323961578309536,-0.7424524426460266,0.6313793659210205],"scale":[1.0000001192092896,1.0000005960464478,0.9999997615814209],"translation":[-2.9145089897042453e-08,0.8363675475120544,-1.3412945421009681e-08]},{"children":[36],"name":"Leg3Base.R","rotation":[0.48101410269737244,-0.8565958738327026,0.006457682233303785,0.18662074208259583],"scale":[0.9999999403953552,1.0000001192092896,1],"translation":[1.187698939197901e-09,0.3915250301361084,1.396204218906405e-08]},{"name":"Leg2Fore.R","rotation":[0.04987342655658722,0.012070796452462673,0.40287071466445923,0.9138173460960388],"scale":[0.9999997615814209,0.9999998807907104,0.9999997019767761],"translation":[4.900767294202524e-07,0.7777489423751831,1.3496240569565998e-07]},{"children":[38],"name":"Leg2Lower.R","rotation":[0.08352430164813995,0.04269447922706604,0.518085777759552,0.8501694202423096],"scale":[1.000000238418579,1.0000003576278687,0.9999999403953552],"translation":[1.2208448652017978e-07,0.9397414326667786,-3.409446946989192e-08]},{"children":[39],"name":"Leg2Mid.R","rotation":[0.1470656394958496,0.028868237510323524,0.4729681611061096,0.8682392835617065],"scale":[1.0000001192092896,1.0000003576278687,1.0000001192092896],"translation":[4.8437236443987786e-08,1.5058820247650146,-2.5024842642551448e-08]},{"children":[40],"name":"Leg2Upper.R","rotation":[-0.4242475926876068,0.0005238187150098383,-0.6472404599189758,0.6333194971084595],"scale":[0.9999997019767761,1,0.9999998211860657],"translation":[3.550610472302651e-09,0.8818532824516296,4.425183419698442e-08]},{"children":[41],"name":"Leg2Base.R","rotation":[-0.4972459375858307,0.7882757782936096,-0.006056289654225111,0.36239632964134216],"scale":[0.9999998211860657,1,0.9999999403953552],"translation":[-7.2600920830723226e-09,0.3915250301361084,-5.773719280455225e-09]},{"name":"Leg1Fore.R","rotation":[-0.015208502300083637,0.04422945901751518,0.4362727701663971,0.8985980749130249],"scale":[1.000000238418579,0.9999995827674866,0.9999997615814209],"translation":[-6.20622927272052e-07,0.8156638741493225,-1.6136721114889951e-07]},{"children":[43],"name":"Leg1Lower.R","rotation":[0.15885458886623383,0.17276015877723694,0.4745163321495056,0.848382830619812],"scale":[1.000000238418579,1.0000001192092896,1.0000004768371582],"translation":[-2.3015780925561558e-07,0.8788077235221863,2.258973452740065e-08]},{"children":[44],"name":"Leg1Mid.R","rotation":[0.2600231170654297,0.12465617805719376,0.4028773903846741,0.8686419725418091],"scale":[1,0.9999997019767761,0.9999999403953552],"translation":[-2.3629894485566183e-08,1.512597680091858,-5.442473494099431e-08]},{"children":[45],"name":"Leg1Upper.R","rotation":[-0.6237055063247681,-0.03962605446577072,-0.3963613212108612,0.6725466251373291],"scale":[1,1,0.9999995827674866],"translation":[4.151442212219081e-08,1.0438144207000732,6.221015524943141e-08]},{"children":[46],"name":"Leg1Base.R","rotation":[-0.5363527536392212,0.5960471630096436,0.0069372160360217094,0.5974993705749512],"scale":[1,1.0000001192092896,1.0000001192092896],"translation":[7.877114072130098e-09,0.3915250301361084,-5.523408841412447e-09]},{"children":[5,7,12,17,22,27,32,37,42,47],"name":"Body","rotation":[-0.9999927282333374,-4.546671483751652e-09,1.1920842553081457e-06,0.003814017167314887],"translation":[-2.1589291564903364e-17,0.5146726369857788,0.22900062799453735]},{"name":"Leg4IK.L","rotation":[-2.6692541510442425e-08,-2.6692541510442425e-08,-0.7071068286895752,0.7071068286895752],"translation":[2.2291481494903564,-0.5599625110626221,-0.7613579630851746]},{"name":"Leg3IK.L","rotation":[-2.6692541510442425e-08,-2.6692541510442425e-08,-0.7071068286895752,0.7071068286895752],"translation":[2.3687760829925537,-0.5599625110626221,-0.033313095569610596]},{"name":"Leg2IK.L","rotation":[-2.6692541510442425e-08,-2.6692541510442425e-08,-0.7071068286895752,0.7071068286895752],"translation":[2.3687760829925537,-0.5599625110626221,0.6964529752731323]},{"name":"Leg1IK.L","rotation":[-2.6692541510442425e-08,-2.6692541510442425e-08,-0.7071068286895752,0.7071068286895752],"translation":[2.2556710243225098,-0.5599625110626221,1.4977319240570068]},{"name":"Leg4IK.R","rotation":[-2.6692541510442425e-08,2.6692541510442425e-08,0.7071068286895752,0.7071068286895752],"translation":[-2.2291481494903564,-0.5599625110626221,-0.7613579630851746]},{"name":"Leg3IK.R","rotation":[-2.6692541510442425e-08,2.6692541510442425e-08,0.7071068286895752,0.7071068286895752],"translation":[-2.3687760829925537,-0.5599625110626221,-0.033313095569610596]},{"name":"Leg2IK.R","rotation":[-2.6692541510442425e-08,2.6692541510442425e-08,0.7071068286895752,0.7071068286895752],"translation":[-2.3687760829925537,-0.5599625110626221,0.6964529752731323]},{"name":"Leg1IK.R","rotation":[-2.6692541510442425e-08,2.6692541510442425e-08,0.7071068286895752,0.7071068286895752],"translation":[-2.2556710243225098,-0.5599625110626221,1.5977319478988647]},{"mesh":0,"name":"Spider","skin":0},{"children":[57,48,49,50,51,52,53,54,55,56],"name":"Armature"}],"animations":[{"channels":[{"sampler":0,"target":{"node":48,"path":"translation"}},{"sampler":1,"target":{"node":48,"path":"rotation"}},{"sampler":2,"target":{"node":48,"path":"scale"}},{"sampler":3,"target":{"node":4,"path":"translation"}},{"sampler":4,"target":{"node":4,"path":"rotation"}},{"sampler":5,"target":{"node":4,"path":"scale"}},{"sampler":6,"target":{"node":0,"path":"translation"}},{"sampler":7,"target":{"node":0,"path":"rotation"}},{"sampler":8,"target":{"node":0,"path":"scale"}},{"sampler":9,"target":{"node":2,"path":"translation"}},{"sampler":10,"target":{"node":2,"path":"rotation"}},{"sampler":11,"target":{"node":2,"path":"scale"}},{"sampler":12,"target":{"node":6,"path":"translation"}},{"sampler":13,"target":{"node":6,"path":"rotation"}},{"sampler":14,"target":{"node":6,"path":"scale"}},{"sampler":15,"target":{"node":11,"path":"rotation"}},{"sampler":16,"target":{"node":10,"path":"rotation"}},{"sampler":17,"target":{"node":9,"path":"rotation"}},{"sampler":18,"target":{"node":8,"path":"rotation"}},{"sampler":19,"target":{"node":16,"path":"rotation"}},{"sampler":20,"target":{"node":15,"path":"rotation"}},{"sampler":21,"target":{"node":14,"path":"rotation"}},{"sampler":22,"target":{"node":13,"path":"rotation"}},{"sampler":23,"target":{"node":21,"path":"rotation"}},{"sampler":24,"target":{"node":20,"path":"rotation"}},{"sampler":25,"target":{"node":19,"path":"rotation"}},{"sampler":26,"target":{"node":18,"path":"rotation"}},{"sampler":27,"target":{"node":26,"path":"rotation"}},{"sampler":28,"target":{"node":25,"path":"rotation"}},{"sampler":29,"target":{"node":24,"path":"rotation"}},{"sampler":30,"target":{"node":23,"path":"translation"}},{"sampler":31,"target":{"node":23,"path":"rotation"}},{"sampler":32,"target":{"node":23,"path":"scale"}},{"sampler":33,"target":{"node":31,"path":"rotation"}},{"sampler":34,"target":{"node":30,"path":"rotation"}},{"sampler":35,"target":{"node":29,"path":"rotation"}},{"sampler":36,"target":{"node":28,"path":"rotation"}},{"sampler":37,"target":{"node":36,"path":"rotation"}},{"sampler":38,"target":{"node":35,"path":"rotation"}},{"sampler":39,"target":{"node":34,"path":"rotation"}},{"sampler":40,"target":{"node":33,"path":"rotation"}},{"sampler":41,"target":{"node":41,"path":"rotation"}},{"sampler":42,"target":{"node":40,"path":"rotation"}},{"sampler":43,"target":{"node":39,"path":"rotation"}},{"sampler":44,"target":{"node":38,"path":"rotation"}},{"sampler":45,"target":{"node":46,"path":"rotation"}},{"sampler":46,"target":{"node":45,"path":"rotation"}},{"sampler":47,"target":{"node":44,"path":"rotation"}},{"sampler":48,"target":{"node":43,"path":"rotation"}},{"sampler":49,"target":{"node":49,"path":"translation"}},{"sampler":50,"target":{"node":49,"path":"rotation"}},{"sampler":51,"target":{"node":49,"path":"scale"}},{"sampler":52,"target":{"node":50,"path":"translation"}},{"sampler":53,"target":{"node":50,"path":"rotation"}},{"sampler":54,"target":{"node":50,"path":"scale"}},{"sampler":55,"target":{"node":51,"path":"translation"}},{"sampler":56,"target":{"node":51,"path":"rotation"}},{"sampler":57,"target":{"node":51,"path":"scale"}},{"sampler":58,"target":{"node":52,"path":"translation"}},{"sampler":59,"target":{"node":52,"path":"rotation"}},{"sampler":60,"target":{"node":52,"path":"scale"}},{"sampler":61,"target":{"node":53,"path":"translation"}},{"sampler":62,"target":{"node":53,"path":"rotation"}},{"sampler":63,"target":{"node":53,"path":"scale"}},{"sampler":64,"target":{"node":54,"path":"translation"}},{"sampler":65,"target":{"node":54,"path":"rotation"}},{"sampler":66,"target":{"node":54,"path":"scale"}},{"sampler":67,"target":{"node":55,"path":"translation"}},{"sampler":68,"target":{"node":55,"path":"rotation"}},{"sampler":69,"target":{"node":55,"path":"scale"}},{"sampler":70,"target":{"node":56,"path":"translation"}},{"sampler":71,"target":{"node":56,"path":"rotation"}},{"sampler":72,"target":{"node":56,"path":"scale"}}],"name":"ArmatureAction","samplers":[{"input":7,"interpolation":"LINEAR","output":8},{"input":7,"interpolation":"LINEAR","output":9},{"input":10,"interpolation":"LINEAR","output":11},{"input":10,"interpolation":"LINEAR","output":12},{"input":7,"interpolation":"LINEAR","output":13},{"input":10,"interpolation":"LINEAR","output":14},{"input":10,"interpolation":"LINEAR","output":15},{"input":7,"interpolation":"LINEAR","output":16},{"input":10,"interpolation":"LINEAR","output":17},{"input":10,"interpolation":"LINEAR","output":18},{"input":7,"interpolation":"LINEAR","output":19},{"input":10,"interpolation":"LINEAR","output":20},{"input":10,"interpolation":"LINEAR","output":21},{"input":7,"interpolation":"LINEAR","output":22},{"input":10,"interpolation":"LINEAR","output":23},{"input":7,"interpolation":"LINEAR","output":24},{"input":7,"interpolation":"LINEAR","output":25},{"input":7,"interpolation":"LINEAR","output":26},{"input":7,"interpolation":"LINEAR","output":27},{"input":7,"interpolation":"LINEAR","output":28},{"input":7,"interpolation":"LINEAR","output":29},{"input":7,"interpolation":"LINEAR","output":30},{"input":7,"interpolation":"LINEAR","output":31},{"input":7,"interpolation":"LINEAR","output":32},{"input":7,"interpolation":"LINEAR","output":33},{"input":7,"interpolation":"LINEAR","output":34},{"input":7,"interpolation":"LINEAR","output":35},{"input":7,"interpolation":"LINEAR","output":36},{"input":7,"interpolation":"LINEAR","output":37},{"input":7,"interpolation":"LINEAR","output":38},{"input":10,"interpolation":"LINEAR","output":39},{"input":7,"interpolation":"LINEAR","output":40},{"input":10,"interpolation":"LINEAR","output":41},{"input":7,"interpolation":"LINEAR","output":42},{"input":7,"interpolation":"LINEAR","output":43},{"input":7,"interpolation":"LINEAR","output":44},{"input":7,"interpolation":"LINEAR","output":45},{"input":7,"interpolation":"LINEAR","output":46},{"input":7,"interpolation":"LINEAR","output":47},{"input":7,"interpolation":"LINEAR","output":48},{"input":7,"interpolation":"LINEAR","output":49},{"input":7,"interpolation":"LINEAR","output":50},{"input":7,"interpolation":"LINEAR","output":51},{"input":7,"interpolation":"LINEAR","output":52},{"input":7,"interpolation":"LINEAR","output":53},{"input":7,"interpolation":"LINEAR","output":54},{"input":7,"interpolation":"LINEAR","output":55},{"input":7,"interpolation":"LINEAR","output":56},{"input":7,"interpolation":"LINEAR","output":57},{"input":7,"interpolation":"LINEAR","output":58},{"input":10,"interpolation":"LINEAR","output":59},{"input":7,"interpolation":"LINEAR","output":60},{"input":7,"interpolation":"LINEAR","output":61},{"input":10,"interpolation":"LINEAR","output":62},{"input":7,"interpolation":"LINEAR","output":63},{"input":7,"interpolation":"LINEAR","output":64},{"input":10,"interpolation":"LINEAR","output":65},{"input":7,"interpolation":"LINEAR","output":66},{"input":7,"interpolation":"LINEAR","output":67},{"input":10,"interpolation":"LINEAR","output":68},{"input":7,"interpolation":"LINEAR","output":69},{"input":7,"interpolation":"LINEAR","output":70},{"input":10,"interpolation":"LINEAR","output":71},{"input":7,"interpolation":"LINEAR","output":72},{"input":7,"interpolation":"LINEAR","output":73},{"input":10,"interpolation":"LINEAR","output":74},{"input":7,"interpolation":"LINEAR","output":75},{"input":7,"interpolation":"LINEAR","output":76},{"input":10,"interpolation":"LINEAR","output":77},{"input":7,"interpolation":"LINEAR","output":78},{"input":7,"interpolation":"LINEAR","output":79},{"input":10,"interpolation":"LINEAR","output":80},{"input":7,"interpolation":"LINEAR","output":81}]}],"materials":[{"doubleSided":true,"name":"Material.001","pbrMetallicRoughness":{}}],"meshes":[{"name":"Cube","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2,"JOINTS_0":3,"WEIGHTS_0":4},"indices":5,"material":0}]}],"skins":[{"inverseBindMatrices":6,"joints":[48,5,4,1,0,3,2,7,6,12,11,10,9,8,17,16,15,14,13,22,21,20,19,18,27,26,25,24,23,32,31,30,29,28,37,36,35,34,33,42,41,40,39,38,47,46,45,44,43,49,50,51,52,53,54,55,56],"name":"Armature"}],"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"max":[2.742279291152954,1.4045029878616333,2.0192716121673584],"min":[-2.742279291152954,-0.6434623599052429,-3.534085512161255],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC2"},{"bufferView":3,"componentType":5121,"count":1000,"type":"VEC4"},{"bufferView":4,"componentType":5126,"count":1000,"type":"VEC4"},{"bufferView":5,"componentType":5123,"count":1500,"type":"SCALAR"},{"bufferView":6,"componentType":5126,"count":57,"type":"MAT4"},{"bufferView":7,"componentType":5126,"count":120,"max":[5],"min":[0.041666666666666664],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":10,"componentType":5126,"count":2,"max":[5],"min":[0.041666666666666664],"type":"SCALAR"},{"bufferView":11,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":14,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":15,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":16,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":17,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":18,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":19,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":20,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":21,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":22,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":23,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":24,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":25,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":26,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":27,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":28,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":29,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":30,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":31,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":32,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":33,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":34,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":35,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":36,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":37,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":38,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":39,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":40,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":41,"componentType":5126,"count":2,"type":"VEC3"},{"bufferView":42,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":43,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":44,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":45,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":46,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":47,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":48,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":49,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":50,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":51,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":52,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":53,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":54,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":55,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":56,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":57,"componentType":5126,"count":120,"type":"VEC4"},{"bufferView":58,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":59,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":60,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":61,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":62,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":63,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":64,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":65,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":66,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":67,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":68,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":69,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":70,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":71,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":72,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":73,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":74,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":75,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":76,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":77,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":78,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":79,"componentType":5126,"count":120,"type":"VEC3"},{"bufferView":80,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":81,"componentType":5126,"count":120,"type":"VEC3"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":12000,"byteOffset":12000},{"buffer":0,"byteLength":8000,"byteOffset":24000},{"buffer":0,"byteLength":4000,"byteOffset":32000},{"buffer":0,"byteLength":16000,"byteOffset":36000},{"buffer":0,"byteLength":3000,"byteOffset":52000},{"buffer":0,"byteLength":3648,"byteOffset":55000},{"buffer":0,"byteLength":480,"byteOffset":58648},{"buffer":0,"byteLength":1440,"byteOffset":59128},{"buffer":0,"byteLength":1920,"byteOffset":60568},{"buffer":0,"byteLength":8,"byteOffset":62488},{"buffer":0,"byteLength":24,"byteOffset":62496},{"buffer":0,"byteLength":24,"byteOffset":62520},{"buffer":0,"byteLength":1920,"byteOffset":62544},{"buffer":0,"byteLength":24,"byteOffset":64464},{"buffer":0,"byteLength":24,"byteOffset":64488},{"buffer":0,"byteLength":1920,"byteOffset":64512},{"buffer":0,"byteLength":24,"byteOffset":66432},{"buffer":0,"byteLength":24,"byteOffset":66456},{"buffer":0,"byteLength":1920,"byteOffset":66480},{"buffer":0,"byteLength":24,"byteOffset":68400},{"buffer":0,"byteLength":24,"byteOffset":68424},{"buffer":0,"byteLength":1920,"byteOffset":68448},{"buffer":0,"byteLength":24,"byteOffset":70368},{"buffer":0,"byteLength":1920,"byteOffset":70392},{"buffer":0,"byteLength":1920,"byteOffset":72312},{"buffer":0,"byteLength":1920,"byteOffset":74232},{"buffer":0,"byteLength":1920,"byteOffset":76152},{"buffer":0,"byteLength":1920,"byteOffset":78072},{"buffer":0,"byteLength":1920,"byteOffset":79992},{"buffer":0,"byteLength":1920,"byteOffset":81912},{"buffer":0,"byteLength":1920,"byteOffset":83832},{"buffer":0,"byteLength":1920,"byteOffset":85752},{"buffer":0,"byteLength":1920,"byteOffset":87672},{"buffer":0,"byteLength":1920,"byteOffset":89592},{"buffer":0,"byteLength":1920,"byteOffset":91512},{"buffer":0,"byteLength":1920,"byteOffset":93432},{"buffer":0,"byteLength":1920,"byteOffset":95352},{"buffer":0,"byteLength":1920,"byteOffset":97272},{"buffer":0,"byteLength":24,"byteOffset":99192},{"buffer":0,"byteLength":1920,"byteOffset":99216},{"buffer":0,"byteLength":24,"byteOffset":101136},{"buffer":0,"byteLength":1920,"byteOffset":101160},{"buffer":0,"byteLength":1920,"byteOffset":103080},{"buffer":0,"byteLength":1920,"byteOffset":105000},{"buffer":0,"byteLength":1920,"byteOffset":106920},{"buffer":0,"byteLength":1920,"byteOffset":108840},{"buffer":0,"byteLength":1920,"byteOffset":110760},{"buffer":0,"byteLength":1920,"byteOffset":112680},{"buffer":0,"byteLength":1920,"byteOffset":114600},{"buffer":0,"byteLength":1920,"byteOffset":116520},{"buffer":0,"byteLength":1920,"byteOffset":118440},{"buffer":0,"byteLength":1920,"byteOffset":120360},{"buffer":0,"byteLength":1920,"byteOffset":122280},{"buffer":0,"byteLength":1920,"byteOffset":124200},{"buffer":0,"byteLength":1920,"byteOffset":126120},{"buffer":0,"byteLength":1920,"byteOffset":128040},{"buffer":0,"byteLength":1920,"byteOffset":129960},{"buffer":0,"byteLength":1440,"byteOffset":131880},{"buffer":0,"byteLength":32,"byteOffset":133320},{"buffer":0,"byteLength":1440,"byteOffset":133352},{"buffer":0,"byteLength":1440,"byteOffset":134792},{"buffer":0,"byteLength":32,"byteOffset":136232},{"buffer":0,"byteLength":1440,"byteOffset":136264},{"buffer":0,"byteLength":1440,"byteOffset":137704},{"buffer":0,"byteLength":32,"byteOffset":139144},{"buffer":0,"byteLength":1440,"byteOffset":139176},{"buffer":0,"byteLength":1440,"byteOffset":140616},{"buffer":0,"byteLength":32,"byteOffset":142056},{"buffer":0,"byteLength":1440,"byteOffset":142088},{"buffer":0,"byteLength":1440,"byteOffset":143528},{"buffer":0,"byteLength":32,"byteOffset":144968},{"buffer":0,"byteLength":1440,"byteOffset":145000},{"buffer":0,"byteLength":1440,"byteOffset":146440},{"buffer":0,"byteLength":32,"byteOffset":147880},{"buffer":0,"byteLength":1440,"byteOffset":147912},{"buffer":0,"byteLength":1440,"byteOffset":149352},{"buffer":0,"byteLength":32,"byteOffset":150792},{"buffer":0,"byteLength":1440,"byteOffset":150824},{"buffer":0,"byteLength":1440,"byteOffset":152264},{"buffer":0,"byteLength":32,"byteOffset":153704},{"buffer":0,"byteLength":1440,"byteOffset":153736}],"buffers":[{"byteLength":155176,"uri":"data:application/octet-stream;base64,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BgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALgAAAC4AAAAuAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAALwAAAC8AAAAvAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAKwAAACsAAAArAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAEADgAUAAEAFAAHAAoABgATAAoAEwAXABUAEgAMABUADAAPABAAAwAJABAACQAWAAUAAgAIAAUACAALABEADQAAABEAAAAEAEwAUgAsAEwALAAfACIAHgArACIAKwAvAEgAUAAkAEgAJAAnAEsAVQAhAEsAIQAuAFYATgAgAFYAIAAjACkAJQAYACkAGAAcADcAQgA8ADcAPAAxAD0APwA0AD0ANAAyAEYAOwA1AEYANQBAADAAMwA5ADAAOQA2AEQARwBBAEQAQQA+ADgAOgBFADgARQBDAB0AGgBPAB0ATwBXACgAGwBUACgAVABKAC0AKgBRAC0AUQBJABkAJgBTABkAUwBNAFgAWwBhAFgAYQBeAF8AYgBtAF8AbQBqAGwAbwBpAGwAaQBmAGUAaABdAGUAXQBaAGAAawBkAGAAZABZAG4AYwBcAG4AXABnAHAAcwB5AHAAeQB2AHgAegCFAHgAhQCDAIQAhwCBAIQAgQB+AH0AfwB0AH0AdAByAHcAggB8AHcAfABxAIYAewB1AIYAdQCAAIgAiwCRAIgAkQCOAJAAkgCdAJAAnQCbAJwAnwCZAJwAmQCWAJUAlwCMAJUAjACKAI8AmgCUAI8AlACJAJ4AkwCNAJ4AjQCYAKIAqACrAKIAqwClAKcAtAC3AKcAtwCqALIArACvALIArwC1AK0AoACjAK0AowCwAKYAoQCuAKYArgCzALYAsQCkALYApACpALoAwADDALoAwwC9AL8AzADOAL8AzgDBAMoAxADHAMoAxwDNAMYAuQC7AMYAuwDIAL4AuADFAL4AxQDLAM8AyQC8AM8AvADCANIA2ADbANIA2wDVANcA5ADmANcA5gDZAOIA3ADfAOIA3wDlAN4A0QDTAN4A0wDgANYA0ADdANYA3QDjAOcA4QDUAOcA1ADaAOkA6wDxAOkA8QDvAPAA8gD9APAA/QD7APwA/gD4APwA+AD2APUA9wDsAPUA7ADqAO4A+gD0AO4A9ADoAP8A8wDtAP8A7QD5AAABAwEJAQABCQEGAQcBCgEVAQcBFQESARQBFwERARQBEQEOAQ0BEAEFAQ0BBQECAQgBEwEMAQgBDAEBARYBCwEEARYBBAEPASkBHQEjASkBIwEvASgBJgEZASgBGQEbAS0BKwElAS0BJQEnASEBHwEsASEBLAEuARgBJAEqARgBKgEeARwBGgEgARwBIAEiATEBMwE6ATEBOgE4ATcBOQFFATcBRQFDAUQBRgE/AUQBPwE9AT4BQAE0AT4BNAEyATYBQgE8ATYBPAEwAUcBOwE1AUcBNQFBAUkBSwFRAUkBUQFPAVABUgFdAVABXQFbAVwBXgFYAVwBWAFWAVUBVwFMAVUBTAFKAU4BWgFUAU4BVAFIAV8BUwFNAV8BTQFZAWABYwFpAWABaQFmAWgBagF1AWgBdQFzAXQBdwFxAXQBcQFuAW0BbwFkAW0BZAFiAWcBcgFsAWcBbAFhAXYBawFlAXYBZQFwAYkBfQGDAYkBgwGPAYgBhgF5AYgBeQF7AY0BiwGFAY0BhQGHAYEBfwGMAYEBjAGOAXgBhAGKAXgBigF+AXwBegGAAXwBgAGCAZEBkwGZAZEBmQGXAZgBmgGlAZgBpQGjAaQBpgGgAaQBoAGeAZ0BnwGUAZ0BlAGSAZYBogGcAZYBnAGQAacBmwGVAacBlQGhAakBqwGxAakBsQGvAbABsgG9AbABvQG7AbwBvgG4AbwBuAG2AbUBtwGsAbUBrAGqAa4BugG0Aa4BtAGoAb8BswGtAb8BrQG5AcABwwHJAcAByQHGAcgBygHVAcgB1QHTAdQB1wHRAdQB0QHOAc0BzwHEAc0BxAHCAccB0gHMAccBzAHBAdYBywHFAdYBxQHQAekB3QHjAekB4wHvAegB5gHZAegB2QHbAe0B6wHlAe0B5QHnAeEB3wHsAeEB7AHuAdgB5AHqAdgB6gHeAdwB2gHgAdwB4AHiAfEB8wH5AfEB+QH3AfgB+gEFAvgBBQIDAgQCBgIAAgQCAAL+Af0B/wH0Af0B9AHyAfYBAgL8AfYB/AHwAQcC+wH1AQcC9QEBAgkCCwIRAgkCEQIPAhACEgIdAhACHQIbAhwCHgIYAhwCGAIWAhUCFwIMAhUCDAIKAg4CGgIUAg4CFAIIAh8CEwINAh8CDQIZAiACIwIpAiACKQImAigCKwI2AigCNgIzAjQCNwIxAjQCMQIuAiwCLwIkAiwCJAIhAicCMgItAicCLQIiAjUCKgIlAjUCJQIwAkkCPQJDAkkCQwJPAkgCRgI5AkgCOQI7Ak0CSwJFAk0CRQJHAkECPwJMAkECTAJOAjgCRAJKAjgCSgI+AjwCOgJAAjwCQAJCAlICVAJZAlICWQJXAlgCWgJmAlgCZgJkAmMCZQJgAmMCYAJeAl0CXwJTAl0CUwJRAlYCYgJcAlYCXAJQAmcCWwJVAmcCVQJhAmkCbwJzAmkCcwJtAm4CewJ/Am4CfwJyAnoCdAJ4AnoCeAJ+AnUCaAJsAnUCbAJ5AnACagJ2AnACdgJ8An0CdwJrAn0CawJxAoICiAKLAoICiwKFAocClAKXAocClwKKApICjAKPApICjwKVAo0CgAKDAo0CgwKQAoYCgQKOAoYCjgKTApYCkQKEApYChAKJAqcCrQKhAqcCoQKbAqgCnQKZAqgCmQKkAq8CqQKlAq8CpQKrAqMCrgKqAqMCqgKfApoCoAKsApoCrAKmApwCogKeApwCngKYArECtgK6ArECugK1ArcCwwLHArcCxwK7AsICvQLBAsICwQLGArwCsAK0ArwCtALAArgCsgK+ArgCvgLEAsUCvwKzAsUCswK5AskCzwLTAskC0wLNAs4C2wLfAs4C3wLSAtoC1ALYAtoC2ALeAtUCyALMAtUCzALZAtACygLWAtAC1gLcAt0C1wLLAt0CywLRAuIC6ALrAuIC6wLlAuYC8wL3AuYC9wLqAvIC7ALvAvIC7wL1Au0C4ALkAu0C5ALxAucC4QLuAucC7gL0AvYC8ALjAvYC4wLpAgcDDQMBAwcDAQP7AggD/QL5AggD+QIEAw8DCQMFAw8DBQMLAwMDDgMKAwMDCgP/AvoCAAMMA/oCDAMGA/wCAgP+AvwC/gL4AhEDFwMbAxEDGwMVAxYDIwMnAxYDJwMaAyIDHAMgAyIDIAMmAx0DEAMUAx0DFAMhAxgDEgMeAxgDHgMkAyUDHwMTAyUDEwMZAykDLwMzAykDMwMtAy4DOwM/Ay4DPwMyAzoDNAM4AzoDOAM+AzUDKAMsAzUDLAM5AzADKgM2AzADNgM8Az0DNwMrAz0DKwMxA0IDSANLA0IDSwNFA0YDUwNXA0YDVwNKA1IDTANPA1IDTwNVA00DQANEA00DRANRA0cDQQNOA0cDTgNUA1YDUANDA1YDQwNJA2cDbQNhA2cDYQNbA2gDXQNZA2gDWQNkA28DaQNlA28DZQNrA2MDbgNqA2MDagNfA1oDYANsA1oDbANmA1wDYgNeA1wDXgNYA3EDdwN7A3EDewN1A3YDgwOHA3YDhwN6A4IDfAOAA4IDgAOGA30DcAN0A30DdAOBA3gDcgN+A3gDfgOEA4UDfwNzA4UDcwN5A4kDjwOTA4kDkwONA44DmwOfA44DnwOSA5oDlAOYA5oDmAOeA5UDiAOMA5UDjAOZA5ADigOWA5ADlgOcA50DlwOLA50DiwORA6IDqAOrA6IDqwOlA6YDswO2A6YDtgOpA7IDrAOvA7IDrwO1A64DoQOkA64DpAOxA6cDoAOtA6cDrQO0A7cDsAOjA7cDowOqA8cDzQPBA8cDwQO7A8gDvQO5A8gDuQPEA88DyQPFA88DxQPLA8MDzgPKA8MD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TmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+cw9Rrv9jabWlCWo/0XvPPt5waq/JcWm1pQlqP9F7zz54BNKqTmtptaUJaj/Re88+jT7qr0Z4abWlCWo/0XvPPhnLKDCae2m1pwlqP9F7zz7t5U+vDU5ptaUJaj/Se88+goncLxOCabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4MKI+vempptaUJaj/Re88+WtPpL1ZeabWlCWo/0XvPPgDKKDCYe2m1pQlqP9F7zz5ssc+vzDBptaUJaj/Re88+NwGPsPxnabWlCWo/0XvPPuaqzi7ueWm1pQlqP9F7zz5TyIEve2hptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1pQlqP9F7zz4ZhMCqTmtptaUJaj/Re88+GYTAqk5rabWlCWo/0XvPPhmEwKpOa2m1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4G1iP+fAELztE0K9GXDnvvwEZD/Ubm27I9lMvcau3r5aKWY/BMwvuVYXVL21b9a+XhZoPyUpjjptxVa98bnSvvXsaD+J6Z652tVTvVoT1L5EoWg/nweCu5kqTL01Zde+DeRnP8KSFrxBaEG9XIfbvjjyZj9jLXm8ASo1vSVf376GCmY/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/eHyevLDKLL0feOG+pYhlP3h8nrywyiy9H3jhvqWIZT94fJ68sMosvR944b6liGU/fRmbvAekLb0AjOC+bMJlP2ROkryp1y+9SxjevoVaZj/IVIa8Gdcyvf2L2r7UMmc/zpJyvNAdNr0XVta+0i1oP13HWbzWQjm9ePDRvm0taT9Ir0S8M/07vd3jzb4ME2o/FLM0vFQhPr37xMq+N8BqP2DQK7zTXz+9SjDJvpwWaz+PoyK8iLNAvd5Oyr5r2Go/nfUQvDs+Q72B+M2+7gpqP/l78Lvg/ka9nPTRvpklaT8LXL27jU5LvQiK077sxmg/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4gzaP7VZD7+1Com/8SHGP7VZD7+1Com/SEyzP7VZD7+1Com/wkSiP7VZD7+1Com/OsSTP7VZD7+1Com/joOIP7VZD7+1Com/mDuBP7VZD7+1Com/Ykp9P7VZD7+1Com/Ykp9P7VZD7+1Com/Ykp9P7VZD7+1Com/hUnlsoVJ5bL0BDW/9AQ1P4VJ5bKFSeWy9AQ1v/QENT8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP5IMgT+SDIE/AACAP5QDhD+UA4Q/AACAP/SeiD/0nog/AACAP6OYjj+jmI4/AACAP5KqlT+SqpU/AACAP7GOnT+xjp0/AACAP/D+pT/w/qU/AACAPz61rj8+ta4/AACAP4xrtz+Ma7c/AACAP8zbvz/M278/AACAP+q/xz/qv8c/AACAP9rRzj/a0c4/AACAP4rL1D+Ky9Q/AACAP+pm2T/qZtk/AACAP+ld3D/pXdw/AACAP3xq3T98at0/AACAP8BZ2z98at0/AACAP+iJ1T98at0/AACAP4SOzD98at0/AACAPyH7wD98at0/AACAP05jsz98at0/AACAP5hapD98at0/AACAP4x0lD98at0/AACAP7pEhD98at0/AACAP1+9aD98at0/AACAP/KrSj98at0/AACAP018Lz98at0/AACAP4ZVGD98at0/AACAP7xeBj98at0/AACAPyB+9T58at0/AACAPy077T58at0/AACAPy077T58at0/AACAPy077T58at0/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utbk/6lwQQLRZD797gqw/6lwQQLRZD79IT58/6lwQQLRZD79IT5k/6lwQQGaz/r6utZ0/6lwQQABNuL5IT6k/6lwQQDjNY76utbk/6lwQQDXNI757gsw/6lwQQDfNY75IT98/6lwQQAFNuL6tte8/6lwQQGez/r5HT/s/6lwQQLRZD7+utf8/6lwQQLNZD79HT/s/6lwQQLRZD7+ste8/6lwQQLRZD79GT98/6lwQQLRZD796gsw/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/6lwQQLRZD7+utb8/x1UQQLRZD78EhME/0yMQQLRZD7/oRsY/PJwPQLRZD78pAs0/MpQOQLRZD7+ZudQ/4+AMQLRZD78Jcdw/fVcKQLRZD79KLOM/MM0GQLRZD78t7+c/KhcCQLRZD7+Dvek/uef0P7RZD790D+c/YGTeP7RZD7+//t8/DoDDP7RZD7+cAdY/ghanP7RZD79Djso/ggOMP7RZD7/qGr8/mEVqP7VZD7/IHbU/TKBKP7VZD78SDa4/pM4+P7RZD78DX6s/lpVQP7RZD78DX6s/8AZ9P7RZD78DX6s/wGabP7RZD78DX6s/CEq4P7RZD78DX6s/toLOP7RZD78DX6s/LmbXP7RZD78DX6s/VszRP7RZD78DX6s/osnCP7RZD78DX6s/UQ6tP7RZD78DX6s/okqTP7RZD78EX6s/qF1wP7RZD78EX6s/StY8P7RZD78EX6s/qF8RP7RZD78EX6s/hLTmPrRZD78EX6s/IE3QPrRZD78EX6s/IE3QPrRZD78EX6s/IE3QPrRZD78EX6s/hUnlsoVJ5bL0BDW/9AQ1P4VJ5bKFSeWy9AQ1v/QENT8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/ViaAP1YmgD8AAIA/V5eAP1eXgD8AAIA/AlCBPwJQgT8AAIA/VU2CP1VNgj8AAIA/T4yDP0+Mgz8AAIA/7wmFP+8JhT8AAIA/NMOGPzTDhj8AAIA/G7WIPxu1iD8AAIA/pdyKP6Xcij8AAIA/zzaNP882jT8AAIA/mcCPP5nAjz8AAIA/AHeSPwB3kj8AAIA/A1eVPwNXlT8AAIA/o12YP6NdmD8AAIA/3IebP9yHmz8AAIA/rtKeP67Snj8AAIA/FjuiPxY7oj8AAIA/Fr6lPxa+pT8AAIA/qlipP6pYqT8AAIA/0QetP9EHrT8AAIA/i8iwP4vIsD8AAIA/1Ze0P9WXtD8AAIA/rnK4P65yuD8AAIA/F1a8PxdWvD8AAIA/Cz/APws/wD8AAIA/iyrEP4sqxD8AAIA/lhXIP5YVyD8AAIA/Kv3LPyr9yz8AAIA/Rd7PP0Xezz8AAIA/57XTP+e10z8AAIA/DYHXPw2B1z8AAIA/tzzbP7c82z8AAIA/4+XeP+Pl3j8AAIA/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tFkPvxnErL4gwuK9tFkPvxnErL6ACWC9tFkPvxnErL5AFw69tFkPvxnErL5AFw69tFkPvxnErL5AFw69tFkPvxnErL6FSeWyhUnlMvQENT/0BDU/hUnlsoVJ5TL0BDU/9AQ1PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD9vNoA/bzaAPwAAgD851oA/OdaAPwAAgD8Z2oE/GdqBPwAAgD/KPIM/yjyDPwAAgD8I+YQ/CPmEPwAAgD+QCYc/kAmHPwAAgD8caYk/HGmJPwAAgD9nEow/ZxKMPwAAgD8tAI8/LQCPPwAAgD8pLZI/KS2SPwAAgD8XlJU/F5SVPwAAgD+zL5k/sy+ZPwAAgD+3+pw/t/qcPwAAgD/g76A/4O+gPwAAgD/oCaU/6AmlPwAAgD+MQ6k/jEOpPwAAgD+Gl60/hpetPwAAgD+TALI/kwCyPwAAgD9tebY/bXm2PwAAgD/R/Lo/0fy6PwAAgD95hb8/eYW/PwAAgD8hDsQ/IQ7EPwAAgD+Ekcg/hJHIPwAAgD9eCs0/XgrNPwAAgD9qc9E/anPRPwAAgD9mx9U/ZsfVPwAAgD8KAdo/CgHaPwAAgD8QG94/EBvePwAAgD86EOI/OhDiPwAAgD8/2+U/P9vlPwAAgD/Zduk/2XbpPwAAgD/I3ew/yN3sPwAAgD/FCvA/xQrwPwAAgD+L+PI/i/jyPwAAgD/VofU/1aH1PwAAgD9hAfg/YQH4PwAAgD/pEfo/6RH6PwAAgD8mzvs/Js77PwAAgD/YMP0/2DD9PwAAgD+4NP4/uDT+PwAAgD+D1P4/g9T+PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD/xCv8/8Qr/PwAAgD+Wsfo/8Qr/PwAAgD/YxO4/8Qr/PwAAgD+y89w/8Qr/PwAAgD8c7cY/8Qr/PwAAgD8OYK4/8Qr/PwAAgD+F+5Q/8Qr/PwAAgD/v3Hg/8Qr/PwAAgD/Az0w/8Qr/PwAAgD9wLSk/8Qr/PwAAgD/2UxE/8Qr/PwAAgD9CoQg/8Qr/PwAAgD9CoQg/8Qr/PwAAgD9CoQg/8Qr/PweaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8A2zSO+UHMIvQeaF8A4zWO+FTNePAeaF8ACTbi+kvnuPQeaF8Bns/6+XhZhPgeaF8C0WQ+/MIuIPgeaF8C0WQ+/kkl6PgeaF8C0WQ+/+K86PgeaF8C0WQ+/jPmyPQeaF8C0WQ+/UHMIvQeaF8C0WQ+/brYtvgeaF8C0WQ+/aA6ZvgeaF8CzWQ+/znTKvgeaF8C0WQ+/N9vdvgeaF8Bms/6+Ntu9vgeaF8AATbi+oeluvgeaF8A2zWO+qznEvQeaF8A2zSO+UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvQeaF8C0WQ+/UHMIvSVLF8C0WQ+/gVX5vDFiFsC0WQ+/Xbu2vLDkFMC0WQ+/HHwevCvYEsC0WQ+/MPLbOypCEMC0WQ+/mM7VPDMoDcC0WQ+/HolDPc+PCcC0WQ+/ZiiRPYZ+BcC0WQ+/tBXCPd35AMC0WQ+/AAPzPb0O+L+0WQ+/bDMRPiBZ7b+0WQ+/4lsnPvTd4b+0WQ+/JDY7Pkmo1b+0WQ+/dv1LPizDyL+0WQ+/Hu1YPq85u7+0WQ+/ZEBhPt8Wrb+0WQ+/iDJkPpRsnr+0WQ+/iDJkPs9nj7+0WQ+/iDJkPlk8gL+0WQ+/iDJkPvI7Yr+0WQ+/iDJkPvCARL+0WQ+/iDJkPkCvJ7+0WQ+/iDJkPnYuDL+0WQ+/iDJkPjjM5L60WQ+/iDJkPpB7tb60WQ+/iDJkPig6i760WQ+/iDJkPvCtTb60WQ+/iDJkPpBCEr60WQ+/iDJkPuCgy720WQ+/iDJkPmDrk720WQ+/iDJkPqChgL20WQ+/iDJkPqChgL20WQ+/iDJkPqChgL20WQ+/iDJkPoVJ5bKFSeUy9AQ1P/QENT+FSeWyhUnlMvQENT/0BDU/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP5IMgT+SDIE/AACAP5QDhD+UA4Q/AACAP/SeiD/0nog/AACAP6OYjj+jmI4/AACAP5KqlT+SqpU/AACAP7GOnT+xjp0/AACAP/D+pT/w/qU/AACAPz61rj8+ta4/AACAP4xrtz+Ma7c/AACAP8zbvz/M278/AACAP+q/xz/qv8c/AACAP9rRzj/a0c4/AACAP4rL1D+Ky9Q/AACAP+pm2T/qZtk/AACAP+ld3D/pXdw/AACAP3xq3T98at0/AACAP8BZ2z98at0/AACAP+iJ1T98at0/AACAP4SOzD98at0/AACAPyH7wD98at0/AACAP05jsz98at0/AACAP5hapD98at0/AACAP4x0lD98at0/AACAP7pEhD98at0/AACAP1+9aD98at0/AACAP/KrSj98at0/AACAP018Lz98at0/AACAP4ZVGD98at0/AACAP7xeBj98at0/AACAPyB+9T58at0/AACAPy077T58at0/AACAPy077T58at0/AACAPy077T58at0/B5oXwLRZD7++SjI/B5oXwLRZD7++SjI/B5oXwLRZD7++SjI/B5oXwLRZD7++SjI/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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/ViaAP1YmgD8AAIA/V5eAP1eXgD8AAIA/AlCBPwJQgT8AAIA/VU2CP1VNgj8AAIA/T4yDP0+Mgz8AAIA/7wmFP+8JhT8AAIA/NMOGPzTDhj8AAIA/G7WIPxu1iD8AAIA/pdyKP6Xcij8AAIA/zzaNP882jT8AAIA/mcCPP5nAjz8AAIA/AHeSPwB3kj8AAIA/A1eVPwNXlT8AAIA/o12YP6NdmD8AAIA/3IebP9yHmz8AAIA/rtKeP67Snj8AAIA/FjuiPxY7oj8AAIA/Fr6lPxa+pT8AAIA/qlipP6pYqT8AAIA/0QetP9EHrT8AAIA/i8iwP4vIsD8AAIA/1Ze0P9WXtD8AAIA/rnK4P65yuD8AAIA/F1a8PxdWvD8AAIA/Cz/APws/wD8AAIA/iyrEP4sqxD8AAIA/lhXIP5YVyD8AAIA/Kv3LPyr9yz8AAIA/Rd7PP0Xezz8AAIA/57XTP+e10z8AAIA/DYHXPw2B1z8AAIA/tzzbP7c82z8AAIA/4+XeP+Pl3j8AAIA/kXniP5F54j8AAIA/vvTlP7705T8AAIA/aFTpP2hU6T8AAIA/lJXsP5SV7D8AAIA/OLXvPzi17z8AAIA/VrDyP1aw8j8AAIA/7oP1P+6D9T8AAIA//yz4P/8s+D8AAIA/hqj6P4ao+j8AAIA/gvP8P4Lz/D8AAIA/8Qr/P/EK/z8AAIA/+nUAQPp1AEAAAIA/E0oBQBNKAUAAAIA/pAACQKQAAkAAAIA/jJgCQIyYAkAAAIA/qBADQKgQA0AAAIA/2GcDQNhnA0AAAIA//ZwDQP2cA0AAAIA/9a4DQPWuA0AAAIA/9a4DQPWuA0AAAIA/9a4DQPWuA0AAAIA/9a4DQPWuA0AAAIA/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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD+SDIE/kgyBPwAAgD+UA4Q/lAOEPwAAgD/0nog/9J6IPwAAgD+jmI4/o5iOPwAAgD+SqpU/kqqVPwAAgD+xjp0/sY6dPwAAgD/w/qU/8P6lPwAAgD8+ta4/PrWuPwAAgD+Ma7c/jGu3PwAAgD/M278/zNu/PwAAgD/qv8c/6r/HPwAAgD/a0c4/2tHOPwAAgD+Ky9Q/isvUPwAAgD/qZtk/6mbZPwAAgD/pXdw/6V3cPwAAgD98at0/fGrdPwAAgD/AWds/fGrdPwAAgD/oidU/fGrdPwAAgD+Ejsw/fGrdPwAAgD8h+8A/fGrdPwAAgD9OY7M/fGrdPwAAgD+YWqQ/fGrdPwAAgD+MdJQ/fGrdPwAAgD+6RIQ/fGrdPwAAgD9fvWg/fGrdPwAAgD/yq0o/fGrdPwAAgD9NfC8/fGrdPwAAgD+GVRg/fGrdPwAAgD+8XgY/fGrdPwAAgD8gfvU+fGrdPwAAgD8tO+0+fGrdPwAAgD8tO+0+fGrdPwAAgD8tO+0+fGrdPw=="}]} diff --git a/data/games/garage/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf b/data/games/garage/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf new file mode 100644 index 0000000..feddfbb --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAA=","byteLength":80}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":6,"target":34963},{"buffer":0,"byteOffset":8,"byteLength":72,"byteStride":24,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":3,"type":"SCALAR","max":[2],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/models/gltf_triangle_without_indices.gltf b/data/games/garage/mods/gltf/models/gltf_triangle_without_indices.gltf new file mode 100644 index 0000000..e91cc0e --- /dev/null +++ b/data/games/garage/mods/gltf/models/gltf_triangle_without_indices.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0}}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA","byteLength":36}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/data/games/garage/mods/gltf/textures/gltf_cube.png b/data/games/garage/mods/gltf/textures/gltf_cube.png new file mode 100644 index 0000000..1d01910 Binary files /dev/null and b/data/games/garage/mods/gltf/textures/gltf_cube.png differ diff --git a/data/games/garage/mods/gltf/textures/gltf_frog.png b/data/games/garage/mods/gltf/textures/gltf_frog.png new file mode 100644 index 0000000..552ae36 Binary files /dev/null and b/data/games/garage/mods/gltf/textures/gltf_frog.png differ diff --git a/data/games/garage/mods/gltf/textures/gltf_snow_man.png b/data/games/garage/mods/gltf/textures/gltf_snow_man.png new file mode 100644 index 0000000..7f27843 Binary files /dev/null and b/data/games/garage/mods/gltf/textures/gltf_snow_man.png differ diff --git a/data/games/garage/mods/gltf/textures/gltf_spider.png b/data/games/garage/mods/gltf/textures/gltf_spider.png new file mode 100644 index 0000000..1e3d3ae Binary files /dev/null and b/data/games/garage/mods/gltf/textures/gltf_spider.png differ diff --git a/data/games/garage/mods/initial_message/init.lua b/data/games/garage/mods/initial_message/init.lua new file mode 100644 index 0000000..255dca0 --- /dev/null +++ b/data/games/garage/mods/initial_message/init.lua @@ -0,0 +1,9 @@ +core.register_on_joinplayer(function(player) + local cb = function(player) + if not player or not player:is_player() then + return + end + core.chat_send_player(player:get_player_name(), "This is the \"Development Test\" [devtest], meant only for testing and development.") + end + core.after(2.0, cb, player) +end) diff --git a/data/games/garage/mods/initial_message/mod.conf b/data/games/garage/mods/initial_message/mod.conf new file mode 100644 index 0000000..32aa2ac --- /dev/null +++ b/data/games/garage/mods/initial_message/mod.conf @@ -0,0 +1,2 @@ +name = initial_message +description = Show message to joining players explaining what this testing game is about diff --git a/data/games/garage/mods/last_mod/init.lua b/data/games/garage/mods/last_mod/init.lua new file mode 100644 index 0000000..3909a39 --- /dev/null +++ b/data/games/garage/mods/last_mod/init.lua @@ -0,0 +1 @@ +-- Nothing to do here, loading order is tested in C++ unittests. diff --git a/data/games/garage/mods/last_mod/mod.conf b/data/games/garage/mods/last_mod/mod.conf new file mode 100644 index 0000000..734bf4c --- /dev/null +++ b/data/games/garage/mods/last_mod/mod.conf @@ -0,0 +1,5 @@ +name = last_mod +description = Mod which should be loaded as last mod. +# Test dependencies +optional_depends = unittests +depends = first_mod diff --git a/data/games/garage/mods/lighting/init.lua b/data/games/garage/mods/lighting/init.lua new file mode 100644 index 0000000..1251ef1 --- /dev/null +++ b/data/games/garage/mods/lighting/init.lua @@ -0,0 +1,156 @@ +local lighting_sections = { + {n = "shadows", d = "Shadows", + entries = { + { n = "intensity", d = "Shadow Intensity", min = 0, max = 1 } + } + }, + { + n = "exposure", d = "Exposure", + entries = { + {n = "luminance_min", d = "Minimum Luminance", min = -10, max = 10}, + {n = "luminance_max", d = "Maximum Luminance", min = -10, max = 10}, + {n = "exposure_correction", d = "Exposure Correction", min = -10, max = 10}, + {n = "speed_dark_bright", d = "Bright light adaptation speed", min = -10, max = 10, type="log2"}, + {n = "speed_bright_dark", d = "Dark scene adaptation speed", min = -10, max = 10, type="log2"}, + {n = "center_weight_power", d = "Power factor for center-weighting", min = 0.1, max = 10}, + } + }, + { + n = "bloom", d = "Bloom", + entries = { + {n = "intensity", d = "Intensity", min = 0, max = 1}, + {n = "strength_factor", d = "Strength Factor", min = 0.1, max = 10}, + {n = "radius", d = "Radius", min = 0.1, max = 8}, + }, + }, + { + n = "volumetric_light", d = "Volumetric Lighting", + entries = { + {n = "strength", d = "Strength", min = 0, max = 1}, + }, + }, +} + +local function dump_lighting(lighting) + local result = "{\n" + local section_count = 0 + for _,section in ipairs(lighting_sections) do + section_count = section_count + 1 + + local parameters = section.entries or {} + local state = lighting[section.n] or {} + + result = result.." "..section.n.." = {\n" + + local count = 0 + for _,v in ipairs(parameters) do + count = count + 1 + result = result.." "..v.n.." = "..(math.floor(state[v.n] * 1000)/1000) + if count < #parameters then + result = result.."," + end + result = result.."\n" + end + + result = result.." }" + + if section_count < #lighting_sections then + result = result.."," + end + result = result.."\n" + end + result = result .."}" + return result +end + +core.register_chatcommand("set_lighting", { + params = "", + description = "Tune lighting parameters", + func = function(player_name, param) + local player = core.get_player_by_name(player_name); + if not player then return end + + local lighting = player:get_lighting() + local exposure = lighting.exposure or {} + + local content = {} + local line = 1 + for _,section in ipairs(lighting_sections) do + local parameters = section.entries or {} + local state = lighting[section.n] or {} + + table.insert(content, "label[1,"..line..";"..section.d.."]") + line = line + 1 + + for _,v in ipairs(parameters) do + table.insert(content, "label[2,"..line..";"..v.d.."]") + table.insert(content, "scrollbaroptions[min=0;max=1000;smallstep=10;largestep=100;thumbsize=10]") + local value = state[v.n] + if v.type == "log2" then + value = math.log(value or 1) / math.log(2) + end + local sb_scale = math.floor(1000 * (math.max(v.min, value or 0) - v.min) / (v.max - v.min)) + table.insert(content, "scrollbar[2,"..(line+0.7)..";12,1;horizontal;"..section.n.."."..v.n..";"..sb_scale.."]") + line = line + 2.7 + end + + line = line + 1 + end + + local form = { + "formspec_version[2]", + "size[15,", line, "]", + "position[0.99,0.15]", + "anchor[1,0]", + "padding[0.05,0.1]", + "no_prepend[]", + } + table.insert_all(form, content) + + core.show_formspec(player_name, "lighting", table.concat(form)) + local debug_value = dump_lighting(lighting) + local debug_ui = player:hud_add({type="text", position={x=0.1, y=0.3}, scale={x=1,y=1}, alignment = {x=1, y=1}, text=debug_value, number=0xFFFFFF}) + player:get_meta():set_int("lighting_hud", debug_ui) + end +}) + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "lighting" then return end + + if not player then return end + + local hud_id = player:get_meta():get_int("lighting_hud") + + if fields.quit then + player:hud_remove(hud_id) + player:get_meta():set_int("lighting_hud", -1) + return + end + + local lighting = player:get_lighting() + for _,section in ipairs(lighting_sections) do + local parameters = section.entries or {} + + local state = (lighting[section.n] or {}) + lighting[section.n] = state + + for _,v in ipairs(parameters) do + + if fields[section.n.."."..v.n] then + local event = core.explode_scrollbar_event(fields[section.n.."."..v.n]) + if event.type == "CHG" then + local value = v.min + (v.max - v.min) * (event.value / 1000); + if v.type == "log2" then + value = math.pow(2, value); + end + state[v.n] = value; + end + end + end + end + + local debug_value = dump_lighting(lighting) + player:hud_change(hud_id, "text", debug_value) + + player:set_lighting(lighting) +end) \ No newline at end of file diff --git a/data/games/garage/mods/lighting/mod.conf b/data/games/garage/mods/lighting/mod.conf new file mode 100644 index 0000000..83bbbef --- /dev/null +++ b/data/games/garage/mods/lighting/mod.conf @@ -0,0 +1,2 @@ +name = lighting +description = UI to control and debug lighting parameters diff --git a/data/games/garage/mods/log/init.lua b/data/games/garage/mods/log/init.lua new file mode 100644 index 0000000..64673f2 --- /dev/null +++ b/data/games/garage/mods/log/init.lua @@ -0,0 +1,16 @@ +local modname = core.get_current_modname() +local prefix = "["..modname.."] " + +-- Startup info +core.log("action", prefix.."modname="..dump(modname)) +core.log("action", prefix.."modpath="..dump(core.get_modpath(modname))) +core.log("action", prefix.."worldpath="..dump(core.get_worldpath())) + +-- Callback info +core.register_on_mods_loaded(function() + core.log("action", prefix.."Callback: on_mods_loaded()") +end) + +core.register_on_chatcommand(function(name, command, params) + core.log("action", prefix.."Caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'") +end) diff --git a/data/games/garage/mods/log/mod.conf b/data/games/garage/mods/log/mod.conf new file mode 100644 index 0000000..d2f37b9 --- /dev/null +++ b/data/games/garage/mods/log/mod.conf @@ -0,0 +1,2 @@ +name = log +description = Log DevTest-related actions diff --git a/data/games/garage/mods/mapgen/init.lua b/data/games/garage/mods/mapgen/init.lua new file mode 100644 index 0000000..d843902 --- /dev/null +++ b/data/games/garage/mods/mapgen/init.lua @@ -0,0 +1,104 @@ +-- +-- Aliases for map generator outputs +-- + +-- ESSENTIAL node aliases +-- Basic nodes +core.register_alias("mapgen_stone", "basenodes:stone") +core.register_alias("mapgen_water_source", "basenodes:water_source") +core.register_alias("mapgen_river_water_source", "basenodes:river_water_source") + +-- Additional essential aliases for v6 +core.register_alias("mapgen_lava_source", "basenodes:lava_source") +core.register_alias("mapgen_dirt", "basenodes:dirt") +core.register_alias("mapgen_dirt_with_grass", "basenodes:dirt_with_grass") +core.register_alias("mapgen_sand", "basenodes:sand") +core.register_alias("mapgen_tree", "basenodes:tree") +core.register_alias("mapgen_leaves", "basenodes:leaves") +core.register_alias("mapgen_apple", "basenodes:apple") + +-- Essential alias for dungeons +core.register_alias("mapgen_cobble", "basenodes:cobble") + +-- Optional aliases for v6 (they all have fallback values in the engine) +if core.settings:get_bool("devtest_v6_mapgen_aliases", false) then + core.register_alias("mapgen_gravel", "basenodes:gravel") + core.register_alias("mapgen_desert_stone", "basenodes:desert_stone") + core.register_alias("mapgen_desert_sand", "basenodes:desert_sand") + core.register_alias("mapgen_dirt_with_snow", "basenodes:dirt_with_snow") + core.register_alias("mapgen_snowblock", "basenodes:snowblock") + core.register_alias("mapgen_snow", "basenodes:snow") + core.register_alias("mapgen_ice", "basenodes:ice") + core.register_alias("mapgen_junglegrass", "basenodes:junglegrass") + core.register_alias("mapgen_jungletree", "basenodes:jungletree") + core.register_alias("mapgen_jungleleaves", "basenodes:jungleleaves") + core.register_alias("mapgen_pine_tree", "basenodes:pine_tree") + core.register_alias("mapgen_pine_needles", "basenodes:pine_needles") +end +-- Optional alias for mossycobble (should fall back to cobble) +if core.settings:get_bool("devtest_dungeon_mossycobble", false) then + core.register_alias("mapgen_mossycobble", "basenodes:mossycobble") +end +-- Optional aliases for dungeon stairs (should fall back to full nodes) +if core.settings:get_bool("devtest_dungeon_stairs", false) then + core.register_alias("mapgen_stair_cobble", "stairs:stair_cobble") + if core.settings:get_bool("devtest_v6_mapgen_aliases", false) then + core.register_alias("mapgen_stair_desert_stone", "stairs:stair_desert_stone") + end +end + +-- +-- Register biomes for biome API +-- + +core.clear_registered_biomes() +core.clear_registered_decorations() + +if core.settings:get_bool("devtest_register_biomes", true) then + core.register_biome({ + name = "mapgen:grassland", + node_top = "basenodes:dirt_with_grass", + depth_top = 1, + node_filler = "basenodes:dirt", + depth_filler = 1, + node_riverbed = "basenodes:sand", + depth_riverbed = 2, + node_dungeon = "basenodes:cobble", + node_dungeon_alt = "basenodes:mossycobble", + node_dungeon_stair = "stairs:stair_cobble", + y_max = 31000, + y_min = 4, + heat_point = 50, + humidity_point = 50, + }) + + core.register_biome({ + name = "mapgen:grassland_ocean", + node_top = "basenodes:sand", + depth_top = 1, + node_filler = "basenodes:sand", + depth_filler = 3, + node_riverbed = "basenodes:sand", + depth_riverbed = 2, + node_cave_liquid = "basenodes:water_source", + node_dungeon = "basenodes:cobble", + node_dungeon_alt = "basenodes:mossycobble", + node_dungeon_stair = "stairs:stair_cobble", + y_max = 3, + y_min = -255, + heat_point = 50, + humidity_point = 50, + }) + + core.register_biome({ + name = "mapgen:grassland_under", + node_cave_liquid = {"basenodes:water_source", "basenodes:lava_source"}, + node_dungeon = "basenodes:cobble", + node_dungeon_alt = "basenodes:mossycobble", + node_dungeon_stair = "stairs:stair_cobble", + y_max = -256, + y_min = -31000, + heat_point = 50, + humidity_point = 50, + }) +end diff --git a/data/games/garage/mods/mapgen/mod.conf b/data/games/garage/mods/mapgen/mod.conf new file mode 100644 index 0000000..15750cc --- /dev/null +++ b/data/games/garage/mods/mapgen/mod.conf @@ -0,0 +1,3 @@ +name = mapgen +description = Minimal map generator +depends = basenodes diff --git a/data/games/garage/mods/modchannels/init.lua b/data/games/garage/mods/modchannels/init.lua new file mode 100644 index 0000000..2003841 --- /dev/null +++ b/data/games/garage/mods/modchannels/init.lua @@ -0,0 +1,14 @@ +-- +-- Mod channels experimental handlers +-- +local mod_channel = core.mod_channel_join("experimental_preview") + +core.register_on_modchannel_message(function(channel, sender, message) + core.log("action", "[modchannels] Server received message `" .. message + .. "` on channel `" .. channel .. "` from sender `" .. sender .. "`") + + if mod_channel:is_writeable() then + mod_channel:send_all("experimental answers to preview") + mod_channel:leave() + end +end) diff --git a/data/games/garage/mods/modchannels/mod.conf b/data/games/garage/mods/modchannels/mod.conf new file mode 100644 index 0000000..7c13aad --- /dev/null +++ b/data/games/garage/mods/modchannels/mod.conf @@ -0,0 +1,2 @@ +name = modchannels +description = Add experimental mod channel handlers diff --git a/data/games/garage/mods/soundstuff/bigfoot.lua b/data/games/garage/mods/soundstuff/bigfoot.lua new file mode 100644 index 0000000..afa2b6e --- /dev/null +++ b/data/games/garage/mods/soundstuff/bigfoot.lua @@ -0,0 +1,48 @@ + +local walk_speed = 2 +local walk_distance = 10 + +core.register_entity("soundstuff:bigfoot", { + initial_properties = { + physical = false, + collisionbox = {-1, -1, -1, 1, 1, 1}, + selectionbox = {-1, -1, -1, 1, 1, 1}, + visual = "upright_sprite", + visual_size = {x = 2, y = 2, z = 2}, + textures = {"soundstuff_bigfoot.png", "soundstuff_bigfoot.png^[transformFX"}, + makes_footstep_sound = true, + static_save = false, + }, + + on_activate = function(self, _staticdata, _dtime_s) + self.min_x = self.object:get_pos().x - walk_distance * 0.5 + self.max_x = self.min_x + walk_distance + self.vel = vector.new(walk_speed, 0, 0) + end, + + on_step = function(self, _dtime, _moveresult) + local pos = self.object:get_pos() + if pos.x < self.min_x then + self.vel = vector.new(walk_speed, 0, 0) + elseif pos.x > self.max_x then + self.vel = vector.new(-walk_speed, 0, 0) + end + self.object:set_velocity(self.vel) + end, +}) + +core.register_chatcommand("spawn_bigfoot", { + params = "", + description = "Spawn a big foot object that makes footstep sounds", + func = function(name, _param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos = player:get_pos() + pos.y = pos.y + player:get_properties().collisionbox[2] + pos.y = pos.y - (-1) -- bigfoot collisionbox goes 1 down + core.add_entity(pos, "soundstuff:bigfoot") + return true + end, +}) diff --git a/data/games/garage/mods/soundstuff/init.lua b/data/games/garage/mods/soundstuff/init.lua new file mode 100644 index 0000000..6c0049a --- /dev/null +++ b/data/games/garage/mods/soundstuff/init.lua @@ -0,0 +1,6 @@ + +local path = core.get_modpath("soundstuff") .. "/" +dofile(path .. "sound_event_items.lua") +dofile(path .. "jukebox.lua") +dofile(path .. "bigfoot.lua") +dofile(path .. "racecar.lua") diff --git a/data/games/garage/mods/soundstuff/jukebox.lua b/data/games/garage/mods/soundstuff/jukebox.lua new file mode 100644 index 0000000..6c70fe3 --- /dev/null +++ b/data/games/garage/mods/soundstuff/jukebox.lua @@ -0,0 +1,331 @@ +local F = core.formspec_escape + +-- hashed node pos -> sound handle +local played_sounds = {} + +-- all of these can be set via the formspec +local meta_keys = { + -- SimpleSoundSpec + "sss.name", + "sss.gain", + "sss.pitch", + "sss.fade", + -- sound parameters + "sparam.gain", + "sparam.pitch", + "sparam.fade", + "sparam.start_time", + "sparam.loop", + "sparam.pos", + "sparam.object", + "sparam.to_player", + "sparam.exclude_player", + "sparam.max_hear_distance", + -- fade + "fade.step", + "fade.gain", + -- other + "ephemeral", +} + +local function get_all_metadata(meta) + return { + sss = { + name = meta:get_string("sss.name"), + gain = meta:get_string("sss.gain"), + pitch = meta:get_string("sss.pitch"), + fade = meta:get_string("sss.fade"), + }, + sparam = { + gain = meta:get_string("sparam.gain"), + pitch = meta:get_string("sparam.pitch"), + fade = meta:get_string("sparam.fade"), + start_time = meta:get_string("sparam.start_time"), + loop = meta:get_string("sparam.loop"), + pos = meta:get_string("sparam.pos"), + object = meta:get_string("sparam.object"), + to_player = meta:get_string("sparam.to_player"), + exclude_player = meta:get_string("sparam.exclude_player"), + max_hear_distance = meta:get_string("sparam.max_hear_distance"), + }, + fade = { + gain = meta:get_string("fade.gain"), + step = meta:get_string("fade.step"), + }, + ephemeral = meta:get_string("ephemeral"), + } +end + +local function log_msg(msg) + core.log("action", msg) + core.chat_send_all(msg) +end + +local function try_call(f, ...) + local function log_on_err(success, errmsg, ...) + if not success then + log_msg("[soundstuff:jukebox] Call failed: "..errmsg) + else + return errmsg, ... + end + end + + return log_on_err(pcall(f, ...)) +end + +local function show_formspec(pos, player) + local meta = core.get_meta(pos) + + local md = get_all_metadata(meta) + + local pos_hash = core.hash_node_position(pos) + local sound_handle = played_sounds[pos_hash] + + local fs = {} + local function fs_add(str) + table.insert(fs, str) + end + + fs_add([[ + formspec_version[6] + size[14,12] + ]]) + + -- SimpleSoundSpec + fs_add(string.format([[ + container[0.5,0.5] + box[-0.1,-0.1;4.2,3.2;#EBEBEB20] + style[*;font=mono,bold] + label[0,0.25;SimpleSoundSpec] + style[*;font=mono] + field[0.00,1.00;4,0.75;sss.name;name;%s] + field[0.00,2.25;1,0.75;sss.gain;gain;%s] + field[1.25,2.25;1,0.75;sss.pitch;pitch;%s] + field[2.50,2.25;1,0.75;sss.fade;fade;%s] + container_end[] + field_close_on_enter[sss.name;false] + field_close_on_enter[sss.gain;false] + field_close_on_enter[sss.pitch;false] + field_close_on_enter[sss.fade;false] + ]], F(md.sss.name), F(md.sss.gain), F(md.sss.pitch), F(md.sss.fade))) + + -- sound parameter table + fs_add(string.format([[ + container[5.5,0.5] + box[-0.1,-0.1;4.2,10.7;#EBEBEB20] + style[*;font=mono,bold] + label[0,0.25;sound parameter table] + style[*;font=mono] + field[0.00,1;1,0.75;sparam.gain;gain;%s] + field[1.25,1;1,0.75;sparam.pitch;pitch;%s] + field[2.50,1;1,0.75;sparam.fade;fade;%s] + field[0,2.25;4,0.75;sparam.start_time;start_time;%s] + field[0,3.50;4,0.75;sparam.loop;loop;%s] + field[0,4.75;4,0.75;sparam.pos;pos;%s] + field[0,6.00;4,0.75;sparam.object;object;%s] + field[0,7.25;4,0.75;sparam.to_player;to_player;%s] + field[0,8.50;4,0.75;sparam.exclude_player;exclude_player;%s] + field[0,9.75;4,0.75;sparam.max_hear_distance;max_hear_distance;%s] + container_end[] + field_close_on_enter[sparam.gain;false] + field_close_on_enter[sparam.pitch;false] + field_close_on_enter[sparam.fade;false] + field_close_on_enter[sparam.start_time;false] + field_close_on_enter[sparam.loop;false] + field_close_on_enter[sparam.pos;false] + field_close_on_enter[sparam.object;false] + field_close_on_enter[sparam.to_player;false] + field_close_on_enter[sparam.exclude_player;false] + field_close_on_enter[sparam.max_hear_distance;false] + tooltip[sparam.object;Get a name with the Branding Iron.] + ]], F(md.sparam.gain), F(md.sparam.pitch), F(md.sparam.fade), + F(md.sparam.start_time), F(md.sparam.loop), F(md.sparam.pos), + F(md.sparam.object), F(md.sparam.to_player), F(md.sparam.exclude_player), + F(md.sparam.max_hear_distance))) + + -- fade + fs_add(string.format([[ + container[10.75,3] + box[-0.1,-0.1;3.2,3.2;#EBEBEB20] + style[*;font=mono,bold] + label[0,0.25;fade] + style[*;font=mono] + field[0.00,1;1,0.75;fade.step;step;%s] + field[1.25,1;1,0.75;fade.gain;gain;%s] + ]], F(md.fade.step), F(md.fade.gain))) + if not sound_handle then + fs_add([[ + box[0,2;3,0.75;#363636FF] + label[0.25,2.375;no sound] + ]]) + else + fs_add([[ + button[0,2;3,0.75;btn_fade;Fade] + ]]) + end + fs_add([[ + container_end[] + field_close_on_enter[fade.step;false] + field_close_on_enter[fade.gain;false] + ]]) + + -- ephemeral + fs_add(string.format([[ + checkbox[0.5,5;ephemeral;ephemeral;%s] + ]], md.ephemeral)) + + -- play/stop and release buttons + if not sound_handle then + fs_add([[ + container[10.75,0.5] + button[0,0;3,0.75;btn_play;Play] + container_end[] + ]]) + else + fs_add([[ + container[10.75,0.5] + button[0,0;3,0.75;btn_stop;Stop] + button[0,1;3,0.75;btn_release;Release] + container_end[] + ]]) + end + + -- save and quit button + fs_add([[ + button_exit[10.75,11;3,0.75;btn_save_quit;Save & Quit] + ]]) + + core.show_formspec(player:get_player_name(), "soundstuff:jukebox@"..pos:to_string(), + table.concat(fs)) +end + +core.register_node("soundstuff:jukebox", { + description = "Jukebox\nAllows to play arbitrary sounds.", + tiles = {"soundstuff_jukebox.png"}, + groups = {dig_immediate = 2}, + + on_construct = function(pos) + local meta = core.get_meta(pos) + -- SimpleSoundSpec + meta:set_string("sss.name", "") + meta:set_string("sss.gain", "") + meta:set_string("sss.pitch", "") + meta:set_string("sss.fade", "") + -- sound parameters + meta:set_string("sparam.gain", "") + meta:set_string("sparam.pitch", "") + meta:set_string("sparam.fade", "") + meta:set_string("sparam.start_time", "") + meta:set_string("sparam.loop", "") + meta:set_string("sparam.pos", pos:to_string()) + meta:set_string("sparam.object", "") + meta:set_string("sparam.to_player", "") + meta:set_string("sparam.exclude_player", "") + meta:set_string("sparam.max_hear_distance", "") + -- fade + meta:set_string("fade.gain", "") + meta:set_string("fade.step", "") + -- other + meta:set_string("ephemeral", "") + + meta:mark_as_private(meta_keys) + end, + + on_rightclick = function(pos, _node, clicker, _itemstack, _pointed_thing) + show_formspec(pos, clicker) + end, +}) + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname:sub(1, 19) ~= "soundstuff:jukebox@" then + return false + end + + local pos = vector.from_string(formname, 20) + if not pos or pos ~= pos:round() then + core.log("error", "[soundstuff:jukebox] Invalid formname.") + return true + end + + local meta = core.get_meta(pos) + + for _, k in ipairs(meta_keys) do + if fields[k] then + meta:set_string(k, fields[k]) + end + end + meta:mark_as_private(meta_keys) + + local pos_hash = core.hash_node_position(pos) + local sound_handle = played_sounds[pos_hash] + + if not sound_handle then + if fields.btn_play then + local md = get_all_metadata(meta) + + local sss = { + name = md.sss.name, + gain = tonumber(md.sss.gain), + pitch = tonumber(md.sss.pitch), + fade = tonumber(md.sss.fade), + } + local sparam = { + gain = tonumber(md.sparam.gain), + pitch = tonumber(md.sparam.pitch), + fade = tonumber(md.sparam.fade), + start_time = tonumber(md.sparam.start_time), + loop = core.is_yes(md.sparam.loop), + pos = vector.from_string(md.sparam.pos), + object = testtools.get_branded_object(md.sparam.object), + to_player = md.sparam.to_player, + exclude_player = md.sparam.exclude_player, + max_hear_distance = tonumber(md.sparam.max_hear_distance), + } + local ephemeral = core.is_yes(md.ephemeral) + + log_msg(string.format( + "[soundstuff:jukebox] Playing sound: core.sound_play(%s, %s, %s)", + string.format("{name=\"%s\", gain=%s, pitch=%s, fade=%s}", + sss.name, sss.gain, sss.pitch, sss.fade), + string.format("{gain=%s, pitch=%s, fade=%s, start_time=%s, loop=%s, pos=%s, " + .."object=%s, to_player=\"%s\", exclude_player=\"%s\", max_hear_distance=%s}", + sparam.gain, sparam.pitch, sparam.fade, sparam.start_time, + sparam.loop, sparam.pos, sparam.object and "", + sparam.to_player, sparam.exclude_player, + sparam.max_hear_distance), + tostring(ephemeral))) + + sound_handle = try_call(core.sound_play, sss, sparam, ephemeral) + + played_sounds[pos_hash] = sound_handle + show_formspec(pos, player) + end + + else + if fields.btn_stop then + log_msg("[soundstuff:jukebox] Stopping sound: core.sound_stop()") + + try_call(core.sound_stop, sound_handle) + + elseif fields.btn_release then + log_msg("[soundstuff:jukebox] Releasing handle.") + + played_sounds[pos_hash] = nil + show_formspec(pos, player) + + elseif fields.btn_fade then + local md = get_all_metadata(meta) + + local step = tonumber(md.fade.step) + local gain = tonumber(md.fade.gain) + + log_msg(string.format( + "[soundstuff:jukebox] Fading sound: core.sound_fade(, %s, %s)", + step, gain)) + + try_call(core.sound_fade, sound_handle, step, gain) + end + end + + return true +end) diff --git a/data/games/garage/mods/soundstuff/mod.conf b/data/games/garage/mods/soundstuff/mod.conf new file mode 100644 index 0000000..a6ed6d4 --- /dev/null +++ b/data/games/garage/mods/soundstuff/mod.conf @@ -0,0 +1,3 @@ +name = soundstuff +description = Example items and nodes for testing sound effects +depends = testtools diff --git a/data/games/garage/mods/soundstuff/racecar.lua b/data/games/garage/mods/soundstuff/racecar.lua new file mode 100644 index 0000000..9162792 --- /dev/null +++ b/data/games/garage/mods/soundstuff/racecar.lua @@ -0,0 +1,31 @@ + +local drive_speed = 20 +local drive_distance = 30 + +core.register_entity("soundstuff:racecar", { + initial_properties = { + physical = false, + collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + visual = "upright_sprite", + visual_size = {x = 1, y = 1, z = 1}, + textures = {"soundstuff_racecar.png", "soundstuff_racecar.png^[transformFX"}, + static_save = false, + }, + + on_activate = function(self, _staticdata, _dtime_s) + self.min_x = self.object:get_pos().x - drive_distance * 0.5 + self.max_x = self.min_x + drive_distance + self.vel = vector.new(drive_speed, 0, 0) + end, + + on_step = function(self, _dtime, _moveresult) + local pos = self.object:get_pos() + if pos.x < self.min_x then + self.vel = vector.new(drive_speed, 0, 0) + elseif pos.x > self.max_x then + self.vel = vector.new(-drive_speed, 0, 0) + end + self.object:set_velocity(self.vel) + end, +}) diff --git a/data/games/garage/mods/soundstuff/sound_event_items.lua b/data/games/garage/mods/soundstuff/sound_event_items.lua new file mode 100644 index 0000000..9b2eb6b --- /dev/null +++ b/data/games/garage/mods/soundstuff/sound_event_items.lua @@ -0,0 +1,209 @@ +local simple_nodes = { + footstep = { "Footstep Sound Node", "soundstuff_node_footstep.png", "Plays sound when you step on it" }, + dig = { "Dig Sound Node", "soundstuff_node_dig.png", "Plays sound when you dig it" }, + dug = { "Dug Sound Node", "soundstuff_node_dug.png", "Plays sound when you've dug it" }, + place = { "Place Sound Node", "soundstuff_node_place.png", "Plays sound when you place it" }, + place_failed = { "Place Failed Sound Node", "soundstuff_node_place_failed.png", "Plays sound when you try to place it but failed" }, +} + +for k,v in pairs(simple_nodes) do + core.register_node("soundstuff:"..k, { + description = v[1].."\n"..v[3], + tiles = {"soundstuff_node_sound.png","soundstuff_node_sound.png",v[2]}, + groups = {dig_immediate=2}, + sounds = { + [k] = { name = "soundstuff_mono", gain = 1.0 }, + } + }) +end + +core.register_node("soundstuff:place_failed_attached", { + description = "Attached Place Failed Sound Node".."\n".. + "Attached to the floor; plays a sound when you try to place it but failed", + tiles = {"soundstuff_node_sound.png", "soundstuff_node_sound.png", "soundstuff_node_place_failed.png"}, + groups = {dig_immediate=2, attached_node=1}, + drawtype = "nodebox", + paramtype = "light", + node_box = { type = "fixed", fixed = { + { -7/16, -7/16, -7/16, 7/16, 7/16, 7/16 }, + { -0.5, -0.5, -0.5, 0.5, -7/16, 0.5 }, + }}, + sounds = { + place_failed = { name = "soundstuff_mono", gain = 1.0 }, + }, +}) + +core.register_node("soundstuff:fall", { + description = "Fall Sound Node".."\n".. + "Falls and plays sound if node below is gone", + tiles = {"soundstuff_node_sound.png", "soundstuff_node_sound.png", "soundstuff_node_fall.png"}, + groups = {dig_immediate=2, falling_node=1}, + sounds = { + fall = { name = "soundstuff_mono", gain = 1.0 }, + } +}) + +core.register_node("soundstuff:fall_attached", { + description = "Attached Fall Sound Node".."\n".. + "Drops as item and plays sound if node below is gone", + tiles = {"soundstuff_node_sound.png", "soundstuff_node_sound.png", "soundstuff_node_fall.png"}, + groups = {dig_immediate=2, attached_node=1}, + drawtype = "nodebox", + paramtype = "light", + node_box = { type = "fixed", fixed = { + { -7/16, -7/16, -7/16, 7/16, 7/16, 7/16 }, + { -0.5, -0.5, -0.5, 0.5, -7/16, 0.5 }, + }}, + sounds = { + fall = { name = "soundstuff_mono", gain = 1.0 }, + } +}) + +core.register_node("soundstuff:footstep_liquid", { + description = "Liquid Footstep Sound Node".."\n".. + "Plays sound when moving inside it; swimmable", + drawtype = "liquid", + tiles = { + "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190", + }, + special_tiles = { + {name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190", + backface_culling = false}, + {name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190", + backface_culling = true}, + }, + liquids_pointable = true, + liquidtype = "source", + liquid_alternative_flowing = "soundstuff:footstep_liquid", + liquid_alternative_source = "soundstuff:footstep_liquid", + liquid_renewable = false, + liquid_range = 0, + liquid_viscosity = 0, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + post_effect_color = {a = 64, r = 0, g = 0, b = 200}, + sounds = { + footstep = { name = "soundstuff_mono", gain = 1.0 }, + } +}) + +core.register_node("soundstuff:footstep_climbable", { + description = "Climbable Footstep Sound Node".."\n".. + "Plays sound when moving inside it; can climb up and down here", + drawtype = "allfaces", + tiles = { + "soundstuff_node_climbable.png", + }, + paramtype = "light", + sunlight_propagates = true, + walkable = false, + climbable = true, + is_ground_content = false, + groups = { dig_immediate = 2 }, + sounds = { + footstep = { name = "soundstuff_mono", gain = 1.0 }, + } +}) + + + +core.register_craftitem("soundstuff:eat", { + description = "Eat Sound Item".."\n".. + "Makes a sound when 'eaten' (with punch key)", + inventory_image = "soundstuff_eat.png", + on_use = core.item_eat(0), + sound = { + eat = { name = "soundstuff_mono", gain = 1.0 }, + } +}) + +core.register_tool("soundstuff:breaks", { + description = "Break Sound Tool".."\n".. + "Digs cracky=3 and more".."\n".. + "Makes a sound when it breaks", + inventory_image = "soundstuff_node_dug.png", + sound = { + breaks = { name = "soundstuff_mono", gain = 1.0 }, + }, + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[2]=2.00, [3]=1.20}, uses=1, maxlevel=0}, + choppy={times={[2]=2.00, [3]=1.20}, uses=1, maxlevel=0}, + snappy={times={[2]=2.00, [3]=1.20}, uses=1, maxlevel=0}, + crumbly={times={[2]=2.00, [3]=1.20}, uses=1, maxlevel=0}, + }, + }, +}) + + +core.register_tool("soundstuff:punch_use", { + description = "Punch Use Sound Tool\n".. + "Digs cracky=3 and more\n".. + "Makes a sound when used on node or entity", + inventory_image = "soundstuff_node_dig.png", + sound = { + punch_use = { name = "soundstuff_mono", gain = 1.0 }, + }, + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[2]=2.00, [3]=1.20}, uses=0, maxlevel=0}, + choppy={times={[2]=2.00, [3]=1.20}, uses=0, maxlevel=0}, + snappy={times={[2]=2.00, [3]=1.20}, uses=0, maxlevel=0}, + crumbly={times={[2]=2.00, [3]=1.20}, uses=0, maxlevel=0}, + }, + }, +}) + +core.register_tool("soundstuff:punch_use_air", { + description = "Punch Use (Air) Sound Tool\n".. + "Makes a sound when used pointing at nothing", + inventory_image = "soundstuff_node_dig.png", + sound = { + punch_use_air = { name = "soundstuff_mono", gain = 1.0 }, + }, +}) + +-- Plays sound repeatedly +core.register_node("soundstuff:positional", { + description = "Positional Sound Node".."\n".. + "Repeatedly plays a sound at the node location", + on_construct = function(pos) + local timer = core.get_node_timer(pos) + timer:start(0) + end, + on_timer = function(pos, elapsed) + local node = core.get_node(pos) + local dist = node.param2 + if dist == 0 then + dist = nil + end + core.sound_play("soundstuff_mono", { pos = pos, max_hear_distance = dist }) + local timer = core.get_node_timer(pos) + timer:start(0.7) + end, + on_rightclick = function(pos, node, clicker) + node.param2 = (node.param2 + 1) % 64 + core.set_node(pos, node) + if clicker and clicker:is_player() then + local dist = node.param2 + local diststr + if dist == 0 then + diststr = "" + else + diststr = tostring(dist) + end + core.chat_send_player(clicker:get_player_name(), "max_hear_distance = " .. diststr) + end + end, + + groups = { dig_immediate = 2 }, + tiles = { "soundstuff_node_sound.png" }, +}) + diff --git a/data/games/garage/mods/soundstuff/sounds/gitignored_sounds/custom_sounds_here.txt b/data/games/garage/mods/soundstuff/sounds/gitignored_sounds/custom_sounds_here.txt new file mode 100644 index 0000000..f07d44b --- /dev/null +++ b/data/games/garage/mods/soundstuff/sounds/gitignored_sounds/custom_sounds_here.txt @@ -0,0 +1,2 @@ +Put your own testing sounds here. The folder is gitignored. +Using a sound-pack is also possible. diff --git a/data/games/garage/mods/soundstuff/sounds/soundstuff_mono.ogg b/data/games/garage/mods/soundstuff/sounds/soundstuff_mono.ogg new file mode 100644 index 0000000..43428d5 Binary files /dev/null and b/data/games/garage/mods/soundstuff/sounds/soundstuff_mono.ogg differ diff --git a/data/games/garage/mods/soundstuff/sounds/soundstuff_sinus.ogg b/data/games/garage/mods/soundstuff/sounds/soundstuff_sinus.ogg new file mode 100644 index 0000000..8dbc00a Binary files /dev/null and b/data/games/garage/mods/soundstuff/sounds/soundstuff_sinus.ogg differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_bigfoot.png b/data/games/garage/mods/soundstuff/textures/soundstuff_bigfoot.png new file mode 100644 index 0000000..6770b3f Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_bigfoot.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_eat.png b/data/games/garage/mods/soundstuff/textures/soundstuff_eat.png new file mode 100644 index 0000000..aed2054 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_eat.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_jukebox.png b/data/games/garage/mods/soundstuff/textures/soundstuff_jukebox.png new file mode 100644 index 0000000..b8c17a6 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_jukebox.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_blank.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_blank.png new file mode 100644 index 0000000..4dffacc Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_blank.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_climbable.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_climbable.png new file mode 100644 index 0000000..3888f79 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_climbable.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_dig.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_dig.png new file mode 100644 index 0000000..67ba111 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_dig.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_dug.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_dug.png new file mode 100644 index 0000000..bab5fbe Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_dug.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_fall.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_fall.png new file mode 100644 index 0000000..17b14f1 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_fall.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_footstep.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_footstep.png new file mode 100644 index 0000000..6367ae9 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_footstep.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_place.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_place.png new file mode 100644 index 0000000..d159ad5 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_place.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_place_failed.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_place_failed.png new file mode 100644 index 0000000..780ba94 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_place_failed.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_node_sound.png b/data/games/garage/mods/soundstuff/textures/soundstuff_node_sound.png new file mode 100644 index 0000000..0592a02 Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_node_sound.png differ diff --git a/data/games/garage/mods/soundstuff/textures/soundstuff_racecar.png b/data/games/garage/mods/soundstuff/textures/soundstuff_racecar.png new file mode 100644 index 0000000..8e8ff5a Binary files /dev/null and b/data/games/garage/mods/soundstuff/textures/soundstuff_racecar.png differ diff --git a/data/games/garage/mods/stairs/init.lua b/data/games/garage/mods/stairs/init.lua new file mode 100644 index 0000000..267540e --- /dev/null +++ b/data/games/garage/mods/stairs/init.lua @@ -0,0 +1,66 @@ +stairs = {} + +-- Node will be called stairs:stair_ +function stairs.register_stair(subname, recipeitem, groups, images, description) + core.register_node(":stairs:stair_" .. subname, { + description = description.."\n".. + "param2 = facedir rotation (0..23)", + drawtype = "nodebox", + tiles = images, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = true, + groups = groups, + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + {-0.5, 0, 0, 0.5, 0.5, 0.5}, + }, + }, + }) +end + +-- Node will be called stairs:slab_ +function stairs.register_slab(subname, recipeitem, groups, images, description) + core.register_node(":stairs:slab_" .. subname, { + description = description, + drawtype = "nodebox", + tiles = images, + paramtype = "light", + is_ground_content = true, + groups = groups, + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + }) +end + +-- Nodes will be called stairs:{stair,slab}_ +function stairs.register_stair_and_slab(subname, recipeitem, groups, images, desc_stair, desc_slab) + stairs.register_stair(subname, recipeitem, groups, images, desc_stair) + stairs.register_slab(subname, recipeitem, groups, images, desc_slab) +end + +stairs.register_stair_and_slab("stone", "basenodes:stone", + {cracky=3}, + {"default_stone.png"}, + "Stone Stair", + "Stone Slab") + +stairs.register_stair_and_slab("desert_stone", "basenodes:desert_stone", + {cracky=3}, + {"default_desert_stone.png"}, + "Desert Stone Stair", + "Desert Stone Slab") + +stairs.register_stair_and_slab("cobble", "basenodes:cobble", + {cracky=3}, + {"default_cobble.png"}, + "Cobblestone Stair", + "Cobblestone Slab") diff --git a/data/games/garage/mods/stairs/mod.conf b/data/games/garage/mods/stairs/mod.conf new file mode 100644 index 0000000..724bff8 --- /dev/null +++ b/data/games/garage/mods/stairs/mod.conf @@ -0,0 +1,3 @@ +name = stairs +description = Adds stairs and slabs +depends = basenodes diff --git a/data/games/garage/mods/testabms/README.md b/data/games/garage/mods/testabms/README.md new file mode 100644 index 0000000..60fa6d6 --- /dev/null +++ b/data/games/garage/mods/testabms/README.md @@ -0,0 +1,6 @@ +# Test ABMs + +This mod contains a nodes and related ABM actions. +By placing these nodes, you can test basic ABM behaviours. + +There are separate tests for ABM `chance`, `interval`, `min_y`, `max_y`, `neighbor` and `without_neighbor` fields. diff --git a/data/games/garage/mods/testabms/after_node.lua b/data/games/garage/mods/testabms/after_node.lua new file mode 100644 index 0000000..fa2b3ab --- /dev/null +++ b/data/games/garage/mods/testabms/after_node.lua @@ -0,0 +1,12 @@ + +local S = core.get_translator("testnodes") + +-- After ABM node +core.register_node("testabms:after_abm", { + description = S("After ABM processed node."), + drawtype = "normal", + tiles = { "testabms_after_node.png" }, + + groups = { dig_immediate = 3 }, +}) + diff --git a/data/games/garage/mods/testabms/chances.lua b/data/games/garage/mods/testabms/chances.lua new file mode 100644 index 0000000..a84e752 --- /dev/null +++ b/data/games/garage/mods/testabms/chances.lua @@ -0,0 +1,56 @@ +-- test ABMs with different chances + +local S = core.get_translator("testnodes") + +-- ABM chance 5 node +core.register_node("testabms:chance_5", { + description = S("Node for test ABM chance_5"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:chance_5") + end, +}) + +core.register_abm({ + label = "testabms:chance_5", + nodenames = "testabms:chance_5", + interval = 10, + chance = 5, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:chance_5 changed this node.") + end +}) + +-- ABM chance 20 node +core.register_node("testabms:chance_20", { + description = S("Node for test ABM chance_20"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:chance_20") + end, +}) + +core.register_abm({ + label = "testabms:chance_20", + nodenames = "testabms:chance_20", + interval = 10, + chance = 20, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:chance_20 changed this node.") + end +}) + diff --git a/data/games/garage/mods/testabms/init.lua b/data/games/garage/mods/testabms/init.lua new file mode 100644 index 0000000..8bf4975 --- /dev/null +++ b/data/games/garage/mods/testabms/init.lua @@ -0,0 +1,7 @@ +local path = core.get_modpath(core.get_current_modname()) + +dofile(path.."/after_node.lua") +dofile(path.."/chances.lua") +dofile(path.."/intervals.lua") +dofile(path.."/min_max.lua") +dofile(path.."/neighbors.lua") diff --git a/data/games/garage/mods/testabms/intervals.lua b/data/games/garage/mods/testabms/intervals.lua new file mode 100644 index 0000000..9284065 --- /dev/null +++ b/data/games/garage/mods/testabms/intervals.lua @@ -0,0 +1,56 @@ +-- test ABMs with different interval + +local S = core.get_translator("testnodes") + +-- ABM inteval 1 node +core.register_node("testabms:interval_1", { + description = S("Node for test ABM interval_1"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:interval_1") + end, +}) + +core.register_abm({ + label = "testabms:interval_1", + nodenames = "testabms:interval_1", + interval = 1, + chance = 1, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:interval_1 changed this node.") + end +}) + +-- ABM interval 60 node +core.register_node("testabms:interval_60", { + description = S("Node for test ABM interval_60"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:interval_60") + end, +}) + +core.register_abm({ + label = "testabms:interval_60", + nodenames = "testabms:interval_60", + interval = 60, + chance = 1, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:interval_60 changed this node.") + end +}) + diff --git a/data/games/garage/mods/testabms/min_max.lua b/data/games/garage/mods/testabms/min_max.lua new file mode 100644 index 0000000..b5df4e4 --- /dev/null +++ b/data/games/garage/mods/testabms/min_max.lua @@ -0,0 +1,58 @@ +-- test ABMs with min_y and max_y + +local S = core.get_translator("testnodes") + +-- ABM min_y node +core.register_node("testabms:min_y", { + description = S("Node for test ABM min_y."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:min_y at y "..pos.y.." with min_y = 0") + end, +}) + +core.register_abm({ + label = "testabms:min_y", + nodenames = "testabms:min_y", + interval = 10, + chance = 1, + min_y = 0, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:min_y changed this node.") + end +}) + +-- ABM max_y node +core.register_node("testabms:max_y", { + description = S("Node for test ABM max_y."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:max_y at y "..pos.y.." with max_y = 0") + end, +}) + +core.register_abm({ + label = "testabms:max_y", + nodenames = "testabms:max_y", + interval = 10, + chance = 1, + max_y = 0, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:max_y changed this node.") + end +}) + diff --git a/data/games/garage/mods/testabms/mod.conf b/data/games/garage/mods/testabms/mod.conf new file mode 100644 index 0000000..ad74cd2 --- /dev/null +++ b/data/games/garage/mods/testabms/mod.conf @@ -0,0 +1,2 @@ +name = testabms +description = Contains some nodes for test ABMs. diff --git a/data/games/garage/mods/testabms/neighbors.lua b/data/games/garage/mods/testabms/neighbors.lua new file mode 100644 index 0000000..0ce21c2 --- /dev/null +++ b/data/games/garage/mods/testabms/neighbors.lua @@ -0,0 +1,99 @@ +-- test ABMs with neighbor and without_neighbor + +local S = core.get_translator("testnodes") + +-- ABM required neighbor +core.register_node("testabms:required_neighbor", { + description = S("Node for test ABM required_neighbor.") .. "\n" + .. S("Sensitive neighbor node is testnodes:normal."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:required_neighbor " + .. "(normal drawtype testnode sensitive)") + end, +}) + +core.register_abm({ + label = "testabms:required_neighbor", + nodenames = "testabms:required_neighbor", + neighbors = {"testnodes:normal"}, + interval = 1, + chance = 1, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:required_neighbor changed this node.") + end +}) + +-- ABM missing neighbor node +core.register_node("testabms:missing_neighbor", { + description = S("Node for test ABM missing_neighbor.") .. "\n" + .. S("Sensitive neighbor node is testnodes:normal."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:missing_neighbor" + .. " (normal drawtype testnode sensitive)") + end, +}) + +core.register_abm({ + label = "testabms:missing_neighbor", + nodenames = "testabms:missing_neighbor", + without_neighbors = {"testnodes:normal"}, + interval = 1, + chance = 1, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:missing_neighbor changed this node.") + end +}) + +-- ABM required and missing neighbor node +core.register_node("testabms:required_missing_neighbor", { + description = S("Node for test ABM required_missing_neighbor.") .. "\n" + .. S("Sensitive neighbor nodes are testnodes:normal and testnodes:glasslike."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:required_missing_neighbor" + .. " (wint normal drawtype testnode and no glasslike" + .. " drawtype testnode sensitive)") + end, +}) + +core.register_abm({ + label = "testabms:required_missing_neighbor", + nodenames = "testabms:required_missing_neighbor", + neighbors = {"testnodes:normal"}, + without_neighbors = {"testnodes:glasslike"}, + interval = 1, + chance = 1, + action = function (pos) + core.swap_node(pos, {name="testabms:after_abm"}) + local meta = core.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:required_missing_neighbor changed this node.") + end +}) + diff --git a/data/games/garage/mods/testabms/textures/testabms_after_node.png b/data/games/garage/mods/testabms/textures/testabms_after_node.png new file mode 100644 index 0000000..dab8759 Binary files /dev/null and b/data/games/garage/mods/testabms/textures/testabms_after_node.png differ diff --git a/data/games/garage/mods/testabms/textures/testabms_wait_node.png b/data/games/garage/mods/testabms/textures/testabms_wait_node.png new file mode 100644 index 0000000..a9bd9a3 Binary files /dev/null and b/data/games/garage/mods/testabms/textures/testabms_wait_node.png differ diff --git a/data/games/garage/mods/testentities/armor.lua b/data/games/garage/mods/testentities/armor.lua new file mode 100644 index 0000000..95351ab --- /dev/null +++ b/data/games/garage/mods/testentities/armor.lua @@ -0,0 +1,61 @@ +-- Armorball: Test entity for testing armor groups +-- Rightclick to change armor group + +local phasearmor = { + [0]={icy=100}, + [1]={fiery=100}, + [2]={icy=100, fiery=100}, + [3]={fleshy=-100}, + [4]={fleshy=1}, + [5]={fleshy=10}, + [6]={fleshy=50}, + [7]={fleshy=100}, + [8]={fleshy=200}, + [9]={fleshy=1000}, + [10]={fleshy=32767}, + [11]={immortal=1}, + [12]={punch_operable=1}, +} +local max_phase = 12 + +core.register_entity("testentities:armorball", { + initial_properties = { + hp_max = 20, + physical = false, + collisionbox = {-0.4,-0.4,-0.4, 0.4,0.4,0.4}, + visual = "sprite", + visual_size = {x=1, y=1}, + textures = {"testentities_armorball.png"}, + spritediv = {x=1, y=max_phase+1}, + initial_sprite_basepos = {x=0, y=0}, + }, + + _phase = 7, + + on_activate = function(self, staticdata) + core.log("action", "[testentities] armorball.on_activate") + self.object:set_armor_groups(phasearmor[self._phase]) + self.object:set_sprite({x=0, y=self._phase}) + end, + + on_rightclick = function(self, clicker) + -- Change armor group and sprite + self._phase = self._phase + 1 + if self._phase >= max_phase + 1 then + self._phase = 0 + end + self.object:set_sprite({x=0, y=self._phase}) + self.object:set_armor_groups(phasearmor[self._phase]) + end, + + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + if not puncher then + return + end + local name = puncher:get_player_name() + if not name then + return + end + core.chat_send_player(name, "time_from_last_punch="..string.format("%.3f", time_from_last_punch).."; damage="..tostring(damage)) + end, +}) diff --git a/data/games/garage/mods/testentities/init.lua b/data/games/garage/mods/testentities/init.lua new file mode 100644 index 0000000..4ad8557 --- /dev/null +++ b/data/games/garage/mods/testentities/init.lua @@ -0,0 +1,5 @@ +dofile(core.get_modpath("testentities").."/visuals.lua") +dofile(core.get_modpath("testentities").."/observers.lua") +dofile(core.get_modpath("testentities").."/selectionbox.lua") +dofile(core.get_modpath("testentities").."/armor.lua") +dofile(core.get_modpath("testentities").."/pointable.lua") diff --git a/data/games/garage/mods/testentities/mod.conf b/data/games/garage/mods/testentities/mod.conf new file mode 100644 index 0000000..7a8cb5a --- /dev/null +++ b/data/games/garage/mods/testentities/mod.conf @@ -0,0 +1,2 @@ +name = testentities +description = Example entities for testing diff --git a/data/games/garage/mods/testentities/models/LICENSE.txt b/data/games/garage/mods/testentities/models/LICENSE.txt new file mode 100644 index 0000000..4317d68 --- /dev/null +++ b/data/games/garage/mods/testentities/models/LICENSE.txt @@ -0,0 +1,7 @@ +Original model by MirceaKitsune (CC BY-SA 3.0). +Various alterations and fixes by kilbith, sofar, xunto, Rogier-5, TeTpaAka, Desour, +stujones11, An0n3m0us (CC BY-SA 3.0): + testentities_sam.b3d + +Jordach (CC BY-SA 3.0): + testentities_sam.png diff --git a/data/games/garage/mods/testentities/models/testentities_sam.b3d b/data/games/garage/mods/testentities/models/testentities_sam.b3d new file mode 100644 index 0000000..3e0827e Binary files /dev/null and b/data/games/garage/mods/testentities/models/testentities_sam.b3d differ diff --git a/data/games/garage/mods/testentities/models/testentities_sam.png b/data/games/garage/mods/testentities/models/testentities_sam.png new file mode 100644 index 0000000..0502178 Binary files /dev/null and b/data/games/garage/mods/testentities/models/testentities_sam.png differ diff --git a/data/games/garage/mods/testentities/observers.lua b/data/games/garage/mods/testentities/observers.lua new file mode 100644 index 0000000..c7e95df --- /dev/null +++ b/data/games/garage/mods/testentities/observers.lua @@ -0,0 +1,37 @@ +local function player_names_excluding(exclude_player_name) + local player_names = {} + for _, player in ipairs(core.get_connected_players()) do + player_names[player:get_player_name()] = true + end + player_names[exclude_player_name] = nil + return player_names +end + +core.register_entity("testentities:observable", { + initial_properties = { + visual = "sprite", + textures = { "testentities_sprite.png" }, + static_save = false, + infotext = "Punch to set observers to anyone but you" + }, + on_activate = function(self) + self.object:set_armor_groups({punch_operable = 1}) + assert(self.object:get_observers() == nil) + -- Using a value of `false` in the table should error. + assert(not pcall(self.object, self.object.set_observers, self.object, {test = false})) + end, + on_punch = function(self, puncher) + local puncher_name = puncher:get_player_name() + local observers = player_names_excluding(puncher_name) + self.object:set_observers(observers) + local got_observers = self.object:get_observers() + for name in pairs(observers) do + assert(got_observers[name]) + end + for name in pairs(got_observers) do + assert(observers[name]) + end + self.object:set_properties({infotext = "Excluding " .. puncher_name}) + return true + end +}) diff --git a/data/games/garage/mods/testentities/pointable.lua b/data/games/garage/mods/testentities/pointable.lua new file mode 100644 index 0000000..e23dcc5 --- /dev/null +++ b/data/games/garage/mods/testentities/pointable.lua @@ -0,0 +1,23 @@ +-- Pointability test Entities + +-- Register wrapper for compactness +local function register_pointable_testentity(name, pointable) + local texture = "testnodes_"..name..".png" + core.register_entity("testentities:"..name, { + initial_properties = { + visual = "cube", + visual_size = {x = 0.6, y = 0.6, z = 0.6}, + textures = { + texture, texture, texture, texture, texture, texture + }, + pointable = pointable, + }, + on_activate = function(self) + self.object:set_armor_groups({[name.."_test"] = 1}) + end + }) +end + +register_pointable_testentity("pointable", true) +register_pointable_testentity("not_pointable", false) +register_pointable_testentity("blocking_pointable", "blocking") diff --git a/data/games/garage/mods/testentities/selectionbox.lua b/data/games/garage/mods/testentities/selectionbox.lua new file mode 100644 index 0000000..8885a2a --- /dev/null +++ b/data/games/garage/mods/testentities/selectionbox.lua @@ -0,0 +1,96 @@ +local function color(hex) + return ("blank.png^[noalpha^[colorize:#%06X:255"):format(hex) +end + +local function random_color() + return color(math.random(0, 0xFFFFFF)) +end + +local function random_rotation() + return 2 * math.pi * vector.new(math.random(), math.random(), math.random()) +end + +local active_selectionbox_entities = 0 -- count active entities + +core.register_entity("testentities:selectionbox", { + initial_properties = { + visual = "cube", + infotext = "Punch to randomize rotation, rightclick to toggle rotation" + }, + on_activate = function(self) + active_selectionbox_entities = active_selectionbox_entities + 1 + + local w, h, l = math.random(), math.random(), math.random() + self.object:set_properties({ + textures = {random_color(), random_color(), random_color(), random_color(), random_color(), random_color()}, + selectionbox = {rotate = true, -w/2, -h/2, -l/2, w/2, h/2, l/2}, + visual_size = vector.new(w, h, l), + automatic_rotate = 2 * math.pi * (math.random() - 0.5) + }) + assert(self.object:get_properties().selectionbox.rotate) + self.object:set_armor_groups({punch_operable = 1}) + self.object:set_rotation(random_rotation()) + end, + on_deactivate = function() + active_selectionbox_entities = active_selectionbox_entities - 1 + end, + on_punch = function(self) + self.object:set_rotation(random_rotation()) + end, + on_rightclick = function(self) + self.object:set_properties({ + automatic_rotate = self.object:get_properties().automatic_rotate == 0 and 2 * math.pi * (math.random() - 0.5) or 0 + }) + end +}) + +local hud_ids = {} +core.register_globalstep(function() + if active_selectionbox_entities == 0 then + return + end + + for _, player in pairs(core.get_connected_players()) do + local offset = player:get_eye_offset() + offset.y = offset.y + player:get_properties().eye_height + local pos1 = vector.add(player:get_pos(), offset) + local raycast = core.raycast(pos1, vector.add(pos1, vector.multiply(player:get_look_dir(), 10)), true, false) + local pointed_thing = raycast() + if pointed_thing.ref == player then + pointed_thing = raycast() + end + local remove_hud_element = true + local pname = player:get_player_name() + local hud_id = hud_ids[pname] + if pointed_thing and pointed_thing.type == "object" then + local ent = pointed_thing.ref:get_luaentity() + if ent and ent.name == "testentities:selectionbox" then + hud_ids[pname] = hud_id or player:hud_add({ + type = "text", -- See HUD element types + position = {x=0.5, y=0.5}, + text = "X", + number = 0xFF0000, + alignment = {x=0, y=0}, + }) + local shade = math.random(0, 0xFF) + core.add_particle({ + -- Random shade of red for the intersection point + texture = color(0x10000 * shade), + pos = pointed_thing.intersection_point, + size = 0.1 + }) + core.add_particle({ + -- Same shade of green for the corresponding intersection normal + texture = color(0x100 * shade), + pos = vector.add(pointed_thing.intersection_point, pointed_thing.intersection_normal * 0.1), + size = 0.1 + }) + remove_hud_element = false + end + end + if remove_hud_element and hud_id then + player:hud_remove(hud_id) + hud_ids[pname] = nil + end + end +end) diff --git a/data/games/garage/mods/testentities/textures/testentities_armorball.png b/data/games/garage/mods/testentities/textures/testentities_armorball.png new file mode 100644 index 0000000..566dbcf Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_armorball.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube1.png b/data/games/garage/mods/testentities/textures/testentities_cube1.png new file mode 100644 index 0000000..c667e42 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube1.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube2.png b/data/games/garage/mods/testentities/textures/testentities_cube2.png new file mode 100644 index 0000000..4818234 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube2.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube3.png b/data/games/garage/mods/testentities/textures/testentities_cube3.png new file mode 100644 index 0000000..03b5daa Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube3.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube4.png b/data/games/garage/mods/testentities/textures/testentities_cube4.png new file mode 100644 index 0000000..6392048 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube4.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube5.png b/data/games/garage/mods/testentities/textures/testentities_cube5.png new file mode 100644 index 0000000..d8acdf0 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube5.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_cube6.png b/data/games/garage/mods/testentities/textures/testentities_cube6.png new file mode 100644 index 0000000..5f81a64 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_cube6.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_dungeon_master.png b/data/games/garage/mods/testentities/textures/testentities_dungeon_master.png new file mode 100644 index 0000000..1e31077 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_dungeon_master.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_sprite.png b/data/games/garage/mods/testentities/textures/testentities_sprite.png new file mode 100644 index 0000000..a4b0196 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_sprite.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_upright_sprite1.png b/data/games/garage/mods/testentities/textures/testentities_upright_sprite1.png new file mode 100644 index 0000000..6242511 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_upright_sprite1.png differ diff --git a/data/games/garage/mods/testentities/textures/testentities_upright_sprite2.png b/data/games/garage/mods/testentities/textures/testentities_upright_sprite2.png new file mode 100644 index 0000000..a79a760 Binary files /dev/null and b/data/games/garage/mods/testentities/textures/testentities_upright_sprite2.png differ diff --git a/data/games/garage/mods/testentities/visuals.lua b/data/games/garage/mods/testentities/visuals.lua new file mode 100644 index 0000000..b61b5e8 --- /dev/null +++ b/data/games/garage/mods/testentities/visuals.lua @@ -0,0 +1,150 @@ +-- Minimal test entities to test visuals + +core.register_entity("testentities:sprite", { + initial_properties = { + visual = "sprite", + textures = { "testentities_sprite.png" }, + }, +}) + +core.register_entity("testentities:upright_sprite", { + initial_properties = { + visual = "upright_sprite", + textures = { + "testentities_upright_sprite1.png", + "testentities_upright_sprite2.png", + }, + }, +}) + +core.register_entity("testentities:cube", { + initial_properties = { + visual = "cube", + textures = { + "testentities_cube1.png", + "testentities_cube2.png", + "testentities_cube3.png", + "testentities_cube4.png", + "testentities_cube5.png", + "testentities_cube6.png", + }, + }, +}) + +core.register_entity("testentities:item", { + initial_properties = { + visual = "item", + wield_item = "testnodes:normal", + }, +}) + +core.register_entity("testentities:wielditem", { + initial_properties = { + visual = "wielditem", + wield_item = "testnodes:normal", + }, +}) + +core.register_entity("testentities:mesh", { + initial_properties = { + visual = "mesh", + mesh = "testnodes_pyramid.obj", + textures = { + "testnodes_mesh_stripes2.png" + }, + }, +}) + +core.register_entity("testentities:mesh_unshaded", { + initial_properties = { + visual = "mesh", + mesh = "testnodes_pyramid.obj", + textures = { + "testnodes_mesh_stripes2.png" + }, + shaded = false, + }, +}) + +core.register_entity("testentities:sam", { + initial_properties = { + visual = "mesh", + mesh = "testentities_sam.b3d", + textures = { + "testentities_sam.png" + }, + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 219}, 30, 0, true) + end, +}) + +-- Advanced visual tests + +-- An entity for testing animated and yaw-modulated sprites +core.register_entity("testentities:yawsprite", { + initial_properties = { + selectionbox = {-0.3, -0.5, -0.3, 0.3, 0.3, 0.3}, + visual = "sprite", + visual_size = {x=0.6666, y=1}, + textures = {"testentities_dungeon_master.png^[makealpha:128,0,0^[makealpha:128,128,0"}, + spritediv = {x=6, y=5}, + initial_sprite_basepos = {x=0, y=0}, + }, + on_activate = function(self, staticdata) + self.object:set_sprite({x=0, y=0}, 3, 0.5, true) + end, +}) + +-- An entity for testing animated upright sprites +core.register_entity("testentities:upright_animated", { + initial_properties = { + visual = "upright_sprite", + textures = {"testnodes_anim.png"}, + spritediv = {x = 1, y = 4}, + }, + on_activate = function(self) + self.object:set_sprite({x=0, y=0}, 4, 1.0, false) + end, +}) + +core.register_entity("testentities:nametag", { + initial_properties = { + visual = "sprite", + textures = { "testentities_sprite.png" }, + }, + + on_activate = function(self, staticdata) + if staticdata ~= "" then + local data = core.deserialize(staticdata) + self.color = data.color + self.bgcolor = data.bgcolor + else + self.color = { + r = math.random(0, 255), + g = math.random(0, 255), + b = math.random(0, 255), + } + + if math.random(0, 10) > 5 then + self.bgcolor = { + r = math.random(0, 255), + g = math.random(0, 255), + b = math.random(0, 255), + a = math.random(0, 255), + } + end + end + + assert(self.color) + self.object:set_properties({ + nametag = tostring(math.random(1000, 10000)), + nametag_color = self.color, + nametag_bgcolor = self.bgcolor, + }) + end, + + get_staticdata = function(self) + return core.serialize({ color = self.color, bgcolor = self.bgcolor }) + end, +}) diff --git a/data/games/garage/mods/testfood/init.lua b/data/games/garage/mods/testfood/init.lua new file mode 100644 index 0000000..0a264a3 --- /dev/null +++ b/data/games/garage/mods/testfood/init.lua @@ -0,0 +1,35 @@ +local S = core.get_translator("testfood") + +core.register_craftitem("testfood:good1", { + description = S("Good Food (+1)").."\n".. + S("Punch: Eat"), + inventory_image = "testfood_good.png", + on_use = core.item_eat(1), +}) +core.register_craftitem("testfood:good5", { + description = S("Good Food (+5)").."\n".. + S("Punch: Eat"), + inventory_image = "testfood_good2.png", + on_use = core.item_eat(5), +}) + +core.register_craftitem("testfood:bad1", { + description = S("Bad Food (-1)").."\n".. + S("Punch: Eat"), + inventory_image = "testfood_bad.png", + on_use = core.item_eat(-1), +}) +core.register_craftitem("testfood:bad5", { + description = S("Bad Food (-5)").."\n".. + S("Punch: Eat"), + inventory_image = "testfood_bad2.png", + on_use = core.item_eat(-5), +}) + +core.register_craftitem("testfood:replace1", { + description = S("Replacing Food (+1)").."\n".. + S("Punch: Eat and replace with 'Good Food (+1)'"), + inventory_image = "testfood_replace.png", + on_use = core.item_eat(1, "testfood:good1"), +}) + diff --git a/data/games/garage/mods/testfood/mod.conf b/data/games/garage/mods/testfood/mod.conf new file mode 100644 index 0000000..7bff21b --- /dev/null +++ b/data/games/garage/mods/testfood/mod.conf @@ -0,0 +1,2 @@ +name = testfood +description = For testing food items diff --git a/data/games/garage/mods/testfood/textures/testfood_bad.png b/data/games/garage/mods/testfood/textures/testfood_bad.png new file mode 100644 index 0000000..6e92514 Binary files /dev/null and b/data/games/garage/mods/testfood/textures/testfood_bad.png differ diff --git a/data/games/garage/mods/testfood/textures/testfood_bad2.png b/data/games/garage/mods/testfood/textures/testfood_bad2.png new file mode 100644 index 0000000..22b5678 Binary files /dev/null and b/data/games/garage/mods/testfood/textures/testfood_bad2.png differ diff --git a/data/games/garage/mods/testfood/textures/testfood_good.png b/data/games/garage/mods/testfood/textures/testfood_good.png new file mode 100644 index 0000000..31df7f5 Binary files /dev/null and b/data/games/garage/mods/testfood/textures/testfood_good.png differ diff --git a/data/games/garage/mods/testfood/textures/testfood_good2.png b/data/games/garage/mods/testfood/textures/testfood_good2.png new file mode 100644 index 0000000..e43dda2 Binary files /dev/null and b/data/games/garage/mods/testfood/textures/testfood_good2.png differ diff --git a/data/games/garage/mods/testfood/textures/testfood_replace.png b/data/games/garage/mods/testfood/textures/testfood_replace.png new file mode 100644 index 0000000..1ef6876 Binary files /dev/null and b/data/games/garage/mods/testfood/textures/testfood_replace.png differ diff --git a/data/games/garage/mods/testformspec/LICENSE.txt b/data/games/garage/mods/testformspec/LICENSE.txt new file mode 100644 index 0000000..07696cc --- /dev/null +++ b/data/games/garage/mods/testformspec/LICENSE.txt @@ -0,0 +1,14 @@ +License of media files +---------------------- +Content imported from minetest_game. + + +BlockMen (CC BY-SA 3.0) + default_chest_front.png + default_chest_lock.png + default_chest_side.png + default_chest_top.png + +stujones11 (CC BY-SA 3.0) +An0n3m0us (CC BY-SA 3.0) + testformspec_character.b3d diff --git a/data/games/garage/mods/testformspec/callbacks.lua b/data/games/garage/mods/testformspec/callbacks.lua new file mode 100644 index 0000000..a4df232 --- /dev/null +++ b/data/games/garage/mods/testformspec/callbacks.lua @@ -0,0 +1,51 @@ +local callback_test = 0 + +local out = function(player, formname, fields, number) + local snum = "" + if number then + snum = " "..number + end + local msg = "Formspec callback"..snum..": player="..player:get_player_name()..", formname=\""..tostring(formname).."\", fields="..dump(fields) + core.chat_send_player(player:get_player_name(), msg) + core.log("action", msg) +end + +core.register_on_player_receive_fields(function(player, formname, fields) + if callback_test == 1 then + out(player, formname, fields) + elseif callback_test == 2 then + out(player, formname, fields, 1) + end +end) +core.register_on_player_receive_fields(function(player, formname, fields) + if callback_test == 2 then + out(player, formname, fields, 2) + return true -- Disable the first callback + end +end) +core.register_on_player_receive_fields(function(player, formname, fields) + if callback_test == 2 then + out(player, formname, fields, 3) + end +end) + +core.register_chatcommand("test_formspec_callbacks", { + params = "[ 0 | 1 | 2 ]", + description = "Test: Change formspec callbacks testing mode", + func = function(name, param) + local mode = tonumber(param) + if not mode then + callback_test = (callback_test + 1 % 3) + else + callback_test = mode + end + if callback_test == 1 then + core.chat_send_player(name, "Formspec callback test mode 1 enabled: Logging only") + elseif callback_test == 2 then + core.chat_send_player(name, "Formspec callback test mode 2 enabled: Three callbacks, disable pre-registered callbacks") + else + callback_test = 0 + core.chat_send_player(name, "Formspec callback test disabled!") + end + end +}) diff --git a/data/games/garage/mods/testformspec/dummy_items.lua b/data/games/garage/mods/testformspec/dummy_items.lua new file mode 100644 index 0000000..50fb989 --- /dev/null +++ b/data/games/garage/mods/testformspec/dummy_items.lua @@ -0,0 +1,14 @@ +-- This code adds dummy items that are supposed to be used in formspecs +-- for testing item_image formspec elements. + +core.register_node("testformspec:node", { + description = "Formspec Test Node", + tiles = { "testformspec_node.png" }, + groups = { dig_immediate = 3, dummy = 1 }, +}) + +core.register_craftitem("testformspec:item", { + description = "Formspec Test Item", + inventory_image = "testformspec_item.png", + groups = { dummy = 1 }, +}) diff --git a/data/games/garage/mods/testformspec/formspec.lua b/data/games/garage/mods/testformspec/formspec.lua new file mode 100644 index 0000000..29014f1 --- /dev/null +++ b/data/games/garage/mods/testformspec/formspec.lua @@ -0,0 +1,591 @@ +local color = core.colorize + +-- \208\176 is a cyrillic small a +local unsafe_url = core.formspec_escape("https://u:p@wikipedi\208\176.org:1233/heIIoll?a=b#c") + +local clip_fs = [[ + style_type[label,button,image_button,item_image_button, + tabheader,scrollbar,table,animated_image + ,field,textarea,checkbox,dropdown;noclip=%c] + + label[0,0;A clipping test] + button_url[0,1;3,0.8;clip_button;A clipping test;]] .. unsafe_url .. [[] + image_button[0,2;3,0.8;testformspec_button_image.png;clip_image_button;A clipping test] + item_image_button[0,3;3,0.8;testformspec:item;clip_item_image_button;A clipping test] + tabheader[0,4.7;3,0.63;clip_tabheader;Clip,Test,Text,Tabs;1;false;false] + field[0,5;3,0.8;clip_field;Title;] + textarea[0,6;3,1;clip_textarea;Title;] + checkbox[0,7.5;clip_checkbox;This is a test;true] + dropdown[0,8;3,0.8;clip_dropdown;Select An Item,One,Two,Three,Four,Five;1] + scrollbar[0,9;3,0.8;horizontal;clip_scrollbar;3] + tablecolumns[text;text] + table[0,10;3,1;clip_table;one,two,three,four;1] + animated_image[-0.5,11;4.5,1;clip_animated_image;testformspec_animation.png;4;100] +]] + +local tabheaders_fs = [[ + tabheader[0,0;10,0.63;tabs_opaque;Opaque,Without,Border;1;false;false] + tabheader[0,1;10,0.63;tabs_opaque_border;Opaque,With,Border;1;false;true] + tabheader[0,2;10,0.63;tabs_transparent;Transparent,Without,Border;1;true;false] + tabheader[0,3;10,0.63;tabs_transparent_border;Transparent,With,Border;1;true;true] + tabheader[0,4;tabs_default;Default,Tabs;1] + tabheader[0,6;10,0.5;tabs_size1;Height=0.5;1;false;false] + tabheader[2,6;10,0.75;tabs_size1;Height=0.75;1;false;false] + tabheader[4,6;10,1;tabs_size2;Height=1;1;false;false] + tabheader[6,6;10,1.25;tabs_size2;Height=1.25;1;false;false] + tabheader[8,6;10,1.5;tabs_size2;Height=1.5;1;false;false] +]] + +local inv_style_fs = [[ + style_type[list;noclip=true] + list[current_player;main;-0.75,0.75;2,2] + + real_coordinates[false] + list[current_player;main;1.5,0;3,2] + real_coordinates[true] + + real_coordinates[false] + style_type[list;size=1.1;spacing=0.1] + list[current_player;main;5,0;3,2] + real_coordinates[true] + + style_type[list;size=.001;spacing=0] + list[current_player;main;7,3.5;8,4] + + box[3,3.5;1,1;#000000] + box[5,3.5;1,1;#000000] + box[4,4.5;1,1;#000000] + box[3,5.5;1,1;#000000] + box[5,5.5;1,1;#000000] + style_type[list;spacing=.25,.125;size=.75,.875] + list[current_player;main;3,3.5;3,3] + + style_type[list;spacing=0;size=1.1] + list[current_player;main;.5,7;8,4] +]] + +-- Some textures from textures/base/pack and Devtest, with many different sizes +-- and aspect ratios. +local image_column = "image,0=logo.png,1=crack_anylength.png^[invert:rgb,2=checkbox_16.png," .. + "3=checkbox_32.png,4=checkbox_64.png,5=default_lava.png," .. + "6=progress_bar.png,7=progress_bar_bg.png" +local words = { + "esciunt", "repudiandae", "repellat", "voluptatem", "autem", "vitae", "et", + "minima", "quasi", "facere", "nihil", "ea", "nemo", "rem", "non", "eos", + "laudantium", "eveniet", "veritatis", +} + +local reseed = math.random(2^31-1) +math.randomseed(1337) + +local table_content = {} +for i = 1, 100 do + table.insert(table_content, words[math.random(#words)]) + table.insert(table_content, words[math.random(#words)]) + table.insert(table_content, words[math.random(#words)]) + table.insert(table_content, math.random(0, 7)) + table.insert(table_content, math.random(0, 7)) + table.insert(table_content, math.random(0, 7)) + table.insert(table_content, words[math.random(#words)]) +end + +math.randomseed(reseed) + +local table_fs = table.concat({ + "tablecolumns[text,align=left;text,align=right;text,align=center;", + image_column, ",align=left;", + image_column, ",align=right;", + image_column, ",align=center;text,align=right]", + "table[0,0;17,12;the_table;", table.concat(table_content, ","), ";1]" +}) + +local hypertext_basic = [[A hypertext element +Normal test +This is a normal text. + +style test + + . + + +Tag test +normal +mono +bold +italic +underlined +big +bigger +left +
center
+right +justify. Here comes a blind text: Lorem testum dolor sit amet consecutor celeron fiftifahivus e shadoninia e smalus jokus anrus relsocutoti rubenwardus. Erasputinus hara holisti dominus wusi. Grumarinsti erltusmuate ol fortitusti fla flo, blani burki e sfani fahif. Ultae ratii, e megus gigae don anonimus. Grinus dimondus krockus e nore. Endus finalus nowus comus endus o blindus tekstus. + +Custom tag test + + + + + +color=green +Action: color=green +Action: hovercolor=yellow +Action URL: open URL +size=24 +font=mono +color=green font=mono size=24 + +action test +action + +img test +Normal: + +width=48 height=48: + +float=left: + +float=right: + + +item test +Normal: + +width=48 height=48 + +angle=30,0,0: + +angle=0,30,0: + +angle=0,0,30: + +rotate=yes: + +rotate=100,0,0: + +rotate=0,100,0: + +rotate=0,0,100: + +rotate=50,75,100: + +angle=-30,-45,90 rotate=100,150,-50: +]] + +local hypertext_global = [[ + +This is a test of the global tag. The parameters are: +background=gray margin=20 valign=bottom halign=right color=pink hovercolor=purple size=12 font=mono +action]] + +local hypertext_fs = "hypertext[0,0;11,9;hypertext;"..core.formspec_escape(hypertext_basic).."]".. + "hypertext[0,9.5;11,2.5;hypertext;"..core.formspec_escape(hypertext_global).."]" + +local style_fs = [[ + style[one_btn1;bgcolor=red;textcolor=yellow;bgcolor_hovered=orange; + bgcolor_pressed=purple] + button_url_exit[0,0;2.5,0.8;one_btn1;Button;]] .. unsafe_url .. [[] + + style[one_btn2;border=false;textcolor=cyan] ]].. + "button[0,1.05;2.5,0.8;one_btn2;Text " .. color("#FF0", "Yellow") .. [[] + + style[one_btn3;bgimg=testformspec_button_image.png;bgimg_hovered=testformspec_hovered.png; + bgimg_pressed=testformspec_pressed.png] + button[0,2.1;1,1;one_btn3;Border] + + style[one_btn4;bgimg=testformspec_button_image.png;bgimg_hovered=testformspec_hovered.png; + bgimg_pressed=testformspec_pressed.png;border=false] + button[1.25,2.1;1,1;one_btn4;NoBor] + + style[one_btn5;bgimg=testformspec_button_image.png;bgimg_hovered=testformspec_hovered.png; + bgimg_pressed=testformspec_pressed.png;border=false;alpha=false] + button[0,3.35;1,1;one_btn5;Alph] + + style[one_btn6;border=true] + image_button[0,4.6;1,1;testformspec_button_image.png;one_btn6;Border] + + style[one_btn7;border=false] + image_button[1.25,4.6;1,1;testformspec_button_image.png;one_btn7;NoBor] + + style[one_btn8;border=false] + image_button[0,5.85;1,1;testformspec_button_image.png;one_btn8;Border;false;true;testformspec_pressed.png] + + style[one_btn9;border=true] + image_button[1.25,5.85;1,1;testformspec_button_image.png;one_btn9;NoBor;false;false;testformspec_pressed.png] + + style[one_btn10;alpha=false] + image_button[0,7.1;1,1;testformspec_button_image.png;one_btn10;NoAlpha] + + style[one_btn11;alpha=true] + image_button[1.25,7.1;1,1;testformspec_button_image.png;one_btn11;Alpha] + + style[one_btn12;border=true] + item_image_button[0,8.35;1,1;testformspec:item;one_btn12;Border] + + style[one_btn13;border=false] + item_image_button[1.25,8.35;1,1;testformspec:item;one_btn13;NoBor] + + style[one_btn14;border=false;bgimg=testformspec_bg.png;fgimg=testformspec_button_image.png] + style[one_btn14:hovered;bgimg=testformspec_bg_hovered.png;fgimg=testformspec_hovered.png;textcolor=yellow] + style[one_btn14:pressed;bgimg=testformspec_bg_pressed.png;fgimg=testformspec_pressed.png;textcolor=blue] + style[one_btn14:hovered+pressed;textcolor=purple] + style[one_btn14:focused;textcolor=red] + image_button[0,9.6;1,1;testformspec_button_image.png;one_btn14;Bg] + + style[one_btn15;border=false;bgcolor=#1cc;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png] + item_image_button[1.25,9.6;1,1;testformspec:item;one_btn15;Bg] + + style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_middle=4,6;padding=5,7;fgimg=testformspec_bg.png;fgimg_middle=1] + style[one_btn16:hovered;bgimg=testformspec_bg_9slice_hovered.png;fgimg=testformspec_bg_hovered.png] + style[one_btn16:pressed;bgimg=testformspec_bg_9slice_pressed.png;fgimg=testformspec_bg_pressed.png] + style[one_btn16:focused;bgimg=testformspec_bg_9slice_focused.png;fgimg=testformspec_bg_focused.png] + image_button[2.5,9.6;2,1;;one_btn16;9-Slice Bg] + + + container[2.75,0] + + style[one_tb1;textcolor=Yellow] + tabheader[0,3;2.5,0.63;one_tb1;Yellow,Text,Tabs;1;false;false] + + style[one_f1;textcolor=yellow] + field[0,4.25;2.5,0.8;one_f1;Field One;Yellow Text] + + style[one_f2;border=false;textcolor=cyan] + field[0,5.75;2.5,0.8;one_f2;Field Two;Borderless Cyan Text] + + style[one_f3;textcolor=yellow] + textarea[0,7.025;2.5,0.8;one_f3;Label;]] .. + core.formspec_escape("Yellow Text\nLine two") .. [[ ] + + style[one_f4;border=false;textcolor=cyan] + textarea[0,8.324999999999999;2.5,0.8;one_f4;Label;]] .. + core.formspec_escape("Borderless Cyan Text\nLine two") .. [[ ] + + container_end[] +]] + +local scroll_fs = + "button[8.5,1;4,1;outside;Outside of container]".. + "box[1,1;8,6;#00aa]".. + "scroll_container[1,1;8,6;scrbar;vertical]".. + "button[0,1;1,1;lorem;Lorem]".. + "animated_image[0,1;4.5,1;clip_animated_image;testformspec_animation.png;4;100]" .. + "button[0,10;1,1;ipsum;Ipsum]".. + "pwdfield[2,2;1,1;lorem2;Lorem]".. + "list[current_player;main;4,4;1,5;]".. + "box[2,5;3,2;#ffff00]".. + "image[1,10;3,2;testformspec_item.png]".. + "image[3,1;testformspec_item.png]".. + "item_image[2,6;3,2;testformspec:node]".. + "label[2,15;bla Bli\nfoo bar]".. + "item_image_button[2,3;1,1;testformspec:node;itemimagebutton;ItemImageButton]".. + "tooltip[0,11;3,2;Buz;#f00;#000]".. + "box[0,11;3,2;#00ff00]".. + "hypertext[3,13;3,3;;" .. hypertext_basic .. "]" .. + "hypertext[3,17;3,3;;Hypertext with no scrollbar\\; the scroll container should scroll.]" .. + "textarea[3,21;3,1;textarea;;More scroll within scroll]" .. + "container[0,18]".. + "box[1,2;3,2;#0a0a]".. + "scroll_container[1,2;3,2;scrbar2;horizontal;0.06]".. + "button[0,0;6,1;butnest;Nest]".. + "label[10,0.5;nest]".. + "scroll_container_end[]".. + "scrollbar[1,0;3.5,0.3;horizontal;scrbar2;0]".. + "container_end[]".. + "dropdown[0,6;2;hmdrpdwn;apple,bulb;1]".. + "image_button[0,4;2,2;testformspec_button_image.png;imagebutton;bbbbtt;false;true;testformspec_pressed.png]".. + "box[1,22.5;4,1;#a00a]".. + "scroll_container_end[]".. + "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height) + "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]".. + "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]".. + "dropdown[0,6;2;hmdrpdwnnn;Outside,of,container;1]".. + "scroll_container[0,8;10,4;scrbar420;vertical;0.1;2]".. + "button[0.5,0.5;10,1;;Container with padding=2]".. + "list[current_player;main;0,5;8,4;]".. + "scroll_container_end[]".. + "scrollbar[10.1,8;0.5,4;vertical;scrbar420;0]".. + -- Buttons for scale comparison + "button[11,8;1,1;;0]".. + "button[11,9;1,1;;1]".. + "button[11,10;1,1;;2]".. + "button[11,11;1,1;;3]".. + "button[11,12;1,1;;4]" + +--style_type[label;textcolor=green] +--label[0,0;Green] +--style_type[label;textcolor=blue] +--label[0,1;Blue] +--style_type[label;textcolor=;border=true] +--label[1.2,0;Border] +--style_type[label;border=true;bgcolor=red] +--label[1.2,1;Background] +--style_type[label;border=;bgcolor=] +--label[0.75,2;Reset] + +local window = { + sizex = 12, + sizey = 13, + positionx = 0.5, + positiony = 0.5, + anchorx = 0.5, + anchory = 0.5, + paddingx = 0.05, + paddingy = 0.05 +} + +local pages = { + -- Real Coordinates + [[ + formspec_version[3] + size[12,13] + image_button[0,0;1,1;logo.png;rc_image_button_1x1;1x1] + image_button[1,0;2,2;logo.png;rc_image_button_2x2;2x2] + button[0,2;1,1;rc_button_1x1;1x1] + button[1,2;2,2;rc_button_2x2;2x2] + item_image[0,4;1,1;air] + item_image[1,4;2,2;air] + item_image_button[0,6;1,1;testformspec:node;rc_item_image_button_1x1;1x1] + item_image_button[1,6;2,2;testformspec:node;rc_item_image_button_2x2;2x2] + field[3,.5;3,.5;rc_field;Field;text] + pwdfield[6,.5;3,1;rc_pwdfield;Password Field] + field[3,1;3,1;;Read-Only Field;text] + textarea[3,2;3,.5;rc_textarea_small;Textarea;text] + textarea[6,2;3,2;rc_textarea_big;Textarea;text\nmore text] + textarea[3,3;3,1;;Read-Only Textarea;text\nmore text] + textlist[3,4;3,2;rc_textlist;Textlist,Perfect Coordinates;1;false] + tableoptions[highlight=#ABCDEF75;background=#00000055;border=false] + table[6,4;3,2;rc_table;Table,Cool Stuff,Foo,Bar;2] + dropdown[3,6;3,1;rc_dropdown_small;This,is,a,dropdown;1] + dropdown[6,6;3,2;rc_dropdown_big;I,am,a,bigger,dropdown;5] + image[0,8;3,2;ignore.png] + box[3,7;3,1;#00A3FF] + checkbox[3,8;rc_checkbox_1;Check me!;false] + checkbox[3,9;rc_checkbox_2;Uncheck me now!;true] + scrollbar[0,11.5;11.5,.5;horizontal;rc_scrollbar_horizontal;500] + scrollbar[11.5,0;.5,11.5;vertical;rc_scrollbar_vertical;0] + list[current_player;main;6,8;3,2;1] + button[9,0;2.5,1;rc_empty_button_1;] + button[9,1;2.5,1;rc_empty_button_2;] + button[9,2;2.5,1;rc_empty_button_3;] ]].. + "label[9,0.5;This is a label.\nLine\nLine\nLine\nEnd]".. + [[button[9,3;1,1;rc_empty_button_4;] + vertlabel[9,4;VERT] + label[10,3;HORIZ] + tabheader[8,0;6,0.65;rc_tabheader;Tab 1,Tab 2,Tab 3,Secrets;1;false;false] + ]], + -- Style + + "formspec_version[3]size[12,13]" .. + ("label[0.375,0.375;Styled - %s %s]"):format( + color("#F00", "red text"), + color("#77FF00CC", "green text")) .. + "label[6.375,0.375;Unstyled]" .. + "box[0,0.75;12,0.1;#999]" .. + "box[6,0.85;0.1,11.15;#999]" .. + "container[0.375,1.225]" .. + style_fs .. + "container_end[]container[6.375,1.225]" .. + style_fs:gsub("one_", "two_"):gsub("style%[[^%]]+%]", ""):gsub("style_type%[[^%]]+%]", "") .. + "container_end[]", + + -- Noclip + "formspec_version[3]size[12,13]" .. + "label[0.1,0.5;Clip]" .. + "container[-2.5,1]" .. clip_fs:gsub("%%c", "false") .. "container_end[]" .. + "label[11,0.5;Noclip]" .. + "container[11.5,1]" .. clip_fs:gsub("%%c", "true") .. "container_end[]", + + -- Table + "size[18,13]real_coordinates[true]" .. + "container[0.5,0.5]" .. table_fs.. "container_end[]", + + -- Hypertext + "size[12,13]real_coordinates[true]" .. + "container[0.5,0.5]" .. hypertext_fs .. "container_end[]", + + -- Tabheaders + "size[12,13]real_coordinates[true]" .. + "container[0.5,1.5]" .. tabheaders_fs .. "container_end[]", + + -- Inv + "size[12,13]real_coordinates[true]" .. inv_style_fs, + + -- Window + function() + return "formspec_version[3]" .. + string.format("size[%s,%s]position[%s,%s]anchor[%s,%s]padding[%s,%s]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + string.format("field[0.5,0.5;2.5,0.5;sizex;X Size;%s]field[3.5,0.5;2.5,0.5;sizey;Y Size;%s]" .. + "field[0.5,1.5;2.5,0.5;positionx;X Position;%s]field[3.5,1.5;2.5,0.5;positiony;Y Position;%s]" .. + "field[0.5,2.5;2.5,0.5;anchorx;X Anchor;%s]field[3.5,2.5;2.5,0.5;anchory;Y Anchor;%s]" .. + "field[0.5,3.5;2.5,0.5;paddingx;X Padding;%s]field[3.5,3.5;2.5,0.5;paddingy;Y Padding;%s]" .. + "button[2,4.5;2.5,0.5;submit_window;Submit]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + "field_close_on_enter[sizex;false]field_close_on_enter[sizey;false]" .. + "field_close_on_enter[positionx;false]field_close_on_enter[positiony;false]" .. + "field_close_on_enter[anchorx;false]field_close_on_enter[anchory;false]" .. + "field_close_on_enter[paddingx;false]field_close_on_enter[paddingy;false]" + end, + + -- Animation + [[ + formspec_version[6] + size[12,13] + animated_image[0.5,0.5;1,1;;testformspec_animation.png;4;100] + animated_image[0.5,1.75;1,1;;testformspec_animation.jpg;4;100] + animated_image[1.75,0.5;1,1;;testformspec_animation.png;100;100] + animated_image[3,0.5;1,1;ani_img_1;testformspec_animation.png;4;1000] + image[0.5,3;1,1;testformspec_bg.png;1] + animated_image[0.5,4.25;1,1;;[combine:16x48:0,0=testformspec_bg.png:0,16=testformspec_bg_hovered.png:0,32=testformspec_bg_pressed.png;3;250;1;1] + image[0.5,5.5;2,1;testformspec_9slice.png;16,0,-16,-16] + animated_image[2.75,5.5;1.5,0.5;;[combine:300x140:0,0=testformspec_9slice.png:0,70=(testformspec_9slice.png^[transformFX);2;500;1;16,0,-16,-16] + button[4.25,0.5;1,1;ani_btn_1;Current +Number] + animated_image[3,1.75;1,1;ani_img_2;testformspec_animation.png;4;1000;2] + button[4.25,1.75;1,1;ani_btn_2;Current +Number] + animated_image[3,3;1,1;;testformspec_animation.png;4;0] + animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3] + animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] + animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] + + ]], + + -- Model + [[ + formspec_version[3] + size[12,13] + style[m1;bgcolor=black] + style[m2;bgcolor=black] + label[5,1;all defaults] + label[5,5.1;angle = 0, 180 +continuous = false +mouse control = false +frame loop range = 0,30] + label[5,9.2;continuous = true +mouse control = true] + model[0.5,0.1;4,4;m1;testformspec_character.b3d;testformspec_character.png] + model[0.5,4.2;4,4;m2;testformspec_character.b3d;testformspec_character.png;0,180;false;false;0,30] + model[0.5,8.3;4,4;m3;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] + ]], + + -- Scroll containers + "formspec_version[7]size[12,13]" .. + scroll_fs, + + -- Sound + [[ + formspec_version[3] + size[12,13] + style[snd_btn;sound=soundstuff_mono] + style[snd_ibtn;sound=soundstuff_mono] + style[snd_drp;sound=soundstuff_mono] + style[snd_chk;sound=soundstuff_mono] + style[snd_tab;sound=soundstuff_mono] + button[0.5,0.5;2,1;snd_btn;Sound] + image_button[0.5,2;2,1;testformspec_item.png;snd_ibtn;Sound] + dropdown[0.5,4;4;snd_drp;Sound,Two,Three;] + checkbox[0.5,5.5.5;snd_chk;Sound;] + tabheader[0.5,7;8,0.65;snd_tab;Soundtab1,Soundtab2,Soundtab3;1;false;false] + ]], + + -- Background + [[ + formspec_version[3] + size[12,13] + box[0,0;12,13;#f0f1] + background[0,0;0,0;testformspec_bg.png;true] + box[3.9,2.9;6.2,4.2;#d00f] + scroll_container[4,3;6,4;scrbar;vertical] + background9[1,0.5;0,0;testformspec_bg_9slice.png;true;4,6] + label[0,0.2;Backgrounds are not be applied to scroll containers,] + label[0,0.5;but to the whole form.] + scroll_container_end[] + scrollbar[3.5,3;0.3,4;vertical;scrbar;0] + container[2,11] + box[-0.1,0.5;3.2,1;#fff5] + background[0,0;2,3;testformspec_bg.png;false] + background9[1,0;2,3;testformspec_bg_9slice.png;false;4,6] + container_end[] + ]], + + -- Unsized + [[ + formspec_version[3] + background9[0,0;0,0;testformspec_bg_9slice.png;true;4,6] + background[1,1;0,0;testformspec_bg.png;true] + ]], +} + +local page_id = 2 +local function show_test_formspec(pname) + local page = pages[page_id] + if type(page) == "function" then + page = page() + end + + local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Table,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]" + + core.show_formspec(pname, "testformspec:formspec", fs) +end + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "testformspec:formspec" then + return false + end + + if fields.maintabs then + page_id = tonumber(fields.maintabs) + show_test_formspec(player:get_player_name()) + return true + end + + if fields.ani_img_1 and fields.ani_btn_1 then + core.chat_send_player(player:get_player_name(), "ani_img_1 = " .. tostring(fields.ani_img_1)) + return true + elseif fields.ani_img_2 and fields.ani_btn_2 then + core.chat_send_player(player:get_player_name(), "ani_img_2 = " .. tostring(fields.ani_img_2)) + return true + end + + if fields.hypertext then + core.chat_send_player(player:get_player_name(), "Hypertext action received: " .. tostring(fields.hypertext)) + return true + end + + for name, value in pairs(fields) do + if window[name] then + print(name, window[name]) + local num_val = tonumber(value) or 0 + + if name == "sizex" and num_val < 4 then + num_val = 6.5 + elseif name == "sizey" and num_val < 5 then + num_val = 5.5 + end + + window[name] = num_val + print(name, window[name]) + end + end + + if fields.submit_window then + show_test_formspec(player:get_player_name()) + end +end) + +core.register_chatcommand("test_formspec", { + params = "", + description = "Open the test formspec", + func = function(name) + if not core.get_player_by_name(name) then + return false, "You need to be online!" + end + + show_test_formspec(name) + return true + end, +}) diff --git a/data/games/garage/mods/testformspec/init.lua b/data/games/garage/mods/testformspec/init.lua new file mode 100644 index 0000000..ef1a841 --- /dev/null +++ b/data/games/garage/mods/testformspec/init.lua @@ -0,0 +1,3 @@ +dofile(core.get_modpath("testformspec").."/dummy_items.lua") +dofile(core.get_modpath("testformspec").."/formspec.lua") +dofile(core.get_modpath("testformspec").."/callbacks.lua") diff --git a/data/games/garage/mods/testformspec/mod.conf b/data/games/garage/mods/testformspec/mod.conf new file mode 100644 index 0000000..00eac30 --- /dev/null +++ b/data/games/garage/mods/testformspec/mod.conf @@ -0,0 +1,2 @@ +name = testformspec +description = Contains an example formspec to test all the features of formspecs diff --git a/data/games/garage/mods/testformspec/models/testformspec_character.b3d b/data/games/garage/mods/testformspec/models/testformspec_character.b3d new file mode 100644 index 0000000..8edbaf6 Binary files /dev/null and b/data/games/garage/mods/testformspec/models/testformspec_character.b3d differ diff --git a/data/games/garage/mods/testformspec/models/testformspec_chest.obj b/data/games/garage/mods/testformspec/models/testformspec_chest.obj new file mode 100644 index 0000000..72ba175 --- /dev/null +++ b/data/games/garage/mods/testformspec/models/testformspec_chest.obj @@ -0,0 +1,79 @@ +# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend' +# www.blender.org +o Top_Cube.002_None_Top_Cube.002_None_bottom +v -0.500000 0.408471 0.720970 +v -0.500000 1.115578 0.013863 +v -0.500000 0.894607 -0.207108 +v -0.500000 0.187501 0.499999 +v 0.500000 1.115578 0.013863 +v 0.500000 0.408471 0.720970 +v 0.500000 0.187501 0.499999 +v 0.500000 0.894607 -0.207108 +v -0.500000 0.187500 -0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.187500 -0.500000 +v 0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 0.0000 0.6875 +vt 1.0000 1.0000 +vt 0.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 0.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 1.0000 +vt 0.0000 0.6875 +vt 0.0000 0.6875 +vt 0.0000 0.0000 +vt 1.0000 0.5000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.5000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 0.7071 0.7071 +vn -0.0000 -1.0000 -0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 -0.0000 +vn 0.0000 -0.7071 0.7071 +vn 0.0000 0.0000 1.0000 +vn -0.0000 0.7071 -0.7071 +vn -0.0000 0.0000 -1.0000 +vn -0.0000 -0.7071 -0.7071 +vn -0.0000 1.0000 -0.0000 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top +s off +f 6/1/1 5/2/1 2/3/1 1/4/1 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom +f 11/5/2 10/6/2 14/7/2 13/8/2 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left +f 1/9/3 2/10/3 3/11/3 4/12/3 +f 5/13/4 6/1/4 7/14/4 8/15/4 +f 4/12/3 9/16/3 10/17/3 11/18/3 +f 12/19/4 7/14/4 13/8/4 14/20/4 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back +f 6/21/5 1/9/5 4/12/5 7/22/5 +f 7/22/6 4/12/6 11/18/6 13/23/6 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front +f 2/10/7 5/24/7 8/25/7 3/11/7 +f 9/16/8 12/26/8 14/27/8 10/17/8 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside +f 4/28/9 3/29/9 8/30/9 7/31/9 +f 7/31/10 12/32/10 9/33/10 4/28/10 diff --git a/data/games/garage/mods/testformspec/textures/default_chest_front.png b/data/games/garage/mods/testformspec/textures/default_chest_front.png new file mode 100644 index 0000000..f413279 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/default_chest_front.png differ diff --git a/data/games/garage/mods/testformspec/textures/default_chest_inside.png b/data/games/garage/mods/testformspec/textures/default_chest_inside.png new file mode 100644 index 0000000..9d2e883 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/default_chest_inside.png differ diff --git a/data/games/garage/mods/testformspec/textures/default_chest_side.png b/data/games/garage/mods/testformspec/textures/default_chest_side.png new file mode 100644 index 0000000..44a65a4 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/default_chest_side.png differ diff --git a/data/games/garage/mods/testformspec/textures/default_chest_top.png b/data/games/garage/mods/testformspec/textures/default_chest_top.png new file mode 100644 index 0000000..1fbdbb9 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/default_chest_top.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_9slice.png b/data/games/garage/mods/testformspec/textures/testformspec_9slice.png new file mode 100644 index 0000000..e36a8be Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_9slice.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_animation.jpg b/data/games/garage/mods/testformspec/textures/testformspec_animation.jpg new file mode 100644 index 0000000..b98ca26 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_animation.jpg differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_animation.png b/data/games/garage/mods/testformspec/textures/testformspec_animation.png new file mode 100644 index 0000000..b972e5d Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_animation.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg.png b/data/games/garage/mods/testformspec/textures/testformspec_bg.png new file mode 100644 index 0000000..cd1e509 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice.png new file mode 100644 index 0000000..34433ac Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_focused.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_focused.png new file mode 100644 index 0000000..2f52dd9 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_focused.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_hovered.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_hovered.png new file mode 100644 index 0000000..01c2dc7 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_hovered.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_pressed.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_pressed.png new file mode 100644 index 0000000..0cbac75 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_9slice_pressed.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_focused.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_focused.png new file mode 100644 index 0000000..6b47ae6 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_focused.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_hovered.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_hovered.png new file mode 100644 index 0000000..3ebbb98 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_hovered.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_bg_pressed.png b/data/games/garage/mods/testformspec/textures/testformspec_bg_pressed.png new file mode 100644 index 0000000..2fb5fc2 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_bg_pressed.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_button_image.png b/data/games/garage/mods/testformspec/textures/testformspec_button_image.png new file mode 100644 index 0000000..75c438a Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_button_image.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_character.png b/data/games/garage/mods/testformspec/textures/testformspec_character.png new file mode 100644 index 0000000..0502178 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_character.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_hovered.png b/data/games/garage/mods/testformspec/textures/testformspec_hovered.png new file mode 100644 index 0000000..3ccad30 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_hovered.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_item.png b/data/games/garage/mods/testformspec/textures/testformspec_item.png new file mode 100644 index 0000000..4fd823b Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_item.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_node.png b/data/games/garage/mods/testformspec/textures/testformspec_node.png new file mode 100644 index 0000000..c107f28 Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_node.png differ diff --git a/data/games/garage/mods/testformspec/textures/testformspec_pressed.png b/data/games/garage/mods/testformspec/textures/testformspec_pressed.png new file mode 100644 index 0000000..45c504f Binary files /dev/null and b/data/games/garage/mods/testformspec/textures/testformspec_pressed.png differ diff --git a/data/games/garage/mods/testfullscreenfs/init.lua b/data/games/garage/mods/testfullscreenfs/init.lua new file mode 100644 index 0000000..1b84033 --- /dev/null +++ b/data/games/garage/mods/testfullscreenfs/init.lua @@ -0,0 +1,71 @@ +local function window_info_equal(a, b) + return + -- size + a.size.x == b.size.x and a.size.y == b.size.y and + -- real_gui_scaling, real_hud_scaling + a.real_gui_scaling == b.real_gui_scaling and + a.real_hud_scaling == b.real_hud_scaling and + -- max_formspec_size + a.max_formspec_size.x == b.max_formspec_size.x and + a.max_formspec_size.y == b.max_formspec_size.y and + -- touch_controls + a.touch_controls == b.touch_controls +end + +local last_window_info = {} + +local function show_fullscreen_fs(name, window) + print(dump(window)) + + local size = window.max_formspec_size + local touch_text = window.touch_controls and "Touch controls enabled" or + "Touch controls disabled" + local fs = { + "formspec_version[4]", + ("size[%f,%f]"):format(size.x, size.y), + "padding[0,0]", + "bgcolor[;true]", + ("button[%f,%f;1,1;%s;%s]"):format(0, 0, "tl", "TL"), + ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, 0, "tr", "TR"), + ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"), + ("button[%f,%f;1,1;%s;%s]"):format(0, size.y - 1, "bl", "BL"), + + ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen"), + ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2 + 1, touch_text), + } + + core.show_formspec(name, "testfullscreenfs:fs", table.concat(fs)) + core.chat_send_player(name, ("Calculated size of %f, %f"):format(size.x, size.y)) + last_window_info[name] = window +end + +core.register_chatcommand("testfullscreenfs", { + func = function(name) + local window = core.get_player_window_information(name) + if not window then + return false, "Unable to get window info" + end + + show_fullscreen_fs(name, window) + return true + end, +}) + +core.register_globalstep(function() + for name, last_window in pairs(last_window_info) do + local window = core.get_player_window_information(name) + if window and not window_info_equal(last_window, window) then + show_fullscreen_fs(name, window) + end + end +end) + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname == "testfullscreenfs:fs" and fields.quit then + last_window_info[player:get_player_name()] = nil + end +end) + +core.register_on_leaveplayer(function(player) + last_window_info[player:get_player_name()] = nil +end) diff --git a/data/games/garage/mods/testfullscreenfs/mod.conf b/data/games/garage/mods/testfullscreenfs/mod.conf new file mode 100644 index 0000000..68e7a10 --- /dev/null +++ b/data/games/garage/mods/testfullscreenfs/mod.conf @@ -0,0 +1,2 @@ +name = testfullscreenfs +description = Test mod to use core.get_player_window_information() diff --git a/data/games/garage/mods/testhud/init.lua b/data/games/garage/mods/testhud/init.lua new file mode 100644 index 0000000..0512345 --- /dev/null +++ b/data/games/garage/mods/testhud/init.lua @@ -0,0 +1,393 @@ +local player_font_huds = {} + +local font_states = { + {0, "Normal font"}, + {1, "Bold font"}, + {2, "Italic font"}, + {3, "Bold and italic font"}, + {4, "Monospace font"}, + {5, "Bold and monospace font"}, + {7, "ZOMG all the font styles"}, + {7, "Colors test! " .. core.colorize("green", "Green") .. + core.colorize("red", "\nRed") .. " END"}, +} + + +local font_default_def = { + type = "text", + position = {x = 0.5, y = 0.5}, + scale = {x = 2, y = 2}, + alignment = { x = 0, y = 0 }, + number = 0xFFFFFF, +} + +local function add_font_hud(player, state) + local def = table.copy(font_default_def) + local statetbl = font_states[state] + def.offset = {x = 0, y = 32 * state} + def.style = statetbl[1] + def.text = statetbl[2] + return player:hud_add(def) +end + +local font_etime = 0 +local font_state = 0 + +core.register_globalstep(function(dtime) + font_etime = font_etime + dtime + if font_etime < 1 then + return + end + font_etime = 0 + for _, player in ipairs(core.get_connected_players()) do + local huds = player_font_huds[player:get_player_name()] + if huds then + for i, hud_id in ipairs(huds) do + local statetbl = font_states[(font_state + i) % #font_states + 1] + player:hud_change(hud_id, "style", statetbl[1]) + player:hud_change(hud_id, "text", statetbl[2]) + end + end + end + font_state = font_state + 1 +end) + +core.register_chatcommand("hudfonts", { + params = "[]", + description = "Show/Hide some text on the HUD with various font options", + func = function(name, param) + local player = core.get_player_by_name(name) + local param = tonumber(param) or 0 + param = math.min(math.max(param, 1), #font_states) + if player_font_huds[name] == nil then + player_font_huds[name] = {} + for i = 1, param do + table.insert(player_font_huds[name], add_font_hud(player, i)) + end + core.chat_send_player(name, ("%d text HUD element(s) added."):format(param)) + else + local huds = player_font_huds[name] + if huds then + for _, hud_id in ipairs(huds) do + player:hud_remove(hud_id) + end + core.chat_send_player(name, "All text HUD elements removed.") + end + player_font_huds[name] = nil + end + return true + end, +}) + +-- Testing waypoint capabilities + +local player_waypoints = {} +core.register_chatcommand("hudwaypoints", { + params = "[ add | add_change | remove ]", + description = "Create HUD waypoints at your position for testing (add: Add waypoints and change them after 0.5s (default). add_change: Add waypoints and change immediately. remove: Remove all waypoints)", + func = function(name, params) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + if params == "remove" then + if player_waypoints[name] then + for i=1, #player_waypoints[name] do + player:hud_remove(player_waypoints[name][i]) + end + player_waypoints[name] = {} + end + return true, "All waypoint HUD elements removed." + end + if not (params == "add_change" or params == "add" or params == "") then + -- Incorrect syntax + return false + end + local regular = player:hud_add { + type = "waypoint", + name = "regular waypoint", + text = "m", + number = 0xFFFFFF, + world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0}) + } + local reduced_precision = player:hud_add { + type = "waypoint", + name = "imprecise waypoint", + text = "m (0.1 steps, precision = 10)", + precision = 10, + number = 0xFFFFFF, + world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0}) + } + local hidden_distance = player:hud_add { + type = "waypoint", + name = "waypoint with hidden distance", + text = "this text is hidden as well (precision = 0)", + precision = 0, + number = 0xFFFFFF, + world_pos = vector.add(player:get_pos(), {x = 0, y = 0.5, z = 0}) + } + local function change(chplayer) + if not (chplayer and chplayer:is_player()) then + return + end + if regular then + chplayer:hud_change(regular, "world_pos", vector.add(player:get_pos(), {x = 0, y = 3, z = 0})) + chplayer:hud_change(regular, "number", 0xFF0000) + end + if reduced_precision then + chplayer:hud_change(reduced_precision, "precision", 2) + chplayer:hud_change(reduced_precision, "text", "m (0.5 steps, precision = 2)") + chplayer:hud_change(reduced_precision, "number", 0xFFFF00) + end + if hidden_distance then + chplayer:hud_change(hidden_distance, "number", 0x0000FF) + end + core.chat_send_player(chplayer:get_player_name(), "Waypoints changed.") + end + if params == "add_change" then + -- change immediate + change(player) + else + core.after(0.5, change, player) + end + local image_waypoint = player:hud_add { + type = "image_waypoint", + text = "testhud_waypoint.png", + world_pos = player:get_pos(), + -- 20% of screen width, 3x image height + scale = {x = -20, y = 3}, + offset = {x = 0, y = -32} + } + if not player_waypoints[name] then + player_waypoints[name] = {} + end + if regular then + table.insert(player_waypoints[name], regular) + end + if reduced_precision then + table.insert(player_waypoints[name], reduced_precision) + end + if hidden_distance then + table.insert(player_waypoints[name], hidden_distance) + end + if image_waypoint then + table.insert(player_waypoints[name], image_waypoint) + end + regular = regular or "error" + reduced_precision = reduced_precision or "error" + hidden_distance = hidden_distance or "error" + image_waypoint = image_waypoint or "error" + return true, "Waypoints added. IDs: regular: " .. regular .. ", reduced precision: " .. reduced_precision .. + ", hidden distance: " .. hidden_distance .. ", image waypoint: " .. image_waypoint + end +}) + +core.register_on_joinplayer(function(player) + player:set_properties({zoom_fov = 15}) +end) + +core.register_chatcommand("zoomfov", { + params = "[]", + description = "Set or display your zoom_fov", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + if param == "" then + local fov = player:get_properties().zoom_fov + return true, "zoom_fov = "..tostring(fov) + end + local fov = tonumber(param) + if not fov then + return false, "Missing or incorrect zoom_fov parameter!" + end + player:set_properties({zoom_fov = fov}) + fov = player:get_properties().zoom_fov + return true, "zoom_fov = "..tostring(fov) + end, +}) + +-- Hotbars + +local hud_hotbar_defs = { + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 0, + alignment = {x=1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 2, + alignment = {x=1, y=1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 0, + offset = {x=140, y=20}, + alignment = {x=-1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 2, + offset = {x=140, y=20}, + alignment = {x=-1, y=1}, + }, +} + + +local player_hud_hotbars= {} +core.register_chatcommand("hudhotbars", { + description = "Shows some test Lua HUD elements of type hotbar. " .. + "(add: Adds elements (default). remove: Removes elements)", + params = "[ add | remove ]", + func = function(name, params) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + + local id_table = player_hud_hotbars[name] + if not id_table then + id_table = {} + player_hud_hotbars[name] = id_table + end + + if params == "remove" then + for _, id in ipairs(id_table) do + player:hud_remove(id) + end + return true, "Hotbars removed." + end + + -- params == "add" or default + for _, def in ipairs(hud_hotbar_defs) do + table.insert(id_table, player:hud_add(def)) + end + return true, #hud_hotbar_defs .." Hotbars added." + end +}) + +-- Inventories + +local hud_inventory_defs = { + { + type = "inventory", + position = {x=0.2, y=0.5}, + direction = 0, + text = "main", + number = 4, + item = 2, + }, + { + type = "inventory", + position = {x=0.2, y=0.5}, + direction = 2, + text = "main", + number = 4, + item = 2, + }, + { + type = "inventory", + position = {x=0.7, y=0.5}, + direction = 1, + text = "main", + number = 4, + item = 2, + }, + { + type = "inventory", + position = {x=0.7, y=0.5}, + direction = 3, + text = "main", + number = 4, + item = 2, + }, +} + +local player_hud_inventories= {} +core.register_chatcommand("hudinventories", { + description = "Shows some test Lua HUD elements of type inventory. (add: Adds elements (default). remove: Removes elements)", + params = "[ add | remove ]", + func = function(name, params) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + + local id_table = player_hud_inventories[name] + if not id_table then + id_table = {} + player_hud_inventories[name] = id_table + end + + if params == "remove" then + for _, id in ipairs(id_table) do + player:hud_remove(id) + end + return true, "HUD Inventories removed." + end + + -- params == "add" or default + for _, def in ipairs(hud_inventory_defs) do + table.insert(id_table, player:hud_add(def)) + end + return true, #hud_inventory_defs .." HUD Inventories added." + end +}) + + +core.register_on_leaveplayer(function(player) + local playername = player:get_player_name() + player_font_huds[playername] = nil + player_waypoints[playername] = nil + player_hud_hotbars[playername] = nil + player_hud_inventories[playername] = nil +end) + +core.register_chatcommand("hudprint", { + description = "Writes all used Lua HUD elements into chat.", + func = function(name, params) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + + local s = "HUD elements:" + for k, elem in pairs(player:hud_get_all()) do + local ename = dump(elem.name) + local etype = dump(elem.type) + local epos = "{x="..elem.position.x..", y="..elem.position.y.."}" + s = s.."\n["..k.."] type = "..etype.." | name = "..ename.." | pos = ".. epos + end + + return true, s + end +}) + +local hud_flags = {"hotbar", "healthbar", "crosshair", "wielditem", "breathbar", + "minimap", "minimap_radar", "basic_debug", "chat"} + +core.register_chatcommand("hudtoggleflag", { + description = "Toggles a hud flag.", + params = "[ ".. table.concat(hud_flags, " | ") .." ]", + func = function(name, params) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + + local flags = player:hud_get_flags() + if flags[params] == nil then + return false, "Unknown hud flag." + end + + flags[params] = not flags[params] + player:hud_set_flags(flags) + return true, "Flag \"".. params .."\" set to ".. tostring(flags[params]) .. "." + end +}) diff --git a/data/games/garage/mods/testhud/mod.conf b/data/games/garage/mods/testhud/mod.conf new file mode 100644 index 0000000..ed9f65c --- /dev/null +++ b/data/games/garage/mods/testhud/mod.conf @@ -0,0 +1,2 @@ +name = testhud +description = For testing HUD functionality diff --git a/data/games/garage/mods/testhud/textures/testhud_waypoint.png b/data/games/garage/mods/testhud/textures/testhud_waypoint.png new file mode 100644 index 0000000..f9f1308 Binary files /dev/null and b/data/games/garage/mods/testhud/textures/testhud_waypoint.png differ diff --git a/data/games/garage/mods/testitems/init.lua b/data/games/garage/mods/testitems/init.lua new file mode 100644 index 0000000..12da2ad --- /dev/null +++ b/data/games/garage/mods/testitems/init.lua @@ -0,0 +1,107 @@ +local S = core.get_translator("testitems") + +-- +-- Texture overlays for items +-- + +-- For the global overlay color test +local GLOBAL_COLOR_ARG = "orange" + +-- Punch handler to set random color with "color" argument in item metadata +local overlay_on_use = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + local color = math.random(0x0, 0xFFFFFF) + local colorstr = string.format("#%06x", color) + meta:set_string("color", colorstr) + core.log("action", "[testitems] Color of "..itemstack:get_name().." changed to "..colorstr) + return itemstack +end +-- Place handler to clear item metadata color +local overlay_on_place = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + meta:set_string("color", "") + return itemstack +end + +core.register_craftitem("testitems:overlay_meta", { + description = S("Texture Overlay Test Item, Meta Color") .. "\n" .. + S("Image must be a square with rainbow cross (inventory and wield)") .. "\n" .. + S("Item meta color must only change square color") .. "\n" .. + S("Punch: Set random color") .. "\n" .. + S("Place: Clear color"), + -- Base texture: A grayscale square (can be colorized) + inventory_image = "testitems_overlay_base.png", + wield_image = "testitems_overlay_base.png", + -- Overlay: A rainbow cross (NOT to be colorized!) + inventory_overlay = "testitems_overlay_overlay.png", + wield_overlay = "testitems_overlay_overlay.png", + + on_use = overlay_on_use, + on_place = overlay_on_place, + on_secondary_use = overlay_on_place, +}) + +core.register_craftitem("testitems:overlay_global", { + description = S("Texture Overlay Test Item, Global Color") .. "\n" .. + S("Image must be an orange square with rainbow cross (inventory and wield)"), + -- Base texture: A grayscale square (to be colorized) + inventory_image = "testitems_overlay_base.png", + wield_image = "testitems_overlay_base.png", + -- Overlay: A rainbow cross (NOT to be colorized!) + inventory_overlay = "testitems_overlay_overlay.png", + wield_overlay = "testitems_overlay_overlay.png", + color = GLOBAL_COLOR_ARG, +}) + +core.register_craftitem("testitems:image_meta", { + description = S("Image Override Meta Test Item"), + inventory_image = "default_apple.png", + wield_image = "basetools_icesword.png", + + on_use = function(itemstack, player) + local meta = itemstack:get_meta() + local state = meta:get_int("state") + state = (state + 1) % 5 + meta:set_int("state", state) + core.chat_send_player(player:get_player_name(), "State " .. state) + + if state == 0 then + meta:set_string("inventory_image", "") + meta:set_string("wield_image", "") + meta:set_string("inventory_overlay", "") + meta:set_string("wield_overlay", "") + meta:set_string("wield_scale", "") + elseif state == 1 then + meta:set_string("inventory_image", "default_tree.png") + meta:set_string("wield_image", "basetools_firesword.png") + elseif state == 2 then + meta:set_string("inventory_image", "default_apple.png^testitems_overridden.png") + meta:set_string("wield_image", "basetools_icesword.png^testitems_overridden.png") + elseif state == 3 then + meta:set_string("inventory_image", "default_tree.png") + meta:set_string("wield_image", "basetools_firesword.png") + meta:set_string("inventory_overlay", "default_apple.png") + meta:set_string("wield_overlay", "default_apple.png") + elseif state == 4 then + local scale = vector.new(0.5, 0.5, 0.5) + meta:set_string("wield_scale", scale:to_string()) + end + + return itemstack + end, +}) + +core.register_craftitem("testitems:telescope_stick", { + description = S("Telescope Stick (Increases range on use.)"), + inventory_image = "testitems_telescope_stick.png", + on_use = function(itemstack, player) + local meta = itemstack:get_meta() + local range = meta:get_float("range") + 1.2 + if range > 10 then + range = 0 + end + meta:set_float("range", range) + core.chat_send_player(player:get_player_name(), "Telescope Stick range set to "..range) + return itemstack + end, +}) diff --git a/data/games/garage/mods/testitems/mod.conf b/data/games/garage/mods/testitems/mod.conf new file mode 100644 index 0000000..f91febe --- /dev/null +++ b/data/games/garage/mods/testitems/mod.conf @@ -0,0 +1,2 @@ +name = testitems +description = Test mod to test misc. items that are neither tools nor nodes diff --git a/data/games/garage/mods/testitems/textures/testitems_overlay_base.png b/data/games/garage/mods/testitems/textures/testitems_overlay_base.png new file mode 100644 index 0000000..f473623 Binary files /dev/null and b/data/games/garage/mods/testitems/textures/testitems_overlay_base.png differ diff --git a/data/games/garage/mods/testitems/textures/testitems_overlay_overlay.png b/data/games/garage/mods/testitems/textures/testitems_overlay_overlay.png new file mode 100644 index 0000000..75a7d9f Binary files /dev/null and b/data/games/garage/mods/testitems/textures/testitems_overlay_overlay.png differ diff --git a/data/games/garage/mods/testitems/textures/testitems_overridden.png b/data/games/garage/mods/testitems/textures/testitems_overridden.png new file mode 100644 index 0000000..08d3e70 Binary files /dev/null and b/data/games/garage/mods/testitems/textures/testitems_overridden.png differ diff --git a/data/games/garage/mods/testitems/textures/testitems_telescope_stick.png b/data/games/garage/mods/testitems/textures/testitems_telescope_stick.png new file mode 100644 index 0000000..88d004c Binary files /dev/null and b/data/games/garage/mods/testitems/textures/testitems_telescope_stick.png differ diff --git a/data/games/garage/mods/testnodes/README.md b/data/games/garage/mods/testnodes/README.md new file mode 100644 index 0000000..13ed972 --- /dev/null +++ b/data/games/garage/mods/testnodes/README.md @@ -0,0 +1,11 @@ +# Test Nodes + +This mod contains a bunch of basic nodes to test development stuff. +Most nodes are kept as minimal as possible in order to show off one particular feature of the engine, to make testing stuff easier. + +This mod includes tests for: + +* drawtypes +* paramtype2's +* node properties such as damage, drowning, falling, etc. +* other random stuff diff --git a/data/games/garage/mods/testnodes/commands.lua b/data/games/garage/mods/testnodes/commands.lua new file mode 100644 index 0000000..946df50 --- /dev/null +++ b/data/games/garage/mods/testnodes/commands.lua @@ -0,0 +1,139 @@ +-- Add chat command to place all the nodes in DevTest + +local function advance_pos(pos, start_pos, advance_z) + if advance_z then + pos.z = pos.z + 2 + pos.x = start_pos.x + else + pos.x = pos.x + 2 + end + if pos.x > 30900 or pos.x - start_pos.x > 46 then + pos.x = start_pos.x + pos.z = pos.z + 2 + end + if pos.z > 30900 then + -- We ran out of space! Aborting + aborted = true + return false + end + return pos +end + +local function place_nodes(param) + local nodes = param.nodes + local name = param.name + local pos = param.pos + local start_pos = param.start_pos + table.sort(nodes) + core.chat_send_player(name, "Placing nodes …") + local nodes_placed = 0 + local aborted = false + for n=1, #nodes do + local itemstring = nodes[n] + local def = core.registered_nodes[itemstring] + local p2_max = 0 + if param.param ~= "no_param2" then + -- Also test the param2 values of the nodes + -- ... but we only use permissible param2 values + if def.paramtype2 == "wallmounted" then + p2_max = 5 + elseif def.paramtype2 == "facedir" then + p2_max = 23 + elseif def.paramtype2 == "4dir" then + p2_max = 3 + elseif def.paramtype2 == "glasslikeliquidlevel" then + p2_max = 63 + elseif def.paramtype2 == "meshoptions" and def.drawtype == "plantlike" then + p2_max = 63 + elseif def.paramtype2 == "leveled" then + p2_max = 127 + elseif def.paramtype2 == "degrotate" and (def.drawtype == "plantlike" or def.drawtype == "mesh") then + p2_max = 239 + elseif def.paramtype2 == "colorfacedir" or + def.paramtype2 == "colorwallmounted" or + def.paramtype2 == "colordegrotate" or + def.paramtype2 == "color4dir" or + def.paramtype2 == "color" then + p2_max = 255 + end + end + for p2 = 0, p2_max do + -- Skip undefined param2 values + if not ((def.paramtype2 == "meshoptions" and p2 % 8 > 4) or + (def.paramtype2 == "colorwallmounted" and p2 % 8 > 5) or + ((def.paramtype2 == "colorfacedir" or def.paramtype2 == "colordegrotate") + and p2 % 32 > 23)) then + + core.set_node(pos, { name = itemstring, param2 = p2 }) + nodes_placed = nodes_placed + 1 + pos = advance_pos(pos, start_pos) + if not pos then + aborted = true + break + end + end + end + if aborted then + break + end + end + if aborted then + core.chat_send_player(name, "Not all nodes could be placed, please move further away from the world boundary. Nodes placed: "..nodes_placed) + end + core.chat_send_player(name, "Nodes placed: "..nodes_placed..".") +end + +local function after_emerge(blockpos, action, calls_remaining, param) + if calls_remaining == 0 then + place_nodes(param) + end +end + +core.register_chatcommand("test_place_nodes", { + params = "[ no_param2 ]", + description = "Test: Place all nodes (except dummy and callback nodes) and optionally their permissible param2 variants", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos = vector.floor(player:get_pos()) + pos.x = math.ceil(pos.x + 3) + pos.z = math.ceil(pos.z + 3) + pos.y = math.ceil(pos.y + 1) + local start_pos = table.copy(pos) + if pos.x > 30800 then + return false, "Too close to world boundary (+X). Please move to X < 30800." + end + if pos.z > 30800 then + return false, "Too close to world boundary (+Z). Please move to Z < 30800." + end + + local aborted = false + local nodes = {} + local emerge_estimate = 0 + for itemstring, def in pairs(core.registered_nodes) do + if itemstring ~= "ignore" and + -- Skip callback test and dummy nodes + -- to avoid clutter and chat spam + core.get_item_group(itemstring, "callback_test") == 0 and + core.get_item_group(itemstring, "dummy") == 0 then + table.insert(nodes, itemstring) + if def.paramtype2 == 0 then + emerge_estimate = emerge_estimate + 1 + else + emerge_estimate = emerge_estimate + 255 + end + end + end + -- Emerge area to make sure that all nodes are being placed. + -- Note we will emerge much more than we need to (overestimation), + -- the estimation code could be improved performance-wise … + local length = 16 + math.ceil(emerge_estimate / 24) * 2 + core.emerge_area(start_pos, + { x = start_pos.x + 46, y = start_pos.y, z = start_pos.z + length }, + after_emerge, { nodes = nodes, name = name, pos = pos, start_pos = start_pos, param = param }) + return true, "Emerging area …" + end, +}) + diff --git a/data/games/garage/mods/testnodes/drawtypes.lua b/data/games/garage/mods/testnodes/drawtypes.lua new file mode 100644 index 0000000..95dae96 --- /dev/null +++ b/data/games/garage/mods/testnodes/drawtypes.lua @@ -0,0 +1,727 @@ +--[[ Drawtype Test: This file tests out and provides examples for +all drawtypes in Luanti. It is attempted to keep the node +definitions as simple and minimal as possible to keep +side-effects to a minimum. + +How to read the node definitions: +There are two parts which are separated by 2 newlines: +The first part contains the things that are more or less essential +for defining the drawtype (except description, which is +at the top for readability). +The second part (after the 2 newlines) contains stuff that are +unrelated to the drawtype, stuff that is mostly there to make +testing this node easier and more convenient. +]] + +local S = core.get_translator("testnodes") + +-- A regular cube +core.register_node("testnodes:normal", { + description = S("\"normal\" Drawtype Test Node").."\n".. + S("Opaque texture"), + drawtype = "normal", + tiles = { "testnodes_normal.png" }, + + groups = { dig_immediate = 3 }, +}) + +-- Standard glasslike node +core.register_node("testnodes:glasslike", { + description = S("\"glasslike\" Drawtype Test Node").."\n".. + S("Transparent node with hidden backfaces"), + drawtype = "glasslike", + paramtype = "light", + tiles = { "testnodes_glasslike.png" }, + + groups = { dig_immediate = 3 }, +}) + +-- Glasslike framed with the two textures (normal and "detail") +core.register_node("testnodes:glasslike_framed", { + description = S("\"glasslike_framed\" Drawtype Test Node").."\n".. + S("Transparent node with hidden backfaces").."\n".. + S("Frame connects to neighbors"), + drawtype = "glasslike_framed", + paramtype = "light", + tiles = { + "testnodes_glasslike_framed.png", + "testnodes_glasslike_detail.png", + }, + + + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +-- Like the one above, but without the "detail" texture (texture 2). +-- This node was added to see how the engine behaves when the "detail" texture +-- is missing. +core.register_node("testnodes:glasslike_framed_no_detail", { + description = S("\"glasslike_framed\" Drawtype without Detail Test Node").."\n".. + S("Transparent node with hidden backfaces").."\n".. + S("Frame connects to neighbors, but the 'detail' tile is not used"), + drawtype = "glasslike_framed", + paramtype = "light", + tiles = { "testnodes_glasslike_framed2.png" }, + + + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + + +core.register_node("testnodes:glasslike_framed_optional", { + description = S("\"glasslike_framed_optional\" Drawtype Test Node").."\n".. + S("Transparent node with hidden backfaces").."\n".. + S("Frame connects if 'connected_glass' setting is true"), + drawtype = "glasslike_framed_optional", + paramtype = "light", + tiles = { + "testnodes_glasslike_framed_optional.png", + "testnodes_glasslike_detail.png", + }, + + + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + + + +core.register_node("testnodes:allfaces", { + description = S("\"allfaces\" Drawtype Test Node").."\n".. + S("Transparent node with visible internal backfaces"), + drawtype = "allfaces", + paramtype = "light", + tiles = { "testnodes_allfaces.png" }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:allfaces_6", { + description = S("\"allfaces 6 Textures\" Drawtype Test Node").."\n".. + S("Transparent node with visible internal backfaces"), + drawtype = "allfaces", + paramtype = "light", + tiles = { + "testnodes_allfaces.png^[colorize:red", + "testnodes_allfaces.png^[colorize:orange", + "testnodes_allfaces.png^[colorize:yellow", + "testnodes_allfaces.png^[colorize:green", + "testnodes_allfaces.png^[colorize:blue", + "testnodes_allfaces.png^[colorize:purple" + }, + + groups = { dig_immediate = 3 }, +}) + +local allfaces_optional_tooltip = "".. + S("Rendering depends on 'leaves_style' setting:").."\n".. + S("* 'fancy': transparent with visible internal backfaces").."\n".. + S("* 'simple': transparent with hidden backfaces").."\n".. + S("* 'opaque': opaque") + +core.register_node("testnodes:allfaces_optional", { + description = S("\"allfaces_optional\" Drawtype Test Node").."\n".. + allfaces_optional_tooltip, + drawtype = "allfaces_optional", + paramtype = "light", + tiles = { "testnodes_allfaces_optional.png" }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:allfaces_optional_waving", { + description = S("Waving \"allfaces_optional\" Drawtype Test Node").."\n".. + allfaces_optional_tooltip.."\n".. + S("Waves if waving leaves are enabled by client"), + drawtype = "allfaces_optional", + paramtype = "light", + tiles = { "testnodes_allfaces_optional.png^[brighten" }, + waving = 2, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:firelike", { + description = S("\"firelike\" Drawtype Test Node").."\n".. + S("Changes shape based on neighbors"), + drawtype = "firelike", + paramtype = "light", + tiles = { "testnodes_firelike.png" }, + + + walkable = false, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:fencelike", { + description = S("\"fencelike\" Drawtype Test Node").."\n".. + S("Connects to neighbors"), + drawtype = "fencelike", + paramtype = "light", + tiles = { "testnodes_fencelike.png" }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:torchlike", { + description = S("Floor \"torchlike\" Drawtype Test Node").."\n".. + S("Always on floor"), + drawtype = "torchlike", + paramtype = "light", + tiles = { "testnodes_torchlike_floor.png^[colorize:#FF0000:64" }, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:torchlike_wallmounted", { + description = S("Wallmounted \"torchlike\" Drawtype Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + drawtype = "torchlike", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { + "testnodes_torchlike_floor.png", + "testnodes_torchlike_ceiling.png", + "testnodes_torchlike_wall.png", + }, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:torchlike_wallmounted_rot", { + description = S("Wallmounted Rotatable Torchlike Drawtype Test Node"), + drawtype = "torchlike", + paramtype = "light", + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + tiles = { + "testnodes_torchlike_floor.png^[colorize:#FFFF00:40", + "testnodes_torchlike_ceiling.png^[colorize:#FFFF00:40", + "testnodes_torchlike_wall.png^[colorize:#FFFF00:40", + }, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:signlike", { + description = S("Floor \"signlike\" Drawtype Test Node").."\n".. + S("Always on floor"), + drawtype = "signlike", + paramtype = "light", + tiles = { "testnodes_signlike.png^[colorize:#FF0000:64" }, + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:signlike_wallmounted", { + description = S("Wallmounted \"signlike\" Drawtype Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + drawtype = "signlike", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { "testnodes_signlike.png" }, + + + walkable = false, + groups = { dig_immediate = 3 }, + sunlight_propagates = true, +}) + +core.register_node("testnodes:signlike_rot", { + description = S("Wallmounted Rotatable Signlike Drawtype Test Node"), + drawtype = "signlike", + paramtype = "light", + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + tiles = { "testnodes_signlike.png^[colorize:#FFFF00:40" }, + + + walkable = false, + groups = { dig_immediate = 3 }, + sunlight_propagates = true, +}) + + + +core.register_node("testnodes:plantlike", { + description = S("\"plantlike\" Drawtype Test Node"), + drawtype = "plantlike", + paramtype = "light", + tiles = { "testnodes_plantlike.png" }, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:plantlike_waving", { + description = S("Waving \"plantlike\" Drawtype Test Node").."\n".. + S("Waves if waving plants are enabled by client"), + drawtype = "plantlike", + paramtype = "light", + tiles = { "testnodes_plantlike_waving.png" }, + waving = 1, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:plantlike_wallmounted", { + description = S("Wallmounted \"plantlike\" Drawtype Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + drawtype = "plantlike", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { "testnodes_plantlike_wallmounted.png" }, + leveled = 1, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + + +-- param2 will rotate +local function rotate_on_rightclick(pos, node, clicker) + local def = core.registered_nodes[node.name] + local aux1 = clicker:get_player_control().aux1 + + local deg, deg_max + local color, color_mult = 0, 0 + if def.paramtype2 == "degrotate" then + deg = node.param2 + deg_max = 240 + elseif def.paramtype2 == "colordegrotate" then + -- MSB [3x color, 5x rotation] LSB + deg = node.param2 % 2^5 + deg_max = 24 + color_mult = 2^5 + color = math.floor(node.param2 / color_mult) + end + + deg = (deg + (aux1 and 10 or 1)) % deg_max + node.param2 = color * color_mult + deg + core.swap_node(pos, node) + core.chat_send_player(clicker:get_player_name(), + "Rotation is now " .. deg .. " / " .. deg_max) +end + +core.register_node("testnodes:plantlike_degrotate", { + description = S("Degrotate \"plantlike\" Drawtype Test Node").."\n".. + S("param2 = horizontal rotation (0..239)"), + drawtype = "plantlike", + paramtype = "light", + paramtype2 = "degrotate", + tiles = { "testnodes_plantlike_degrotate.png" }, + + on_rightclick = rotate_on_rightclick, + place_param2 = 7, + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:mesh_degrotate", { + description = S("Degrotate \"mesh\" Drawtype Test Node").."\n".. + S("param2 = horizontal rotation (0..239)"), + drawtype = "mesh", + paramtype = "light", + paramtype2 = "degrotate", + mesh = "testnodes_ocorner.obj", + tiles = { "testnodes_mesh_stripes7.png" }, + + on_rightclick = rotate_on_rightclick, + place_param2 = 10, -- 15° + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:mesh_colordegrotate", { + description = S("Colordegrotate \"mesh\" Drawtype Test Node").."\n".. + S("param2 = color + horizontal rotation (0..23, 32..55, ...)"), + drawtype = "mesh", + paramtype = "light", + paramtype2 = "colordegrotate", + palette = "testnodes_palette_facedir.png", + mesh = "testnodes_ocorner.obj", + tiles = { "testnodes_mesh_stripes8.png" }, + + on_rightclick = rotate_on_rightclick, + -- color index 1, 1 step (=15°) rotated + place_param2 = 1 * 2^5 + 1, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +-- param2 will change height +core.register_node("testnodes:plantlike_leveled", { + description = S("Leveled \"plantlike\" Drawtype Test Node").."\n".. + S("param2 = height (0..255)"), + drawtype = "plantlike", + paramtype = "light", + paramtype2 = "leveled", + tiles = { + { name = "testnodes_plantlike_leveled.png", tileable_vertical = true }, + }, + + + -- We set a default param2 here only for convenience, to make the "plant" visible after placement + place_param2 = 8, + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +-- param2 changes shape +core.register_node("testnodes:plantlike_meshoptions", { + description = S("Meshoptions \"plantlike\" Drawtype Test Node").."\n".. + S("param2 = plant shape"), + drawtype = "plantlike", + paramtype = "light", + paramtype2 = "meshoptions", + tiles = { "testnodes_plantlike_meshoptions.png" }, + + + walkable = false, + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:plantlike_rooted", { + description = S("\"rooted_plantlike\" Drawtype Test Node"), + drawtype = "plantlike_rooted", + paramtype = "light", + tiles = { "testnodes_plantlike_rooted_base.png" }, + special_tiles = { "testnodes_plantlike_rooted.png" }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:plantlike_rooted_wallmounted", { + description = S("Wallmounted \"rooted_plantlike\" Drawtype Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + drawtype = "plantlike_rooted", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_wallmounted.png" }, + special_tiles = { "testnodes_plantlike_rooted_wallmounted.png" }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:plantlike_rooted_waving", { + description = S("Waving \"rooted_plantlike\" Drawtype Test Node").."\n".. + S("Waves if waving plants are enabled by client"), + drawtype = "plantlike_rooted", + paramtype = "light", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_waving.png", + }, + special_tiles = { "testnodes_plantlike_rooted_waving.png" }, + waving = 1, + + groups = { dig_immediate = 3 }, +}) + +-- param2 changes height +core.register_node("testnodes:plantlike_rooted_leveled", { + description = S("Leveled \"rooted_plantlike\" Drawtype Test Node").."\n".. + S("param2 = height (0..255)"), + drawtype = "plantlike_rooted", + paramtype = "light", + paramtype2 = "leveled", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_leveled.png", + }, + special_tiles = { + { name = "testnodes_plantlike_rooted_leveled.png", tileable_vertical = true }, + }, + + + -- We set a default param2 here only for convenience, to make the "plant" visible after placement + place_param2 = 8, + groups = { dig_immediate = 3 }, +}) + +-- param2 changes shape +core.register_node("testnodes:plantlike_rooted_meshoptions", { + description = S("Meshoptions \"rooted_plantlike\" Drawtype Test Node").."\n".. + S("param2 = plant shape"), + drawtype = "plantlike_rooted", + paramtype = "light", + paramtype2 = "meshoptions", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_meshoptions.png", + }, + special_tiles = { + "testnodes_plantlike_rooted_meshoptions.png", + }, + + groups = { dig_immediate = 3 }, +}) + +-- param2 changes rotation +core.register_node("testnodes:plantlike_rooted_degrotate", { + description = S("Degrotate \"rooted_plantlike\" Drawtype Test Node").."\n".. + S("param2 = horizontal rotation (0..239)"), + drawtype = "plantlike_rooted", + paramtype = "light", + paramtype2 = "degrotate", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_degrotate.png", + }, + special_tiles = { + "testnodes_plantlike_rooted_degrotate.png", + }, + + groups = { dig_immediate = 3 }, +}) + +-- Demonstrative liquid nodes, source and flowing form. +-- DRAWTYPE ONLY, NO LIQUID PHYSICS! +-- Liquid ranges 0 to 8 +for r = 0, 8 do + core.register_node("testnodes:liquid_"..r, { + description = S("\"liquid\" Drawtype Test Node, Range @1", r).."\n".. + S("Drawtype only; all liquid physics are disabled"), + drawtype = "liquid", + paramtype = "light", + tiles = { + "testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100", + }, + special_tiles = { + {name="testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false}, + {name="testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=true}, + }, + use_texture_alpha = "blend", + + + walkable = false, + liquid_range = r, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquid_flowing_"..r, + liquid_alternative_source = "testnodes:liquid_"..r, + groups = { dig_immediate = 3 }, + }) + core.register_node("testnodes:liquid_flowing_"..r, { + description = S("\"flowingliquid\" Drawtype Test Node, Range @1", r).."\n".. + S("Drawtype only; all liquid physics are disabled").."\n".. + S("param2 = flowing liquid level"), + drawtype = "flowingliquid", + paramtype = "light", + paramtype2 = "flowingliquid", + tiles = { + "testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100", + }, + special_tiles = { + {name="testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false}, + {name="testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false}, + }, + use_texture_alpha = "blend", + + + walkable = false, + liquid_range = r, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquid_flowing_"..r, + liquid_alternative_source = "testnodes:liquid_"..r, + groups = { dig_immediate = 3 }, + }) + +end + +-- Waving liquid test (drawtype only) +core.register_node("testnodes:liquid_waving", { + description = S("Waving \"liquid\" Drawtype Test Node").."\n".. + S("Drawtype only; all liquid physics are disabled").."\n".. + S("Waves if waving liquids are enabled by client"), + drawtype = "liquid", + paramtype = "light", + tiles = { + "testnodes_liquidsource.png^[colorize:#0000FF:127", + }, + special_tiles = { + {name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=false}, + {name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=true}, + }, + use_texture_alpha = "blend", + waving = 3, + + + walkable = false, + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquid_flowing_waving", + liquid_alternative_source = "testnodes:liquid_waving", + groups = { dig_immediate = 3 }, +}) +core.register_node("testnodes:liquid_flowing_waving", { + description = S("Waving \"flowingliquid\" Drawtype Test Node").."\n".. + S("Drawtype only; all liquid physics are disabled").."\n".. + S("param2 = flowing liquid level").."\n".. + S("Waves if waving liquids are enabled by client"), + drawtype = "flowingliquid", + paramtype = "light", + paramtype2 = "flowingliquid", + tiles = { + "testnodes_liquidflowing.png^[colorize:#0000FF:127", + }, + special_tiles = { + {name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false}, + {name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false}, + }, + use_texture_alpha = "blend", + waving = 3, + + + walkable = false, + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquid_flowing_waving", + liquid_alternative_source = "testnodes:liquid_waving", + groups = { dig_immediate = 3 }, +}) + +-- Invisible node +core.register_node("testnodes:airlike", { + description = S("\"airlike\" Drawtype Test Node").."\n".. + S("Invisible node").."\n".. + S("Inventory/wield image = no_texture_airlike.png"), + drawtype = "airlike", + paramtype = "light", + -- inventory/wield images are left empty to make sure the 'no texture' + -- fallback for airlike nodes is working properly. + + + walkable = false, + groups = { dig_immediate = 3 }, + sunlight_propagates = true, +}) + +-- param2 changes liquid height +core.register_node("testnodes:glassliquid", { + description = S("\"glasslike_framed\" Drawtype with Liquid Test Node").."\n".. + S("param2 = liquid level (0..63)"), + drawtype = "glasslike_framed", + paramtype = "light", + paramtype2 = "glasslikeliquidlevel", + tiles = { + "testnodes_glasslikeliquid.png", + }, + special_tiles = { + "testnodes_liquid.png", + }, + + groups = { dig_immediate = 3 }, +}) + +-- Adding many raillike examples, primarily to demonstrate the behavior of +-- "raillike groups". Nodes of the same type (rail, groupless, line, street) +-- should connect to nodes of the same "rail type" (=same shape, different +-- color) only. +local rails = { + { "rail", {"testnodes_rail_straight.png", "testnodes_rail_curved.png", "testnodes_rail_t_junction.png", "testnodes_rail_crossing.png"}, S("Connects to rails")}, + { "line", {"testnodes_line_straight.png", "testnodes_line_curved.png", "testnodes_line_t_junction.png", "testnodes_line_crossing.png"}, S("Connects to lines")}, + { "street", {"testnodes_street_straight.png", "testnodes_street_curved.png", "testnodes_street_t_junction.png", "testnodes_street_crossing.png"}, S("Connects to streets")}, + -- the "groupless" nodes are nodes in which the "connect_to_raillike" group is not set + { "groupless", {"testnodes_rail2_straight.png", "testnodes_rail2_curved.png", "testnodes_rail2_t_junction.png", "testnodes_rail2_crossing.png"}, S("Connects to 'groupless' rails") }, +} +local colors = { "", "cyan", "red" } + +for r=1, #rails do + local id = rails[r][1] + local tiles = rails[r][2] + local tt = rails[r][3] + local raillike_group + if id ~= "groupless" then + raillike_group = core.raillike_group(id) + end + for c=1, #colors do + local color + if colors[c] ~= "" then + color = colors[c] + end + core.register_node("testnodes:raillike_"..id..c, { + description = S("\"raillike\" Drawtype Test Node: @1 @2", id, c).."\n".. + tt, + drawtype = "raillike", + paramtype = "light", + tiles = tiles, + groups = { connect_to_raillike = raillike_group, dig_immediate = 3 }, + + + color = color, + selection_box = { + type = "fixed", + fixed = {{-0.5, -0.5, -0.5, 0.5, -0.4, 0.5}}, + }, + sunlight_propagates = true, + walkable = false, + }) + end +end + + + +-- Add visual_scale variants of previous nodes for half and double size +local scale = function(subname, append) + local original = "testnodes:"..subname + local def = table.copy(core.registered_items[original]) + local orig_desc + if append and type(append) == "string" then + orig_desc = ItemStack(original):get_short_description() + orig_desc = orig_desc .. "\n" .. append + elseif append ~= false then + orig_desc = ItemStack(original):get_description() + else + orig_desc = ItemStack(original):get_short_description() + end + def.visual_scale = 2.0 + def.description = S("Double-sized @1", orig_desc) + core.register_node("testnodes:"..subname.."_double", def) + def = table.copy(core.registered_items[original]) + def.visual_scale = 0.5 + def.description = S("Half-sized @1", orig_desc) + core.register_node("testnodes:"..subname.."_half", def) +end + +local allfaces_newsize_tt = "".. + S("Rendering depends on 'leaves_style' setting:").."\n".. + S("* 'fancy'/'simple': transparent").."\n".. + S("* 'opaque': opaque") + +scale("allfaces", S("Transparent node")) +scale("allfaces_optional", allfaces_newsize_tt) +scale("allfaces_optional_waving", allfaces_newsize_tt .. "\n" .. S("Waving if waving leaves are enabled by client")) +scale("plantlike") +scale("plantlike_wallmounted") +scale("torchlike_wallmounted") +scale("signlike_wallmounted") +scale("firelike") diff --git a/data/games/garage/mods/testnodes/init.lua b/data/games/garage/mods/testnodes/init.lua new file mode 100644 index 0000000..49a45e0 --- /dev/null +++ b/data/games/garage/mods/testnodes/init.lua @@ -0,0 +1,13 @@ +local path = core.get_modpath(core.get_current_modname()) + +dofile(path.."/drawtypes.lua") +dofile(path.."/meshes.lua") +dofile(path.."/nodeboxes.lua") +dofile(path.."/param2.lua") +dofile(path.."/performance_test_nodes.lua") +dofile(path.."/properties.lua") +dofile(path.."/liquids.lua") +dofile(path.."/light.lua") +dofile(path.."/textures.lua") +dofile(path.."/overlays.lua") +dofile(path.."/commands.lua") diff --git a/data/games/garage/mods/testnodes/light.lua b/data/games/garage/mods/testnodes/light.lua new file mode 100644 index 0000000..0f1ccb7 --- /dev/null +++ b/data/games/garage/mods/testnodes/light.lua @@ -0,0 +1,50 @@ +-- Test Nodes: Light test + +local S = core.get_translator("testnodes") + +-- All possible light levels +for i=1, core.LIGHT_MAX do + core.register_node("testnodes:light"..string.format("%02d", i), { + description = S("Light Source (@1)", i), + paramtype = "light", + light_source = i, + + + tiles ={"testnodes_light_"..i..".png"}, + drawtype = "glasslike", + walkable = false, + sunlight_propagates = true, + is_ground_content = false, + groups = {dig_immediate=3}, + }) +end + +-- Lets light through, but not sunlight, leading to a +-- reduction in light level when light passes through +core.register_node("testnodes:sunlight_filter", { + description = S("Sunlight Filter") .."\n".. + S("Lets light through, but weakens sunlight"), + paramtype = "light", + + + drawtype = "glasslike", + tiles = { + "testnodes_sunlight_filter.png", + }, + groups = { dig_immediate = 3 }, +}) + +-- Lets light and sunlight through without obstruction +core.register_node("testnodes:sunlight_propagator", { + description = S("Sunlight Propagator") .."\n".. + S("Lets all light through"), + paramtype = "light", + sunlight_propagates = true, + + + drawtype = "glasslike", + tiles = { + "testnodes_sunlight_filter.png^[brighten", + }, + groups = { dig_immediate = 3 }, +}) diff --git a/data/games/garage/mods/testnodes/liquids.lua b/data/games/garage/mods/testnodes/liquids.lua new file mode 100644 index 0000000..130b2a7 --- /dev/null +++ b/data/games/garage/mods/testnodes/liquids.lua @@ -0,0 +1,157 @@ +-- Add liquids for ranges and viscosity levels 0-8 + +for d=0, 8 do + local tt_normal + if d == 0 then + tt_normal = "\n".."Swimmable, renewable liquid" + else + tt_normal = "\n".."Swimmable, spreading, renewable liquid" + end + core.register_node("testnodes:rliquid_"..d, { + description = "Test Liquid Source, Range "..d.. + tt_normal .. "\n" .. "(falling & floating node)", + drawtype = "liquid", + tiles = {"testnodes_liquidsource_r"..d..".png"}, + special_tiles = { + {name = "testnodes_liquidsource_r"..d..".png", backface_culling = false}, + {name = "testnodes_liquidsource_r"..d..".png", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + liquid_alternative_flowing = "testnodes:rliquid_flowing_"..d, + liquid_alternative_source = "testnodes:rliquid_"..d, + liquid_range = d, + -- Also use these nodes to test falling, floating liquid source nodes + groups = {float = 1, falling_node = 1}, + }) + + core.register_node("testnodes:rliquid_flowing_"..d, { + description = "Flowing Test Liquid, Range "..d.. + tt_normal, + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing_r"..d..".png"}, + special_tiles = { + {name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false}, + {name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "flowing", + liquid_alternative_flowing = "testnodes:rliquid_flowing_"..d, + liquid_alternative_source = "testnodes:rliquid_"..d, + liquid_range = d, + }) + + if d <= 7 then + + local tt_reduced = "" + if d > 0 then + tt_reduced = "\n".."Reduced swimming/spreading speed" + end + + local mod = "^[colorize:#000000:127" + core.register_node("testnodes:vliquid_"..d, { + description = "Test Liquid Source, Viscosity/Resistance "..d.."\n".. + "Swimmable, spreading, renewable liquid".. + tt_reduced, + drawtype = "liquid", + tiles = {"testnodes_liquidsource_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + liquid_alternative_flowing = "testnodes:vliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vliquid_"..d, + liquid_viscosity = d, + }) + + core.register_node("testnodes:vliquid_flowing_"..d, { + description = "Flowing Test Liquid, Viscosity/Resistance "..d.."\n".. + "Swimmable, spreading, renewable liquid".. + tt_reduced, + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "flowing", + liquid_alternative_flowing = "testnodes:vliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vliquid_"..d, + liquid_viscosity = d, + }) + + mod = "^[colorize:#000000:192" + local v = 4 + core.register_node("testnodes:vrliquid_"..d, { + description = "Test Liquid Source, Viscosity "..v..", Resistance "..d.."\n".. + "Swimmable, spreading, renewable liquid".. + tt_reduced, + drawtype = "liquid", + tiles = {"testnodes_liquidsource_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + liquid_alternative_flowing = "testnodes:vrliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vrliquid_"..d, + liquid_viscosity = v, + move_resistance = d, + }) + + core.register_node("testnodes:vrliquid_flowing_"..d, { + description = "Flowing Test Liquid, Viscosity "..v..", Resistance "..d.."\n".. + "Swimmable, spreading, renewable liquid".. + tt_reduced, + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "flowing", + liquid_alternative_flowing = "testnodes:vrliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vrliquid_"..d, + liquid_viscosity = v, + move_resistance = d, + }) + + end + +end diff --git a/data/games/garage/mods/testnodes/meshes.lua b/data/games/garage/mods/testnodes/meshes.lua new file mode 100644 index 0000000..4fafa8a --- /dev/null +++ b/data/games/garage/mods/testnodes/meshes.lua @@ -0,0 +1,179 @@ +-- Meshes + +local S = core.get_translator("testnodes") + +local ocorner_cbox = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, -0.25, 0.5}, + {-0.5, -0.25, -0.25, 0.25, 0, 0.5}, + {-0.5, 0, 0, 0, 0.25, 0.5}, + {-0.5, 0.25, 0.25, -0.25, 0.5, 0.5} + } +} + +local tall_pyr_cbox = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }, + { -0.375, -0.25, -0.375, 0.375, 0, 0.375}, + { -0.25, 0, -0.25, 0.25, 0.25, 0.25}, + { -0.125, 0.25, -0.125, 0.125, 0.5, 0.125} + } +} + +-- Normal mesh +core.register_node("testnodes:mesh", { + description = S("Mesh Test Node"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes2.png"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + + groups = {dig_immediate=3}, +}) + +-- Facedir mesh: outer corner slope +core.register_node("testnodes:mesh_facedir", { + description = S("Facedir Mesh Test Node").."\n".. + S("param2 = facedir rotation (0..23)"), + drawtype = "mesh", + mesh = "testnodes_ocorner.obj", + tiles = {"testnodes_mesh_stripes.png"}, + paramtype = "light", + paramtype2 = "facedir", + collision_box = ocorner_cbox, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:mesh_colorfacedir", { + description = S("Color Facedir Mesh Test Node").."\n".. + S("param2 = color + facedir rotation (0..23, 32..55, ...)"), + drawtype = "mesh", + mesh = "testnodes_ocorner.obj", + tiles = {"testnodes_mesh_stripes3.png"}, + paramtype = "light", + paramtype2 = "colorfacedir", + palette = "testnodes_palette_facedir.png", + collision_box = ocorner_cbox, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:mesh_4dir", { + description = S("4dir Mesh Test Node").."\n".. + S("param2 = 4dir rotation (0..3)"), + drawtype = "mesh", + mesh = "testnodes_ocorner.obj", + tiles = {"testnodes_mesh_stripes5.png"}, + paramtype = "light", + paramtype2 = "4dir", + collision_box = ocorner_cbox, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:mesh_color4dir", { + description = S("Color 4dir Mesh Test Node").."\n".. + S("param2 = color + 4dir rotation (0..255)"), + drawtype = "mesh", + mesh = "testnodes_ocorner.obj", + tiles = {"testnodes_mesh_stripes6.png"}, + paramtype = "light", + paramtype2 = "color4dir", + palette = "testnodes_palette_4dir.png", + collision_box = ocorner_cbox, + + groups = {dig_immediate=3}, +}) + +-- Wallmounted mesh: pyramid +core.register_node("testnodes:mesh_wallmounted", { + description = S("Wallmounted Mesh Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes9.png"}, + paramtype = "light", + paramtype2 = "wallmounted", + collision_box = tall_pyr_cbox, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:mesh_colorwallmounted", { + description = S("Color Wallmounted Mesh Test Node").."\n".. + S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes10.png"}, + paramtype = "light", + paramtype2 = "colorwallmounted", + palette = "testnodes_palette_wallmounted.png", + collision_box = tall_pyr_cbox, + + groups = {dig_immediate=3}, +}) + + +core.register_node("testnodes:mesh_double", { + description = S("Double-sized Mesh Test Node"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes2.png"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + visual_scale = 2, + + groups = {dig_immediate=3}, +}) +core.register_node("testnodes:mesh_half", { + description = S("Half-sized Mesh Test Node"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes2.png"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + visual_scale = 0.5, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:mesh_waving1", { + description = S("Plantlike-waving Mesh Test Node").."\n".. + S("Waves if waving plants are enabled by client"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes4.png^[multiply:#B0FFB0"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + waving = 1, + + groups = {dig_immediate=3}, +}) +core.register_node("testnodes:mesh_waving2", { + description = S("Leaflike-waving Mesh Test Node").."\n".. + S("Waves if waving leaves are enabled by client"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes4.png^[multiply:#FFFFB0"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + waving = 2, + + groups = {dig_immediate=3}, +}) +core.register_node("testnodes:mesh_waving3", { + description = S("Liquidlike-waving Mesh Test Node").."\n".. + S("Waves if waving liquids are enabled by client"), + drawtype = "mesh", + mesh = "testnodes_pyramid.obj", + tiles = {"testnodes_mesh_stripes4.png^[multiply:#B0B0FF"}, + paramtype = "light", + collision_box = tall_pyr_cbox, + waving = 3, + + groups = {dig_immediate=3}, +}) diff --git a/data/games/garage/mods/testnodes/mod.conf b/data/games/garage/mods/testnodes/mod.conf new file mode 100644 index 0000000..d894c34 --- /dev/null +++ b/data/games/garage/mods/testnodes/mod.conf @@ -0,0 +1,3 @@ +name = testnodes +description = Contains a bunch of basic example nodes for demonstrative purposes, development and testing +depends = stairs diff --git a/data/games/garage/mods/testnodes/models/testnodes_marble_glass.obj b/data/games/garage/mods/testnodes/models/testnodes_marble_glass.obj new file mode 100644 index 0000000..5acd93e --- /dev/null +++ b/data/games/garage/mods/testnodes/models/testnodes_marble_glass.obj @@ -0,0 +1,1619 @@ +# Blender 3.3.1 +# www.blender.org +o Cube +v 0.323728 0.323728 -0.323728 +v 0.323728 -0.323728 -0.323728 +v 0.323728 0.323728 0.323728 +v 0.323728 -0.323728 0.323728 +v -0.323728 0.323728 -0.323728 +v -0.323728 -0.323728 -0.323728 +v -0.323728 0.323728 0.323728 +v -0.323728 -0.323728 0.323728 +v -0.396318 -0.396318 0.000000 +v 0.000000 -0.396318 -0.396318 +v 0.396318 0.000000 -0.396318 +v -0.396318 0.000000 0.396318 +v 0.396318 0.000000 0.396318 +v -0.396318 0.000000 -0.396318 +v 0.000000 0.396318 0.396318 +v 0.396318 0.396318 0.000000 +v 0.000000 -0.396318 0.396318 +v -0.396318 0.396318 0.000000 +v 0.000000 0.396318 -0.396318 +v 0.396318 -0.396318 0.000000 +v 0.000000 0.561355 -0.000000 +v 0.000000 0.000000 0.561355 +v -0.561355 0.000000 0.000000 +v 0.000000 -0.561355 -0.000000 +v 0.561355 0.000000 -0.000000 +v -0.000000 0.000000 -0.561355 +v -0.378043 -0.378043 -0.169966 +v -0.378043 -0.378043 0.169966 +v 0.169966 -0.378043 -0.378043 +v -0.169966 -0.378043 -0.378043 +v 0.378043 0.169966 -0.378043 +v 0.378043 -0.169966 -0.378043 +v -0.378043 -0.169966 0.378043 +v -0.378043 0.169966 0.378043 +v 0.378043 0.169966 0.378043 +v 0.378043 -0.169966 0.378043 +v -0.378043 0.169966 -0.378043 +v -0.378043 -0.169966 -0.378043 +v 0.169966 0.378043 0.378043 +v -0.169966 0.378043 0.378043 +v 0.378043 0.378043 -0.169966 +v 0.378043 0.378043 0.169966 +v -0.169966 -0.378043 0.378043 +v 0.169966 -0.378043 0.378043 +v -0.378043 0.378043 0.169966 +v -0.378043 0.378043 -0.169966 +v -0.169966 0.378043 -0.378043 +v 0.169966 0.378043 -0.378043 +v 0.378043 -0.378043 0.169966 +v 0.378043 -0.378043 -0.169966 +v 0.000000 0.518474 -0.214562 +v -0.214562 0.518474 -0.000000 +v 0.000000 0.518474 0.214562 +v 0.214562 0.518474 -0.000000 +v 0.214562 0.000000 0.518474 +v 0.000000 0.214562 0.518474 +v -0.214562 0.000000 0.518474 +v 0.000000 -0.214562 0.518474 +v -0.518474 0.000000 0.214562 +v -0.518474 0.214562 0.000000 +v -0.518474 0.000000 -0.214562 +v -0.518474 -0.214562 0.000000 +v 0.000000 -0.518474 -0.214562 +v 0.214562 -0.518474 -0.000000 +v 0.000000 -0.518474 0.214562 +v -0.214562 -0.518474 -0.000000 +v 0.518474 0.000000 -0.214562 +v 0.518474 0.214562 -0.000000 +v 0.518474 0.000000 0.214562 +v 0.518474 -0.214562 -0.000000 +v -0.214562 0.000000 -0.518474 +v -0.000000 0.214562 -0.518474 +v 0.214562 0.000000 -0.518474 +v -0.000000 -0.214562 -0.518474 +v 0.205792 0.479498 -0.205792 +v -0.205792 0.479498 -0.205792 +v -0.205792 0.479498 0.205792 +v 0.205792 0.479498 0.205792 +v 0.205792 -0.205792 0.479498 +v 0.205792 0.205792 0.479498 +v -0.205792 0.205792 0.479498 +v -0.205792 -0.205792 0.479498 +v -0.479498 -0.205792 0.205792 +v -0.479498 0.205792 0.205792 +v -0.479498 0.205792 -0.205792 +v -0.479498 -0.205792 -0.205792 +v -0.205792 -0.479498 -0.205792 +v 0.205792 -0.479498 -0.205792 +v 0.205792 -0.479498 0.205792 +v -0.205792 -0.479498 0.205792 +v 0.479498 -0.205792 -0.205792 +v 0.479498 0.205792 -0.205792 +v 0.479498 0.205792 0.205792 +v 0.479498 -0.205792 0.205792 +v -0.205792 -0.205792 -0.479498 +v -0.205792 0.205792 -0.479498 +v 0.205792 0.205792 -0.479498 +v 0.205792 -0.205792 -0.479498 +v -0.355165 -0.355165 -0.249858 +v -0.391777 -0.391777 -0.085992 +v -0.391777 -0.391777 0.085992 +v -0.355165 -0.355165 0.249858 +v 0.249858 -0.355165 -0.355165 +v 0.085992 -0.391777 -0.391777 +v -0.085992 -0.391777 -0.391777 +v -0.249858 -0.355165 -0.355165 +v 0.355165 0.249858 -0.355165 +v 0.391777 0.085992 -0.391777 +v 0.391777 -0.085992 -0.391777 +v 0.355165 -0.249858 -0.355165 +v -0.355165 -0.249858 0.355165 +v -0.391777 -0.085992 0.391777 +v -0.391777 0.085992 0.391777 +v -0.355165 0.249858 0.355165 +v 0.355165 0.249858 0.355165 +v 0.391777 0.085992 0.391777 +v 0.391777 -0.085992 0.391777 +v 0.355165 -0.249858 0.355165 +v -0.355165 0.249858 -0.355165 +v -0.391777 0.085992 -0.391777 +v -0.391777 -0.085992 -0.391777 +v -0.355165 -0.249858 -0.355165 +v 0.249858 0.355165 0.355165 +v 0.085992 0.391777 0.391777 +v -0.085992 0.391777 0.391777 +v -0.249858 0.355165 0.355165 +v 0.355165 0.355165 -0.249858 +v 0.391777 0.391777 -0.085992 +v 0.391777 0.391777 0.085992 +v 0.355165 0.355165 0.249858 +v -0.249858 -0.355165 0.355165 +v -0.085992 -0.391777 0.391777 +v 0.085992 -0.391777 0.391777 +v 0.249858 -0.355165 0.355165 +v -0.355165 0.355165 0.249858 +v -0.391777 0.391777 0.085992 +v -0.391777 0.391777 -0.085992 +v -0.355165 0.355165 -0.249858 +v -0.249858 0.355165 -0.355165 +v -0.085992 0.391777 -0.391777 +v 0.085992 0.391777 -0.391777 +v 0.249858 0.355165 -0.355165 +v 0.355165 -0.355165 0.249858 +v 0.391777 -0.391777 0.085992 +v 0.391777 -0.391777 -0.085992 +v 0.355165 -0.355165 -0.249858 +v 0.000000 0.466551 -0.311553 +v 0.000000 0.550455 -0.109376 +v -0.311553 0.466551 0.000000 +v -0.109376 0.550455 0.000000 +v 0.000000 0.466551 0.311553 +v 0.000000 0.550455 0.109376 +v 0.311553 0.466551 0.000000 +v 0.109376 0.550455 0.000000 +v 0.311553 0.000000 0.466551 +v 0.109376 0.000000 0.550455 +v 0.000000 0.311553 0.466551 +v 0.000000 0.109376 0.550455 +v -0.311553 0.000000 0.466551 +v -0.109376 0.000000 0.550455 +v 0.000000 -0.311553 0.466551 +v 0.000000 -0.109376 0.550455 +v -0.466551 0.000000 0.311553 +v -0.550455 0.000000 0.109376 +v -0.466551 0.311553 0.000000 +v -0.550455 0.109376 0.000000 +v -0.466551 0.000000 -0.311553 +v -0.550455 0.000000 -0.109376 +v -0.466551 -0.311553 0.000000 +v -0.550455 -0.109376 0.000000 +v 0.000000 -0.466551 -0.311553 +v 0.000000 -0.550455 -0.109376 +v 0.311553 -0.466551 0.000000 +v 0.109376 -0.550455 0.000000 +v 0.000000 -0.466551 0.311553 +v 0.000000 -0.550455 0.109376 +v -0.311553 -0.466551 0.000000 +v -0.109376 -0.550455 0.000000 +v 0.466551 0.000000 -0.311553 +v 0.550455 0.000000 -0.109376 +v 0.466551 0.311553 0.000000 +v 0.550455 0.109376 0.000000 +v 0.466551 0.000000 0.311553 +v 0.550455 0.000000 0.109376 +v 0.466551 -0.311553 0.000000 +v 0.550455 -0.109376 0.000000 +v -0.311553 0.000000 -0.466551 +v -0.109376 0.000000 -0.550455 +v 0.000000 0.311553 -0.466551 +v 0.000000 0.109376 -0.550455 +v 0.311553 0.000000 -0.466551 +v 0.109376 0.000000 -0.550455 +v 0.000000 -0.311553 -0.466551 +v 0.000000 -0.109376 -0.550455 +v 0.190929 0.435731 -0.296656 +v 0.104690 0.507685 -0.213841 +v 0.213841 0.507685 -0.104690 +v 0.296656 0.435731 -0.190929 +v -0.190929 0.435731 -0.296656 +v -0.296656 0.435731 -0.190929 +v -0.213841 0.507685 -0.104690 +v -0.104690 0.507685 -0.213841 +v -0.213841 0.507685 0.104690 +v -0.296656 0.435731 0.190929 +v -0.190929 0.435731 0.296656 +v -0.104690 0.507685 0.213841 +v 0.213841 0.507685 0.104690 +v 0.104690 0.507685 0.213841 +v 0.190929 0.435731 0.296656 +v 0.296656 0.435731 0.190929 +v 0.296656 -0.190929 0.435731 +v 0.213841 -0.104690 0.507685 +v 0.104690 -0.213841 0.507685 +v 0.190929 -0.296656 0.435731 +v 0.296656 0.190929 0.435731 +v 0.190929 0.296656 0.435731 +v 0.104690 0.213841 0.507685 +v 0.213841 0.104690 0.507685 +v -0.104690 0.213841 0.507685 +v -0.190929 0.296656 0.435731 +v -0.296656 0.190929 0.435731 +v -0.213841 0.104690 0.507685 +v -0.104690 -0.213841 0.507685 +v -0.213841 -0.104690 0.507685 +v -0.296656 -0.190929 0.435731 +v -0.190929 -0.296656 0.435731 +v -0.435731 -0.190929 0.296656 +v -0.507685 -0.104690 0.213841 +v -0.507685 -0.213841 0.104690 +v -0.435731 -0.296656 0.190929 +v -0.435731 0.190929 0.296656 +v -0.435731 0.296656 0.190929 +v -0.507685 0.213841 0.104690 +v -0.507685 0.104690 0.213841 +v -0.507685 0.213841 -0.104690 +v -0.435731 0.296656 -0.190929 +v -0.435731 0.190929 -0.296656 +v -0.507685 0.104690 -0.213841 +v -0.507685 -0.213841 -0.104690 +v -0.507685 -0.104690 -0.213841 +v -0.435731 -0.190929 -0.296656 +v -0.435731 -0.296656 -0.190929 +v -0.190929 -0.435731 -0.296656 +v -0.104690 -0.507685 -0.213841 +v -0.213841 -0.507685 -0.104690 +v -0.296656 -0.435731 -0.190929 +v 0.190929 -0.435731 -0.296656 +v 0.296656 -0.435731 -0.190929 +v 0.213841 -0.507685 -0.104690 +v 0.104690 -0.507685 -0.213841 +v 0.213841 -0.507685 0.104690 +v 0.296656 -0.435731 0.190929 +v 0.190929 -0.435731 0.296656 +v 0.104690 -0.507685 0.213841 +v -0.213841 -0.507685 0.104690 +v -0.104690 -0.507685 0.213841 +v -0.190929 -0.435731 0.296656 +v -0.296656 -0.435731 0.190929 +v 0.435731 -0.190929 -0.296656 +v 0.507685 -0.104690 -0.213841 +v 0.507685 -0.213841 -0.104690 +v 0.435731 -0.296656 -0.190929 +v 0.435731 0.190929 -0.296656 +v 0.435731 0.296656 -0.190929 +v 0.507685 0.213841 -0.104690 +v 0.507685 0.104690 -0.213841 +v 0.507685 0.213841 0.104690 +v 0.435731 0.296656 0.190929 +v 0.435731 0.190929 0.296656 +v 0.507685 0.104690 0.213841 +v 0.507685 -0.213841 0.104690 +v 0.507685 -0.104690 0.213841 +v 0.435731 -0.190929 0.296656 +v 0.435731 -0.296656 0.190929 +v -0.296656 -0.190929 -0.435731 +v -0.213841 -0.104690 -0.507685 +v -0.104690 -0.213841 -0.507685 +v -0.190929 -0.296656 -0.435731 +v -0.296656 0.190929 -0.435731 +v -0.190929 0.296656 -0.435731 +v -0.104690 0.213841 -0.507685 +v -0.213841 0.104690 -0.507685 +v 0.104690 0.213841 -0.507685 +v 0.190929 0.296656 -0.435731 +v 0.296656 0.190929 -0.435731 +v 0.213841 0.104690 -0.507685 +v 0.104690 -0.213841 -0.507685 +v 0.213841 -0.104690 -0.507685 +v 0.296656 -0.190929 -0.435731 +v 0.190929 -0.296656 -0.435731 +v 0.277006 0.400861 -0.277006 +v 0.097104 0.458010 -0.308740 +v 0.109074 0.539153 -0.109074 +v 0.308740 0.458010 -0.097104 +v -0.097104 0.458010 -0.308740 +v -0.277006 0.400861 -0.277006 +v -0.308740 0.458010 -0.097104 +v -0.109074 0.539153 -0.109074 +v -0.109074 0.539153 0.109074 +v -0.308740 0.458010 0.097104 +v -0.277006 0.400861 0.277006 +v -0.097104 0.458010 0.308740 +v 0.308740 0.458010 0.097104 +v 0.109074 0.539153 0.109074 +v 0.097104 0.458010 0.308740 +v 0.277006 0.400861 0.277006 +v 0.277006 -0.277006 0.400861 +v 0.308740 -0.097104 0.458010 +v 0.109074 -0.109074 0.539153 +v 0.097104 -0.308740 0.458010 +v 0.308740 0.097104 0.458010 +v 0.277006 0.277006 0.400861 +v 0.097104 0.308740 0.458010 +v 0.109074 0.109074 0.539153 +v -0.109074 0.109074 0.539153 +v -0.097104 0.308740 0.458010 +v -0.277006 0.277006 0.400861 +v -0.308740 0.097104 0.458010 +v -0.097104 -0.308740 0.458010 +v -0.109074 -0.109074 0.539153 +v -0.308740 -0.097104 0.458010 +v -0.277006 -0.277006 0.400861 +v -0.400861 -0.277006 0.277006 +v -0.458010 -0.097104 0.308740 +v -0.539153 -0.109074 0.109074 +v -0.458010 -0.308740 0.097104 +v -0.458010 0.097104 0.308740 +v -0.400861 0.277006 0.277006 +v -0.458010 0.308740 0.097104 +v -0.539153 0.109074 0.109074 +v -0.539153 0.109074 -0.109074 +v -0.458010 0.308740 -0.097104 +v -0.400861 0.277006 -0.277006 +v -0.458010 0.097104 -0.308740 +v -0.458010 -0.308740 -0.097104 +v -0.539153 -0.109074 -0.109074 +v -0.458010 -0.097104 -0.308740 +v -0.400861 -0.277006 -0.277006 +v -0.277006 -0.400861 -0.277006 +v -0.097104 -0.458010 -0.308740 +v -0.109074 -0.539153 -0.109074 +v -0.308740 -0.458010 -0.097104 +v 0.097104 -0.458010 -0.308740 +v 0.277006 -0.400861 -0.277006 +v 0.308740 -0.458010 -0.097104 +v 0.109074 -0.539153 -0.109074 +v 0.109074 -0.539153 0.109074 +v 0.308740 -0.458010 0.097104 +v 0.277006 -0.400861 0.277006 +v 0.097104 -0.458010 0.308740 +v -0.308740 -0.458010 0.097104 +v -0.109074 -0.539153 0.109074 +v -0.097104 -0.458010 0.308740 +v -0.277006 -0.400861 0.277006 +v 0.400861 -0.277006 -0.277006 +v 0.458010 -0.097104 -0.308740 +v 0.539153 -0.109074 -0.109074 +v 0.458010 -0.308740 -0.097104 +v 0.458010 0.097104 -0.308740 +v 0.400861 0.277006 -0.277006 +v 0.458010 0.308740 -0.097104 +v 0.539153 0.109074 -0.109074 +v 0.539153 0.109074 0.109074 +v 0.458010 0.308740 0.097104 +v 0.400861 0.277006 0.277006 +v 0.458010 0.097104 0.308740 +v 0.458010 -0.308740 0.097104 +v 0.539153 -0.109074 0.109074 +v 0.458010 -0.097104 0.308740 +v 0.400861 -0.277006 0.277006 +v -0.277006 -0.277006 -0.400861 +v -0.308740 -0.097104 -0.458010 +v -0.109074 -0.109074 -0.539153 +v -0.097104 -0.308740 -0.458010 +v -0.308740 0.097104 -0.458010 +v -0.277006 0.277006 -0.400861 +v -0.097104 0.308740 -0.458010 +v -0.109074 0.109074 -0.539153 +v 0.109074 0.109074 -0.539153 +v 0.097104 0.308740 -0.458010 +v 0.277006 0.277006 -0.400861 +v 0.308740 0.097104 -0.458010 +v 0.097104 -0.308740 -0.458010 +v 0.109074 -0.109074 -0.539153 +v 0.308740 -0.097104 -0.458010 +v 0.277006 -0.277006 -0.400861 +vn 0.5774 0.5774 -0.5774 +vn 0.4530 0.6304 -0.6304 +vn 0.4902 0.7207 -0.4902 +vn 0.6304 0.6304 -0.4530 +vn 0.3090 0.6725 -0.6725 +vn 0.3371 0.7812 -0.5254 +vn 0.3638 0.8575 -0.3638 +vn 0.5254 0.7812 -0.3371 +vn 0.6725 0.6725 -0.3090 +vn 0.1560 0.6984 -0.6984 +vn 0.1715 0.8189 -0.5477 +vn -0.0000 0.7071 -0.7071 +vn -0.0000 0.8326 -0.5539 +vn -0.0000 0.9246 -0.3809 +vn 0.1869 0.9066 -0.3783 +vn 0.1950 0.9612 -0.1950 +vn 0.3783 0.9066 -0.1869 +vn -0.0000 0.9806 -0.1961 +vn -0.0000 1.0000 -0.0000 +vn 0.1961 0.9806 -0.0000 +vn 0.3809 0.9246 -0.0000 +vn 0.5477 0.8189 -0.1715 +vn 0.6984 0.6984 -0.1560 +vn 0.5539 0.8326 -0.0000 +vn 0.7071 0.7071 -0.0000 +vn -0.1560 0.6984 -0.6984 +vn -0.1715 0.8189 -0.5477 +vn -0.3090 0.6725 -0.6725 +vn -0.3371 0.7812 -0.5254 +vn -0.3638 0.8575 -0.3638 +vn -0.1869 0.9066 -0.3783 +vn -0.4530 0.6304 -0.6304 +vn -0.4902 0.7207 -0.4902 +vn -0.5774 0.5774 -0.5774 +vn -0.6304 0.6304 -0.4530 +vn -0.6725 0.6725 -0.3090 +vn -0.5254 0.7812 -0.3371 +vn -0.5477 0.8189 -0.1715 +vn -0.3783 0.9066 -0.1869 +vn -0.6984 0.6984 -0.1560 +vn -0.7071 0.7071 -0.0000 +vn -0.5539 0.8326 -0.0000 +vn -0.3809 0.9246 -0.0000 +vn -0.1950 0.9612 -0.1950 +vn -0.1961 0.9806 -0.0000 +vn -0.1950 0.9612 0.1950 +vn -0.0000 0.9806 0.1961 +vn -0.3783 0.9066 0.1869 +vn -0.3638 0.8575 0.3638 +vn -0.1869 0.9066 0.3783 +vn -0.0000 0.9246 0.3809 +vn -0.5477 0.8189 0.1715 +vn -0.6984 0.6984 0.1560 +vn -0.6725 0.6725 0.3090 +vn -0.5254 0.7812 0.3371 +vn -0.4902 0.7207 0.4902 +vn -0.3371 0.7812 0.5254 +vn -0.6304 0.6304 0.4530 +vn -0.5774 0.5774 0.5774 +vn -0.4530 0.6304 0.6304 +vn -0.3090 0.6725 0.6725 +vn -0.1715 0.8189 0.5477 +vn -0.0000 0.8326 0.5539 +vn -0.1560 0.6984 0.6984 +vn -0.0000 0.7071 0.7071 +vn 0.5477 0.8189 0.1715 +vn 0.6984 0.6984 0.1560 +vn 0.3783 0.9066 0.1869 +vn 0.3638 0.8575 0.3638 +vn 0.5254 0.7812 0.3371 +vn 0.6725 0.6725 0.3090 +vn 0.1950 0.9612 0.1950 +vn 0.1869 0.9066 0.3783 +vn 0.1715 0.8189 0.5477 +vn 0.3371 0.7812 0.5254 +vn 0.1560 0.6984 0.6984 +vn 0.3090 0.6725 0.6725 +vn 0.4902 0.7207 0.4902 +vn 0.6304 0.6304 0.4530 +vn 0.4530 0.6304 0.6304 +vn 0.5774 0.5774 0.5774 +vn 0.5774 -0.5774 0.5774 +vn 0.6304 -0.4530 0.6304 +vn 0.4902 -0.4902 0.7207 +vn 0.4530 -0.6304 0.6304 +vn 0.6725 -0.3090 0.6725 +vn 0.5254 -0.3371 0.7812 +vn 0.3638 -0.3638 0.8575 +vn 0.3371 -0.5254 0.7812 +vn 0.3090 -0.6725 0.6725 +vn 0.6984 -0.1560 0.6984 +vn 0.5477 -0.1715 0.8189 +vn 0.7071 -0.0000 0.7071 +vn 0.5539 -0.0000 0.8326 +vn 0.3809 -0.0000 0.9246 +vn 0.3783 -0.1869 0.9066 +vn 0.1950 -0.1950 0.9612 +vn 0.1869 -0.3783 0.9066 +vn 0.1961 -0.0000 0.9806 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -0.1961 0.9806 +vn -0.0000 -0.3809 0.9246 +vn 0.1715 -0.5477 0.8189 +vn 0.1560 -0.6984 0.6984 +vn -0.0000 -0.5539 0.8326 +vn -0.0000 -0.7071 0.7071 +vn 0.6984 0.1560 0.6984 +vn 0.5477 0.1715 0.8189 +vn 0.6725 0.3090 0.6725 +vn 0.5254 0.3371 0.7812 +vn 0.3638 0.3638 0.8575 +vn 0.3783 0.1869 0.9066 +vn 0.6304 0.4530 0.6304 +vn 0.4902 0.4902 0.7207 +vn 0.3371 0.5254 0.7812 +vn 0.1715 0.5477 0.8189 +vn 0.1869 0.3783 0.9066 +vn -0.0000 0.5539 0.8326 +vn -0.0000 0.3809 0.9246 +vn 0.1950 0.1950 0.9612 +vn -0.0000 0.1961 0.9806 +vn -0.1950 0.1950 0.9612 +vn -0.1961 -0.0000 0.9806 +vn -0.1869 0.3783 0.9066 +vn -0.3638 0.3638 0.8575 +vn -0.3783 0.1869 0.9066 +vn -0.3809 -0.0000 0.9246 +vn -0.1715 0.5477 0.8189 +vn -0.3371 0.5254 0.7812 +vn -0.4902 0.4902 0.7207 +vn -0.5254 0.3371 0.7812 +vn -0.6304 0.4530 0.6304 +vn -0.6725 0.3090 0.6725 +vn -0.5477 0.1715 0.8189 +vn -0.5539 -0.0000 0.8326 +vn -0.6984 0.1560 0.6984 +vn -0.7071 -0.0000 0.7071 +vn -0.1715 -0.5477 0.8189 +vn -0.1560 -0.6984 0.6984 +vn -0.1869 -0.3783 0.9066 +vn -0.3638 -0.3638 0.8575 +vn -0.3371 -0.5254 0.7812 +vn -0.3090 -0.6725 0.6725 +vn -0.1950 -0.1950 0.9612 +vn -0.3783 -0.1869 0.9066 +vn -0.5477 -0.1715 0.8189 +vn -0.5254 -0.3371 0.7812 +vn -0.6984 -0.1560 0.6984 +vn -0.6725 -0.3090 0.6725 +vn -0.4902 -0.4902 0.7207 +vn -0.4530 -0.6304 0.6304 +vn -0.6304 -0.4530 0.6304 +vn -0.5774 -0.5774 0.5774 +vn -0.7207 -0.4902 0.4902 +vn -0.6304 -0.6304 0.4530 +vn -0.7812 -0.3371 0.5254 +vn -0.8575 -0.3638 0.3638 +vn -0.7812 -0.5254 0.3371 +vn -0.6725 -0.6725 0.3090 +vn -0.8189 -0.1715 0.5477 +vn -0.8326 -0.0000 0.5539 +vn -0.9246 -0.0000 0.3809 +vn -0.9066 -0.1869 0.3783 +vn -0.9612 -0.1950 0.1950 +vn -0.9066 -0.3783 0.1869 +vn -0.9806 -0.0000 0.1961 +vn -1.0000 -0.0000 -0.0000 +vn -0.9806 -0.1961 -0.0000 +vn -0.9246 -0.3809 -0.0000 +vn -0.8189 -0.5477 0.1715 +vn -0.6984 -0.6984 0.1560 +vn -0.8326 -0.5539 -0.0000 +vn -0.7071 -0.7071 -0.0000 +vn -0.8189 0.1715 0.5477 +vn -0.7812 0.3371 0.5254 +vn -0.8575 0.3638 0.3638 +vn -0.9066 0.1869 0.3783 +vn -0.7207 0.4902 0.4902 +vn -0.7812 0.5254 0.3371 +vn -0.8189 0.5477 0.1715 +vn -0.9066 0.3783 0.1869 +vn -0.8326 0.5539 -0.0000 +vn -0.9246 0.3809 -0.0000 +vn -0.9612 0.1950 0.1950 +vn -0.9806 0.1961 -0.0000 +vn -0.9612 0.1950 -0.1950 +vn -0.9806 -0.0000 -0.1961 +vn -0.9066 0.3783 -0.1869 +vn -0.8575 0.3638 -0.3638 +vn -0.9066 0.1869 -0.3783 +vn -0.9246 -0.0000 -0.3809 +vn -0.8189 0.5477 -0.1715 +vn -0.7812 0.5254 -0.3371 +vn -0.7207 0.4902 -0.4902 +vn -0.7812 0.3371 -0.5254 +vn -0.6304 0.4530 -0.6304 +vn -0.6725 0.3090 -0.6725 +vn -0.8189 0.1715 -0.5477 +vn -0.8326 -0.0000 -0.5539 +vn -0.6984 0.1560 -0.6984 +vn -0.7071 -0.0000 -0.7071 +vn -0.8189 -0.5477 -0.1715 +vn -0.6984 -0.6984 -0.1560 +vn -0.9066 -0.3783 -0.1869 +vn -0.8575 -0.3638 -0.3638 +vn -0.7812 -0.5254 -0.3371 +vn -0.6725 -0.6725 -0.3090 +vn -0.9612 -0.1950 -0.1950 +vn -0.9066 -0.1869 -0.3783 +vn -0.8189 -0.1715 -0.5477 +vn -0.7812 -0.3371 -0.5254 +vn -0.6984 -0.1560 -0.6984 +vn -0.6725 -0.3090 -0.6725 +vn -0.7207 -0.4902 -0.4902 +vn -0.6304 -0.6304 -0.4530 +vn -0.6304 -0.4530 -0.6304 +vn -0.5774 -0.5774 -0.5774 +vn -0.4530 -0.6304 -0.6304 +vn -0.4902 -0.7207 -0.4902 +vn -0.3090 -0.6725 -0.6725 +vn -0.3371 -0.7812 -0.5254 +vn -0.3638 -0.8575 -0.3638 +vn -0.5254 -0.7812 -0.3371 +vn -0.1560 -0.6984 -0.6984 +vn -0.1715 -0.8189 -0.5477 +vn -0.0000 -0.7071 -0.7071 +vn -0.0000 -0.8326 -0.5539 +vn -0.0000 -0.9246 -0.3809 +vn -0.1869 -0.9066 -0.3783 +vn -0.1950 -0.9612 -0.1950 +vn -0.3783 -0.9066 -0.1869 +vn -0.0000 -0.9806 -0.1961 +vn -0.0000 -1.0000 -0.0000 +vn -0.1961 -0.9806 -0.0000 +vn -0.3809 -0.9246 -0.0000 +vn -0.5477 -0.8189 -0.1715 +vn -0.5539 -0.8326 -0.0000 +vn 0.1560 -0.6984 -0.6984 +vn 0.1715 -0.8189 -0.5477 +vn 0.3090 -0.6725 -0.6725 +vn 0.3371 -0.7812 -0.5254 +vn 0.3638 -0.8575 -0.3638 +vn 0.1869 -0.9066 -0.3783 +vn 0.4530 -0.6304 -0.6304 +vn 0.4902 -0.7207 -0.4902 +vn 0.5774 -0.5774 -0.5774 +vn 0.6304 -0.6304 -0.4530 +vn 0.6725 -0.6725 -0.3090 +vn 0.5254 -0.7812 -0.3371 +vn 0.5477 -0.8189 -0.1715 +vn 0.3783 -0.9066 -0.1869 +vn 0.6984 -0.6984 -0.1560 +vn 0.7071 -0.7071 -0.0000 +vn 0.5539 -0.8326 -0.0000 +vn 0.3809 -0.9246 -0.0000 +vn 0.1950 -0.9612 -0.1950 +vn 0.1961 -0.9806 -0.0000 +vn 0.1950 -0.9612 0.1950 +vn -0.0000 -0.9806 0.1961 +vn 0.3783 -0.9066 0.1869 +vn 0.3638 -0.8575 0.3638 +vn 0.1869 -0.9066 0.3783 +vn -0.0000 -0.9246 0.3809 +vn 0.5477 -0.8189 0.1715 +vn 0.6984 -0.6984 0.1560 +vn 0.6725 -0.6725 0.3090 +vn 0.5254 -0.7812 0.3371 +vn 0.4902 -0.7207 0.4902 +vn 0.3371 -0.7812 0.5254 +vn 0.6304 -0.6304 0.4530 +vn 0.1715 -0.8189 0.5477 +vn -0.0000 -0.8326 0.5539 +vn -0.5477 -0.8189 0.1715 +vn -0.3783 -0.9066 0.1869 +vn -0.3638 -0.8575 0.3638 +vn -0.5254 -0.7812 0.3371 +vn -0.1950 -0.9612 0.1950 +vn -0.1869 -0.9066 0.3783 +vn -0.1715 -0.8189 0.5477 +vn -0.3371 -0.7812 0.5254 +vn -0.4902 -0.7207 0.4902 +vn 0.6304 -0.4530 -0.6304 +vn 0.7207 -0.4902 -0.4902 +vn 0.6725 -0.3090 -0.6725 +vn 0.7812 -0.3371 -0.5254 +vn 0.8575 -0.3638 -0.3638 +vn 0.7812 -0.5254 -0.3371 +vn 0.6984 -0.1560 -0.6984 +vn 0.8189 -0.1715 -0.5477 +vn 0.7071 -0.0000 -0.7071 +vn 0.8326 -0.0000 -0.5539 +vn 0.9246 -0.0000 -0.3809 +vn 0.9066 -0.1869 -0.3783 +vn 0.9612 -0.1950 -0.1950 +vn 0.9066 -0.3783 -0.1869 +vn 0.9806 -0.0000 -0.1961 +vn 1.0000 -0.0000 -0.0000 +vn 0.9806 -0.1961 -0.0000 +vn 0.9246 -0.3809 -0.0000 +vn 0.8189 -0.5477 -0.1715 +vn 0.8326 -0.5539 -0.0000 +vn 0.6984 0.1560 -0.6984 +vn 0.8189 0.1715 -0.5477 +vn 0.6725 0.3090 -0.6725 +vn 0.7812 0.3371 -0.5254 +vn 0.8575 0.3638 -0.3638 +vn 0.9066 0.1869 -0.3783 +vn 0.6304 0.4530 -0.6304 +vn 0.7207 0.4902 -0.4902 +vn 0.7812 0.5254 -0.3371 +vn 0.8189 0.5477 -0.1715 +vn 0.9066 0.3783 -0.1869 +vn 0.8326 0.5539 -0.0000 +vn 0.9246 0.3809 -0.0000 +vn 0.9612 0.1950 -0.1950 +vn 0.9806 0.1961 -0.0000 +vn 0.9612 0.1950 0.1950 +vn 0.9806 -0.0000 0.1961 +vn 0.9066 0.3783 0.1869 +vn 0.8575 0.3638 0.3638 +vn 0.9066 0.1869 0.3783 +vn 0.9246 -0.0000 0.3809 +vn 0.8189 0.5477 0.1715 +vn 0.7812 0.5254 0.3371 +vn 0.7207 0.4902 0.4902 +vn 0.7812 0.3371 0.5254 +vn 0.8189 0.1715 0.5477 +vn 0.8326 -0.0000 0.5539 +vn 0.8189 -0.5477 0.1715 +vn 0.9066 -0.3783 0.1869 +vn 0.8575 -0.3638 0.3638 +vn 0.7812 -0.5254 0.3371 +vn 0.9612 -0.1950 0.1950 +vn 0.9066 -0.1869 0.3783 +vn 0.8189 -0.1715 0.5477 +vn 0.7812 -0.3371 0.5254 +vn 0.7207 -0.4902 0.4902 +vn -0.4902 -0.4902 -0.7207 +vn -0.5254 -0.3371 -0.7812 +vn -0.3638 -0.3638 -0.8575 +vn -0.3371 -0.5254 -0.7812 +vn -0.5477 -0.1715 -0.8189 +vn -0.5539 -0.0000 -0.8326 +vn -0.3809 -0.0000 -0.9246 +vn -0.3783 -0.1869 -0.9066 +vn -0.1950 -0.1950 -0.9612 +vn -0.1869 -0.3783 -0.9066 +vn -0.1961 -0.0000 -0.9806 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 -0.1961 -0.9806 +vn -0.0000 -0.3809 -0.9246 +vn -0.1715 -0.5477 -0.8189 +vn -0.0000 -0.5539 -0.8326 +vn -0.5477 0.1715 -0.8189 +vn -0.5254 0.3371 -0.7812 +vn -0.3638 0.3638 -0.8575 +vn -0.3783 0.1869 -0.9066 +vn -0.4902 0.4902 -0.7207 +vn -0.3371 0.5254 -0.7812 +vn -0.1715 0.5477 -0.8189 +vn -0.1869 0.3783 -0.9066 +vn -0.0000 0.5539 -0.8326 +vn -0.0000 0.3809 -0.9246 +vn -0.1950 0.1950 -0.9612 +vn -0.0000 0.1961 -0.9806 +vn 0.1950 0.1950 -0.9612 +vn 0.1961 -0.0000 -0.9806 +vn 0.1869 0.3783 -0.9066 +vn 0.3638 0.3638 -0.8575 +vn 0.3783 0.1869 -0.9066 +vn 0.3809 -0.0000 -0.9246 +vn 0.1715 0.5477 -0.8189 +vn 0.3371 0.5254 -0.7812 +vn 0.4902 0.4902 -0.7207 +vn 0.5254 0.3371 -0.7812 +vn 0.5477 0.1715 -0.8189 +vn 0.5539 -0.0000 -0.8326 +vn 0.1715 -0.5477 -0.8189 +vn 0.1869 -0.3783 -0.9066 +vn 0.3638 -0.3638 -0.8575 +vn 0.3371 -0.5254 -0.7812 +vn 0.1950 -0.1950 -0.9612 +vn 0.3783 -0.1869 -0.9066 +vn 0.5477 -0.1715 -0.8189 +vn 0.5254 -0.3371 -0.7812 +vn 0.4902 -0.4902 -0.7207 +vt 0.500000 1.000000 +vt 1.000000 0.500000 +vt -0.000000 0.500000 +vt 0.500000 1.000000 +vt 1.000000 0.000000 +vt -0.000000 0.000000 +vt 0.500000 0.500000 +vt 0.500000 0.500000 +vt 0.500000 0.000000 +vt -0.000000 1.000000 +vt 0.500000 0.500000 +vt 1.000000 1.000000 +vt 0.500000 0.000000 +vt -0.000000 0.500000 +vt 1.000000 0.500000 +vt -0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 0.500000 +vt 0.750000 0.000000 +vt 1.000000 0.750000 +vt 0.750000 1.000000 +vt 0.250000 0.000000 +vt 1.000000 0.250000 +vt -0.000000 0.250000 +vt -0.000000 0.250000 +vt 1.000000 0.250000 +vt 0.500000 0.250000 +vt 0.500000 0.250000 +vt 0.250000 0.500000 +vt 0.500000 0.750000 +vt 0.750000 0.500000 +vt 0.250000 0.000000 +vt 0.750000 0.500000 +vt -0.000000 0.750000 +vt 0.750000 0.500000 +vt 0.250000 1.000000 +vt 0.250000 0.500000 +vt 0.500000 0.750000 +vt 0.750000 0.000000 +vt 0.250000 0.750001 +vt 0.250000 0.250000 +vt 0.750000 0.250000 +vt 0.750000 0.750000 +vt 0.750000 0.250000 +vt 0.250000 0.250000 +vt 0.625000 0.000000 +vt 1.000000 0.875000 +vt 0.875000 0.000000 +vt 1.000000 0.625000 +vt 0.625000 1.000000 +vt 0.125000 0.000000 +vt 0.875000 1.000000 +vt 0.375000 0.000000 +vt 1.000000 0.375000 +vt -0.000000 0.375000 +vt 1.000000 0.125000 +vt -0.000000 0.125000 +vt -0.000000 0.125000 +vt 1.000000 0.125000 +vt -0.000000 0.375000 +vt 1.000000 0.375000 +vt 0.500000 0.375000 +vt 0.500000 0.125000 +vt 0.500000 0.375000 +vt 0.500000 0.125000 +vt 0.375000 0.500000 +vt 0.125000 0.500000 +vt 0.500000 0.875000 +vt 0.875000 0.500000 +vt 0.500000 0.625000 +vt 0.625000 0.500000 +vt 0.125000 0.000000 +vt 0.875000 0.500000 +vt 0.375000 -0.000000 +vt 0.625000 0.500000 +vt -0.000000 0.625000 +vt 0.875000 0.500000 +vt -0.000000 0.875000 +vt 0.625000 0.500000 +vt 0.125000 1.000000 +vt 0.375000 0.500000 +vt 0.375000 1.000000 +vt 0.125000 0.500000 +vt 0.500000 0.625000 +vt 0.625000 0.000000 +vt 0.500000 0.875000 +vt 0.875000 0.000000 +vt 0.250000 0.875000 +vt 0.125000 0.750000 +vt 0.250000 0.625000 +vt 0.375000 0.750000 +vt 0.375000 0.250000 +vt 0.250000 0.375000 +vt 0.125000 0.250000 +vt 0.250000 0.125000 +vt 0.875000 0.250000 +vt 0.750000 0.375000 +vt 0.625000 0.250000 +vt 0.750000 0.125000 +vt 0.750000 0.875000 +vt 0.625000 0.750000 +vt 0.750000 0.625000 +vt 0.875000 0.750000 +vt 0.875000 0.250000 +vt 0.750000 0.375000 +vt 0.625000 0.250000 +vt 0.750000 0.125000 +vt 0.375000 0.250000 +vt 0.250000 0.375000 +vt 0.125000 0.250000 +vt 0.250000 0.125000 +vt 0.375000 0.875000 +vt 0.125000 0.875000 +vt 0.125000 0.625000 +vt 0.375000 0.625000 +vt 0.375000 0.125000 +vt 0.375000 0.375000 +vt 0.125000 0.375000 +vt 0.125000 0.125000 +vt 0.875000 0.125000 +vt 0.875000 0.375000 +vt 0.625000 0.375000 +vt 0.625000 0.125000 +vt 0.875000 0.875000 +vt 0.625000 0.875000 +vt 0.625000 0.625000 +vt 0.875000 0.625000 +vt 0.875000 0.125000 +vt 0.875000 0.375000 +vt 0.625000 0.375000 +vt 0.625000 0.125000 +vt 0.375000 0.125000 +vt 0.375000 0.375000 +vt 0.125000 0.375000 +vt 0.125000 0.125000 +vt 0.562500 0.000000 +vt 1.000000 0.937500 +vt 0.687500 0.000000 +vt 1.000000 0.812500 +vt 0.812500 0.000000 +vt 1.000000 0.687500 +vt 0.937500 0.000000 +vt 1.000000 0.562500 +vt 0.562500 1.000000 +vt 0.062500 0.000000 +vt 0.687500 1.000000 +vt 0.187500 0.000000 +vt 0.812500 1.000000 +vt 0.312500 0.000000 +vt 0.937500 1.000000 +vt 0.437500 0.000000 +vt 1.000000 0.437500 +vt -0.000000 0.437500 +vt 1.000000 0.312500 +vt -0.000000 0.312500 +vt 1.000000 0.187500 +vt -0.000000 0.187500 +vt 1.000000 0.062500 +vt -0.000000 0.062500 +vt -0.000000 0.062500 +vt 1.000000 0.062500 +vt -0.000000 0.187500 +vt 1.000000 0.187500 +vt -0.000000 0.312500 +vt 1.000000 0.312500 +vt -0.000000 0.437500 +vt 1.000000 0.437500 +vt 0.500000 0.437500 +vt 0.500000 0.312500 +vt 0.500000 0.187500 +vt 0.500000 0.062500 +vt 0.500000 0.437500 +vt 0.500000 0.312500 +vt 0.500000 0.187500 +vt 0.500000 0.062500 +vt 0.437500 0.500000 +vt 0.312500 0.500000 +vt 0.187500 0.500000 +vt 0.062500 0.500000 +vt 0.500000 0.937500 +vt 0.937500 0.500000 +vt 0.500000 0.812500 +vt 0.812500 0.500000 +vt 0.500000 0.687500 +vt 0.687500 0.500000 +vt 0.500000 0.562500 +vt 0.562500 0.500000 +vt 0.062500 0.000000 +vt 0.937500 0.500000 +vt 0.187500 0.000000 +vt 0.812500 0.500000 +vt 0.312500 -0.000000 +vt 0.687500 0.500000 +vt 0.437500 0.000000 +vt 0.562500 0.500000 +vt -0.000000 0.562500 +vt 0.937500 0.500000 +vt -0.000000 0.687500 +vt 0.812500 0.500000 +vt -0.000000 0.812500 +vt 0.687500 0.500000 +vt -0.000000 0.937500 +vt 0.562500 0.500000 +vt 0.062500 1.000000 +vt 0.437500 0.500000 +vt 0.187500 1.000000 +vt 0.312500 0.500000 +vt 0.312500 1.000000 +vt 0.187500 0.500000 +vt 0.437500 1.000000 +vt 0.062500 0.500000 +vt 0.500000 0.562500 +vt 0.562500 0.000000 +vt 0.500000 0.687500 +vt 0.687500 0.000000 +vt 0.500000 0.812500 +vt 0.812500 0.000000 +vt 0.500000 0.937500 +vt 0.937500 0.000000 +vt 0.250000 0.937500 +vt 0.250000 0.812501 +vt 0.062500 0.750000 +vt 0.187500 0.750001 +vt 0.250000 0.562500 +vt 0.250000 0.687500 +vt 0.437500 0.750000 +vt 0.312500 0.750001 +vt 0.437500 0.250000 +vt 0.312500 0.250000 +vt 0.250000 0.437500 +vt 0.250000 0.312500 +vt 0.062500 0.250000 +vt 0.187500 0.250000 +vt 0.250000 0.062500 +vt 0.250000 0.187500 +vt 0.937500 0.250000 +vt 0.812500 0.250000 +vt 0.750000 0.437500 +vt 0.750000 0.312500 +vt 0.562500 0.250000 +vt 0.687500 0.250000 +vt 0.750000 0.062500 +vt 0.750000 0.187500 +vt 0.750000 0.937500 +vt 0.750000 0.812500 +vt 0.562500 0.750000 +vt 0.687500 0.750000 +vt 0.750000 0.562500 +vt 0.750000 0.687500 +vt 0.937500 0.750000 +vt 0.812500 0.750000 +vt 0.937500 0.250000 +vt 0.812500 0.250000 +vt 0.750000 0.437500 +vt 0.750000 0.312500 +vt 0.562500 0.250000 +vt 0.687500 0.250000 +vt 0.750000 0.062500 +vt 0.750000 0.187500 +vt 0.437500 0.250000 +vt 0.312500 0.250000 +vt 0.250000 0.437500 +vt 0.250000 0.312500 +vt 0.062500 0.250000 +vt 0.187500 0.250000 +vt 0.250000 0.062500 +vt 0.250000 0.187500 +vt 0.375000 0.937500 +vt 0.312500 0.875000 +vt 0.375000 0.812500 +vt 0.437500 0.875000 +vt 0.125000 0.937500 +vt 0.062500 0.875000 +vt 0.125000 0.812500 +vt 0.187500 0.875000 +vt 0.125000 0.687500 +vt 0.062500 0.625000 +vt 0.125000 0.562500 +vt 0.187500 0.625000 +vt 0.375000 0.687500 +vt 0.312500 0.625000 +vt 0.375000 0.562500 +vt 0.437500 0.625000 +vt 0.437500 0.125000 +vt 0.375000 0.187500 +vt 0.312500 0.125000 +vt 0.375000 0.062500 +vt 0.437500 0.375000 +vt 0.375000 0.437500 +vt 0.312500 0.375000 +vt 0.375000 0.312500 +vt 0.187500 0.375000 +vt 0.125000 0.437500 +vt 0.062500 0.375000 +vt 0.125000 0.312500 +vt 0.187500 0.125000 +vt 0.125000 0.187500 +vt 0.062500 0.125000 +vt 0.125000 0.062500 +vt 0.937500 0.125000 +vt 0.875000 0.187500 +vt 0.812500 0.125000 +vt 0.875000 0.062500 +vt 0.937500 0.375000 +vt 0.875000 0.437500 +vt 0.812500 0.375000 +vt 0.875000 0.312500 +vt 0.687500 0.375000 +vt 0.625000 0.437500 +vt 0.562500 0.375000 +vt 0.625000 0.312500 +vt 0.687500 0.125000 +vt 0.625000 0.187500 +vt 0.562500 0.125000 +vt 0.625000 0.062500 +vt 0.875000 0.937500 +vt 0.812500 0.875000 +vt 0.875000 0.812500 +vt 0.937500 0.875000 +vt 0.625000 0.937500 +vt 0.562500 0.875000 +vt 0.625000 0.812500 +vt 0.687500 0.875000 +vt 0.625000 0.687500 +vt 0.562500 0.625000 +vt 0.625000 0.562500 +vt 0.687500 0.625000 +vt 0.875000 0.687500 +vt 0.812500 0.625000 +vt 0.875000 0.562500 +vt 0.937500 0.625000 +vt 0.937500 0.125000 +vt 0.875000 0.187500 +vt 0.812500 0.125000 +vt 0.875000 0.062500 +vt 0.937500 0.375000 +vt 0.875000 0.437500 +vt 0.812500 0.375000 +vt 0.875000 0.312500 +vt 0.687500 0.375000 +vt 0.625000 0.437500 +vt 0.562500 0.375000 +vt 0.625000 0.312500 +vt 0.687500 0.125000 +vt 0.625000 0.187500 +vt 0.562500 0.125000 +vt 0.625000 0.062500 +vt 0.437500 0.125000 +vt 0.375000 0.187500 +vt 0.312500 0.125000 +vt 0.375000 0.062500 +vt 0.437500 0.375000 +vt 0.375000 0.437500 +vt 0.312500 0.375000 +vt 0.375000 0.312500 +vt 0.187500 0.375000 +vt 0.125000 0.437500 +vt 0.062500 0.375000 +vt 0.125000 0.312500 +vt 0.187500 0.125000 +vt 0.125000 0.187500 +vt 0.062500 0.125000 +vt 0.125000 0.062500 +vt 0.437500 0.937500 +vt 0.312500 0.937500 +vt 0.312500 0.812500 +vt 0.437500 0.812500 +vt 0.187500 0.937500 +vt 0.062500 0.937500 +vt 0.062500 0.812500 +vt 0.187500 0.812501 +vt 0.187500 0.687500 +vt 0.062500 0.687500 +vt 0.062500 0.562500 +vt 0.187500 0.562500 +vt 0.437500 0.687500 +vt 0.312500 0.687500 +vt 0.312500 0.562500 +vt 0.437500 0.562500 +vt 0.437500 0.062500 +vt 0.437500 0.187500 +vt 0.312500 0.187500 +vt 0.312500 0.062500 +vt 0.437500 0.312500 +vt 0.437500 0.437500 +vt 0.312500 0.437500 +vt 0.312500 0.312500 +vt 0.187500 0.312500 +vt 0.187500 0.437500 +vt 0.062500 0.437500 +vt 0.062500 0.312500 +vt 0.187500 0.062500 +vt 0.187500 0.187500 +vt 0.062500 0.187500 +vt 0.062500 0.062500 +vt 0.937500 0.062500 +vt 0.937500 0.187500 +vt 0.812500 0.187500 +vt 0.812500 0.062500 +vt 0.937500 0.312500 +vt 0.937500 0.437500 +vt 0.812500 0.437500 +vt 0.812500 0.312500 +vt 0.687500 0.312500 +vt 0.687500 0.437500 +vt 0.562500 0.437500 +vt 0.562500 0.312500 +vt 0.687500 0.062500 +vt 0.687500 0.187500 +vt 0.562500 0.187500 +vt 0.562500 0.062500 +vt 0.937500 0.937500 +vt 0.812500 0.937500 +vt 0.812500 0.812500 +vt 0.937500 0.812500 +vt 0.687500 0.937500 +vt 0.562500 0.937500 +vt 0.562500 0.812500 +vt 0.687500 0.812500 +vt 0.687500 0.687500 +vt 0.562500 0.687500 +vt 0.562500 0.562500 +vt 0.687500 0.562500 +vt 0.937500 0.687500 +vt 0.812500 0.687500 +vt 0.812500 0.562500 +vt 0.937500 0.562500 +vt 0.937500 0.062500 +vt 0.937500 0.187500 +vt 0.812500 0.187500 +vt 0.812500 0.062500 +vt 0.937500 0.312500 +vt 0.937500 0.437500 +vt 0.812500 0.437500 +vt 0.812500 0.312500 +vt 0.687500 0.312500 +vt 0.687500 0.437500 +vt 0.562500 0.437500 +vt 0.562500 0.312500 +vt 0.687500 0.062500 +vt 0.687500 0.187500 +vt 0.562500 0.187500 +vt 0.562500 0.062500 +vt 0.437500 0.062500 +vt 0.437500 0.187500 +vt 0.312500 0.187500 +vt 0.312500 0.062500 +vt 0.437500 0.312500 +vt 0.437500 0.437500 +vt 0.312500 0.437500 +vt 0.312500 0.312500 +vt 0.187500 0.312500 +vt 0.187500 0.437500 +vt 0.062500 0.437500 +vt 0.062500 0.312500 +vt 0.187500 0.062500 +vt 0.187500 0.187500 +vt 0.062500 0.187500 +vt 0.062500 0.062500 +s 1 +f 1/1/1 142/210/2 291/364/3 127/180/4 +f 142/210/2 48/82/5 195/268/6 291/364/3 +f 291/364/3 195/268/6 75/112/7 198/271/8 +f 127/180/4 291/364/3 198/271/8 41/68/9 +f 48/82/5 141/208/10 292/365/11 195/268/6 +f 141/208/10 19/36/12 147/220/13 292/365/11 +f 292/365/11 147/220/13 51/88/14 196/269/15 +f 195/268/6 292/365/11 196/269/15 75/112/7 +f 75/112/7 196/269/15 293/366/16 197/270/17 +f 196/269/15 51/88/14 148/221/18 293/366/16 +f 293/366/16 148/221/18 21/40/19 154/227/20 +f 197/270/17 293/366/16 154/227/20 54/91/21 +f 41/68/9 198/271/8 294/367/22 128/182/23 +f 198/271/8 75/112/7 197/270/17 294/367/22 +f 294/367/22 197/270/17 54/91/21 153/226/24 +f 128/182/23 294/367/22 153/226/24 16/30/25 +f 19/36/12 140/206/26 295/368/27 147/220/13 +f 140/206/26 47/80/28 199/272/29 295/368/27 +f 295/368/27 199/272/29 76/113/30 202/275/31 +f 147/220/13 295/368/27 202/275/31 51/88/14 +f 47/80/28 139/204/32 296/369/33 199/272/29 +f 139/204/32 5/10/34 138/202/35 296/369/33 +f 296/369/33 138/202/35 46/78/36 200/273/37 +f 199/272/29 296/369/33 200/273/37 76/113/30 +f 76/113/30 200/273/37 297/370/38 201/274/39 +f 200/273/37 46/78/36 137/200/40 297/370/38 +f 297/370/38 137/200/40 18/34/41 149/222/42 +f 201/274/39 297/370/38 149/222/42 52/89/43 +f 51/88/14 202/275/31 298/371/44 148/221/18 +f 202/275/31 76/113/30 201/274/39 298/371/44 +f 298/371/44 201/274/39 52/89/43 150/223/45 +f 148/221/18 298/371/44 150/223/45 21/40/19 +f 21/40/19 150/223/45 299/372/46 152/225/47 +f 150/223/45 52/89/43 203/276/48 299/372/46 +f 299/372/46 203/276/48 77/114/49 206/279/50 +f 152/225/47 299/372/46 206/279/50 53/90/51 +f 52/89/43 149/222/42 300/373/52 203/276/48 +f 149/222/42 18/34/41 136/198/53 300/373/52 +f 300/373/52 136/198/53 45/76/54 204/277/55 +f 203/276/48 300/373/52 204/277/55 77/114/49 +f 77/114/49 204/277/55 301/374/56 205/278/57 +f 204/277/55 45/76/54 135/196/58 301/374/56 +f 301/374/56 135/196/58 7/14/59 126/179/60 +f 205/278/57 301/374/56 126/179/60 40/67/61 +f 53/90/51 206/279/50 302/375/62 151/224/63 +f 206/279/50 77/114/49 205/278/57 302/375/62 +f 302/375/62 205/278/57 40/67/61 125/178/64 +f 151/224/63 302/375/62 125/178/64 15/29/65 +f 16/30/25 153/226/24 303/376/66 129/184/67 +f 153/226/24 54/91/21 207/280/68 303/376/66 +f 303/376/66 207/280/68 78/115/69 210/283/70 +f 129/184/67 303/376/66 210/283/70 42/70/71 +f 54/91/21 154/227/20 304/377/72 207/280/68 +f 154/227/20 21/40/19 152/225/47 304/377/72 +f 304/377/72 152/225/47 53/90/51 208/281/73 +f 207/280/68 304/377/72 208/281/73 78/115/69 +f 78/115/69 208/281/73 305/378/74 209/282/75 +f 208/281/73 53/90/51 151/224/63 305/378/74 +f 305/378/74 151/224/63 15/29/65 124/177/76 +f 209/282/75 305/378/74 124/177/76 39/66/77 +f 42/70/71 210/283/70 306/379/78 130/186/79 +f 210/283/70 78/115/69 209/282/75 306/379/78 +f 306/379/78 209/282/75 39/66/77 123/176/80 +f 130/186/79 306/379/78 123/176/80 3/7/81 +f 4/9/82 118/171/83 307/380/84 134/194/85 +f 118/171/83 36/63/86 211/284/87 307/380/84 +f 307/380/84 211/284/87 79/116/88 214/287/89 +f 134/194/85 307/380/84 214/287/89 44/74/90 +f 36/63/86 117/170/91 308/381/92 211/284/87 +f 117/170/91 13/27/93 155/228/94 308/381/92 +f 308/381/92 155/228/94 55/92/95 212/285/96 +f 211/284/87 308/381/92 212/285/96 79/116/88 +f 79/116/88 212/285/96 309/382/97 213/286/98 +f 212/285/96 55/92/95 156/229/99 309/382/97 +f 309/382/97 156/229/99 22/41/100 162/235/101 +f 213/286/98 309/382/97 162/235/101 58/95/102 +f 44/74/90 214/287/89 310/383/103 133/192/104 +f 214/287/89 79/116/88 213/286/98 310/383/103 +f 310/383/103 213/286/98 58/95/102 161/234/105 +f 133/192/104 310/383/103 161/234/105 17/32/106 +f 13/27/93 116/169/107 311/384/108 155/228/94 +f 116/169/107 35/62/109 215/288/110 311/384/108 +f 311/384/108 215/288/110 80/117/111 218/291/112 +f 155/228/94 311/384/108 218/291/112 55/92/95 +f 35/62/109 115/168/113 312/385/114 215/288/110 +f 115/168/113 3/7/81 123/176/80 312/385/114 +f 312/385/114 123/176/80 39/66/77 216/289/115 +f 215/288/110 312/385/114 216/289/115 80/117/111 +f 80/117/111 216/289/115 313/386/116 217/290/117 +f 216/289/115 39/66/77 124/177/76 313/386/116 +f 313/386/116 124/177/76 15/29/65 157/230/118 +f 217/290/117 313/386/116 157/230/118 56/93/119 +f 55/92/95 218/291/112 314/387/120 156/229/99 +f 218/291/112 80/117/111 217/290/117 314/387/120 +f 314/387/120 217/290/117 56/93/119 158/231/121 +f 156/229/99 314/387/120 158/231/121 22/41/100 +f 22/41/100 158/231/121 315/388/122 160/233/123 +f 158/231/121 56/93/119 219/292/124 315/388/122 +f 315/388/122 219/292/124 81/118/125 222/295/126 +f 160/233/123 315/388/122 222/295/126 57/94/127 +f 56/93/119 157/230/118 316/389/128 219/292/124 +f 157/230/118 15/29/65 125/178/64 316/389/128 +f 316/389/128 125/178/64 40/67/61 220/293/129 +f 219/292/124 316/389/128 220/293/129 81/118/125 +f 81/118/125 220/293/129 317/390/130 221/294/131 +f 220/293/129 40/67/61 126/179/60 317/390/130 +f 317/390/130 126/179/60 7/14/59 114/166/132 +f 221/294/131 317/390/130 114/166/132 34/60/133 +f 57/94/127 222/295/126 318/391/134 159/232/135 +f 222/295/126 81/118/125 221/294/131 318/391/134 +f 318/391/134 221/294/131 34/60/133 113/164/136 +f 159/232/135 318/391/134 113/164/136 12/25/137 +f 17/32/106 161/234/105 319/392/138 132/190/139 +f 161/234/105 58/95/102 223/296/140 319/392/138 +f 319/392/138 223/296/140 82/119/141 226/299/142 +f 132/190/139 319/392/138 226/299/142 43/72/143 +f 58/95/102 162/235/101 320/393/144 223/296/140 +f 162/235/101 22/41/100 160/233/123 320/393/144 +f 320/393/144 160/233/123 57/94/127 224/297/145 +f 223/296/140 320/393/144 224/297/145 82/119/141 +f 82/119/141 224/297/145 321/394/146 225/298/147 +f 224/297/145 57/94/127 159/232/135 321/394/146 +f 321/394/146 159/232/135 12/25/137 112/162/148 +f 225/298/147 321/394/146 112/162/148 33/58/149 +f 43/72/143 226/299/142 322/395/150 131/188/151 +f 226/299/142 82/119/141 225/298/147 322/395/150 +f 322/395/150 225/298/147 33/58/149 111/160/152 +f 131/188/151 322/395/150 111/160/152 8/16/153 +f 8/17/153 111/161/152 323/396/154 102/142/155 +f 111/161/152 33/59/149 227/300/156 323/396/154 +f 323/396/154 227/300/156 83/120/157 230/303/158 +f 102/142/155 323/396/154 230/303/158 28/48/159 +f 33/59/149 112/163/148 324/397/160 227/300/156 +f 112/163/148 12/26/137 163/236/161 324/397/160 +f 324/397/160 163/236/161 59/96/162 228/301/163 +f 227/300/156 324/397/160 228/301/163 83/120/157 +f 83/120/157 228/301/163 325/398/164 229/302/165 +f 228/301/163 59/96/162 164/237/166 325/398/164 +f 325/398/164 164/237/166 23/42/167 170/243/168 +f 229/302/165 325/398/164 170/243/168 62/99/169 +f 28/48/159 230/303/158 326/399/170 101/140/171 +f 230/303/158 83/120/157 229/302/165 326/399/170 +f 326/399/170 229/302/165 62/99/169 169/242/172 +f 101/140/171 326/399/170 169/242/172 9/19/173 +f 12/26/137 113/165/136 327/400/174 163/236/161 +f 113/165/136 34/61/133 231/304/175 327/400/174 +f 327/400/174 231/304/175 84/121/176 234/307/177 +f 163/236/161 327/400/174 234/307/177 59/96/162 +f 34/61/133 114/167/132 328/401/178 231/304/175 +f 114/167/132 7/15/59 135/197/58 328/401/178 +f 328/401/178 135/197/58 45/77/54 232/305/179 +f 231/304/175 328/401/178 232/305/179 84/121/176 +f 84/121/176 232/305/179 329/402/180 233/306/181 +f 232/305/179 45/77/54 136/199/53 329/402/180 +f 329/402/180 136/199/53 18/35/41 165/238/182 +f 233/306/181 329/402/180 165/238/182 60/97/183 +f 59/96/162 234/307/177 330/403/184 164/237/166 +f 234/307/177 84/121/176 233/306/181 330/403/184 +f 330/403/184 233/306/181 60/97/183 166/239/185 +f 164/237/166 330/403/184 166/239/185 23/42/167 +f 23/42/167 166/239/185 331/404/186 168/241/187 +f 166/239/185 60/97/183 235/308/188 331/404/186 +f 331/404/186 235/308/188 85/122/189 238/311/190 +f 168/241/187 331/404/186 238/311/190 61/98/191 +f 60/97/183 165/238/182 332/405/192 235/308/188 +f 165/238/182 18/35/41 137/201/40 332/405/192 +f 332/405/192 137/201/40 46/79/36 236/309/193 +f 235/308/188 332/405/192 236/309/193 85/122/189 +f 85/122/189 236/309/193 333/406/194 237/310/195 +f 236/309/193 46/79/36 138/203/35 333/406/194 +f 333/406/194 138/203/35 5/11/34 119/172/196 +f 237/310/195 333/406/194 119/172/196 37/64/197 +f 61/98/191 238/311/190 334/407/198 167/240/199 +f 238/311/190 85/122/189 237/310/195 334/407/198 +f 334/407/198 237/310/195 37/64/197 120/173/200 +f 167/240/199 334/407/198 120/173/200 14/28/201 +f 9/19/173 169/242/172 335/408/202 100/138/203 +f 169/242/172 62/99/169 239/312/204 335/408/202 +f 335/408/202 239/312/204 86/123/205 242/315/206 +f 100/138/203 335/408/202 242/315/206 27/46/207 +f 62/99/169 170/243/168 336/409/208 239/312/204 +f 170/243/168 23/42/167 168/241/187 336/409/208 +f 336/409/208 168/241/187 61/98/191 240/313/209 +f 239/312/204 336/409/208 240/313/209 86/123/205 +f 86/123/205 240/313/209 337/410/210 241/314/211 +f 240/313/209 61/98/191 167/240/199 337/410/210 +f 337/410/210 167/240/199 14/28/201 121/174/212 +f 241/314/211 337/410/210 121/174/212 38/65/213 +f 27/46/207 242/315/206 338/411/214 99/136/215 +f 242/315/206 86/123/205 241/314/211 338/411/214 +f 338/411/214 241/314/211 38/65/213 122/175/216 +f 99/136/215 338/411/214 122/175/216 6/13/217 +f 6/12/217 106/150/218 339/412/219 99/137/215 +f 106/150/218 30/52/220 243/316/221 339/412/219 +f 339/412/219 243/316/221 87/124/222 246/319/223 +f 99/137/215 339/412/219 246/319/223 27/47/207 +f 30/52/220 105/148/224 340/413/225 243/316/221 +f 105/148/224 10/21/226 171/244/227 340/413/225 +f 340/413/225 171/244/227 63/100/228 244/317/229 +f 243/316/221 340/413/225 244/317/229 87/124/222 +f 87/124/222 244/317/229 341/414/230 245/318/231 +f 244/317/229 63/100/228 172/245/232 341/414/230 +f 341/414/230 172/245/232 24/43/233 178/251/234 +f 245/318/231 341/414/230 178/251/234 66/103/235 +f 27/47/207 246/319/223 342/415/236 100/139/203 +f 246/319/223 87/124/222 245/318/231 342/415/236 +f 342/415/236 245/318/231 66/103/235 177/250/237 +f 100/139/203 342/415/236 177/250/237 9/20/173 +f 10/21/226 104/146/238 343/416/239 171/244/227 +f 104/146/238 29/50/240 247/320/241 343/416/239 +f 343/416/239 247/320/241 88/125/242 250/323/243 +f 171/244/227 343/416/239 250/323/243 63/100/228 +f 29/50/240 103/144/244 344/417/245 247/320/241 +f 103/144/244 2/4/246 146/218/247 344/417/245 +f 344/417/245 146/218/247 50/86/248 248/321/249 +f 247/320/241 344/417/245 248/321/249 88/125/242 +f 88/125/242 248/321/249 345/418/250 249/322/251 +f 248/321/249 50/86/248 145/216/252 345/418/250 +f 345/418/250 145/216/252 20/38/253 173/246/254 +f 249/322/251 345/418/250 173/246/254 64/101/255 +f 63/100/228 250/323/243 346/419/256 172/245/232 +f 250/323/243 88/125/242 249/322/251 346/419/256 +f 346/419/256 249/322/251 64/101/255 174/247/257 +f 172/245/232 346/419/256 174/247/257 24/43/233 +f 24/43/233 174/247/257 347/420/258 176/249/259 +f 174/247/257 64/101/255 251/324/260 347/420/258 +f 347/420/258 251/324/260 89/126/261 254/327/262 +f 176/249/259 347/420/258 254/327/262 65/102/263 +f 64/101/255 173/246/254 348/421/264 251/324/260 +f 173/246/254 20/38/253 144/214/265 348/421/264 +f 348/421/264 144/214/265 49/84/266 252/325/267 +f 251/324/260 348/421/264 252/325/267 89/126/261 +f 89/126/261 252/325/267 349/422/268 253/326/269 +f 252/325/267 49/84/266 143/212/270 349/422/268 +f 349/422/268 143/212/270 4/8/82 134/195/85 +f 253/326/269 349/422/268 134/195/85 44/75/90 +f 65/102/263 254/327/262 350/423/271 175/248/272 +f 254/327/262 89/126/261 253/326/269 350/423/271 +f 350/423/271 253/326/269 44/75/90 133/193/104 +f 175/248/272 350/423/271 133/193/104 17/33/106 +f 9/20/173 177/250/237 351/424/273 101/141/171 +f 177/250/237 66/103/235 255/328/274 351/424/273 +f 351/424/273 255/328/274 90/127/275 258/331/276 +f 101/141/171 351/424/273 258/331/276 28/49/159 +f 66/103/235 178/251/234 352/425/277 255/328/274 +f 178/251/234 24/43/233 176/249/259 352/425/277 +f 352/425/277 176/249/259 65/102/263 256/329/278 +f 255/328/274 352/425/277 256/329/278 90/127/275 +f 90/127/275 256/329/278 353/426/279 257/330/280 +f 256/329/278 65/102/263 175/248/272 353/426/279 +f 353/426/279 175/248/272 17/33/106 132/191/139 +f 257/330/280 353/426/279 132/191/139 43/73/143 +f 28/49/159 258/331/276 354/427/281 102/143/155 +f 258/331/276 90/127/275 257/330/280 354/427/281 +f 354/427/281 257/330/280 43/73/143 131/189/151 +f 102/143/155 354/427/281 131/189/151 8/18/153 +f 2/5/246 110/158/282 355/428/283 146/219/247 +f 110/158/282 32/56/284 259/332/285 355/428/283 +f 355/428/283 259/332/285 91/128/286 262/335/287 +f 146/219/247 355/428/283 262/335/287 50/87/248 +f 32/56/284 109/156/288 356/429/289 259/332/285 +f 109/156/288 11/23/290 179/252/291 356/429/289 +f 356/429/289 179/252/291 67/104/292 260/333/293 +f 259/332/285 356/429/289 260/333/293 91/128/286 +f 91/128/286 260/333/293 357/430/294 261/334/295 +f 260/333/293 67/104/292 180/253/296 357/430/294 +f 357/430/294 180/253/296 25/44/297 186/259/298 +f 261/334/295 357/430/294 186/259/298 70/107/299 +f 50/87/248 262/335/287 358/431/300 145/217/252 +f 262/335/287 91/128/286 261/334/295 358/431/300 +f 358/431/300 261/334/295 70/107/299 185/258/301 +f 145/217/252 358/431/300 185/258/301 20/39/253 +f 11/23/290 108/154/302 359/432/303 179/252/291 +f 108/154/302 31/54/304 263/336/305 359/432/303 +f 359/432/303 263/336/305 92/129/306 266/339/307 +f 179/252/291 359/432/303 266/339/307 67/104/292 +f 31/54/304 107/152/308 360/433/309 263/336/305 +f 107/152/308 1/2/1 127/181/4 360/433/309 +f 360/433/309 127/181/4 41/69/9 264/337/310 +f 263/336/305 360/433/309 264/337/310 92/129/306 +f 92/129/306 264/337/310 361/434/311 265/338/312 +f 264/337/310 41/69/9 128/183/23 361/434/311 +f 361/434/311 128/183/23 16/31/25 181/254/313 +f 265/338/312 361/434/311 181/254/313 68/105/314 +f 67/104/292 266/339/307 362/435/315 180/253/296 +f 266/339/307 92/129/306 265/338/312 362/435/315 +f 362/435/315 265/338/312 68/105/314 182/255/316 +f 180/253/296 362/435/315 182/255/316 25/44/297 +f 25/44/297 182/255/316 363/436/317 184/257/318 +f 182/255/316 68/105/314 267/340/319 363/436/317 +f 363/436/317 267/340/319 93/130/320 270/343/321 +f 184/257/318 363/436/317 270/343/321 69/106/322 +f 68/105/314 181/254/313 364/437/323 267/340/319 +f 181/254/313 16/31/25 129/185/67 364/437/323 +f 364/437/323 129/185/67 42/71/71 268/341/324 +f 267/340/319 364/437/323 268/341/324 93/130/320 +f 93/130/320 268/341/324 365/438/325 269/342/326 +f 268/341/324 42/71/71 130/187/79 365/438/325 +f 365/438/325 130/187/79 3/7/81 115/168/113 +f 269/342/326 365/438/325 115/168/113 35/62/109 +f 69/106/322 270/343/321 366/439/327 183/256/328 +f 270/343/321 93/130/320 269/342/326 366/439/327 +f 366/439/327 269/342/326 35/62/109 116/169/107 +f 183/256/328 366/439/327 116/169/107 13/27/93 +f 20/39/253 185/258/301 367/440/329 144/215/265 +f 185/258/301 70/107/299 271/344/330 367/440/329 +f 367/440/329 271/344/330 94/131/331 274/347/332 +f 144/215/265 367/440/329 274/347/332 49/85/266 +f 70/107/299 186/259/298 368/441/333 271/344/330 +f 186/259/298 25/44/297 184/257/318 368/441/333 +f 368/441/333 184/257/318 69/106/322 272/345/334 +f 271/344/330 368/441/333 272/345/334 94/131/331 +f 94/131/331 272/345/334 369/442/335 273/346/336 +f 272/345/334 69/106/322 183/256/328 369/442/335 +f 369/442/335 183/256/328 13/27/93 117/170/91 +f 273/346/336 369/442/335 117/170/91 36/63/86 +f 49/85/266 274/347/332 370/443/337 143/213/270 +f 274/347/332 94/131/331 273/346/336 370/443/337 +f 370/443/337 273/346/336 36/63/86 118/171/83 +f 143/213/270 370/443/337 118/171/83 4/9/82 +f 6/13/217 122/175/216 371/444/338 106/151/218 +f 122/175/216 38/65/213 275/348/339 371/444/338 +f 371/444/338 275/348/339 95/132/340 278/351/341 +f 106/151/218 371/444/338 278/351/341 30/53/220 +f 38/65/213 121/174/212 372/445/342 275/348/339 +f 121/174/212 14/28/201 187/260/343 372/445/342 +f 372/445/342 187/260/343 71/108/344 276/349/345 +f 275/348/339 372/445/342 276/349/345 95/132/340 +f 95/132/340 276/349/345 373/446/346 277/350/347 +f 276/349/345 71/108/344 188/261/348 373/446/346 +f 373/446/346 188/261/348 26/45/349 194/267/350 +f 277/350/347 373/446/346 194/267/350 74/111/351 +f 30/53/220 278/351/341 374/447/352 105/149/224 +f 278/351/341 95/132/340 277/350/347 374/447/352 +f 374/447/352 277/350/347 74/111/351 193/266/353 +f 105/149/224 374/447/352 193/266/353 10/22/226 +f 14/28/201 120/173/200 375/448/354 187/260/343 +f 120/173/200 37/64/197 279/352/355 375/448/354 +f 375/448/354 279/352/355 96/133/356 282/355/357 +f 187/260/343 375/448/354 282/355/357 71/108/344 +f 37/64/197 119/172/196 376/449/358 279/352/355 +f 119/172/196 5/11/34 139/205/32 376/449/358 +f 376/449/358 139/205/32 47/81/28 280/353/359 +f 279/352/355 376/449/358 280/353/359 96/133/356 +f 96/133/356 280/353/359 377/450/360 281/354/361 +f 280/353/359 47/81/28 140/207/26 377/450/360 +f 377/450/360 140/207/26 19/37/12 189/262/362 +f 281/354/361 377/450/360 189/262/362 72/109/363 +f 71/108/344 282/355/357 378/451/364 188/261/348 +f 282/355/357 96/133/356 281/354/361 378/451/364 +f 378/451/364 281/354/361 72/109/363 190/263/365 +f 188/261/348 378/451/364 190/263/365 26/45/349 +f 26/45/349 190/263/365 379/452/366 192/265/367 +f 190/263/365 72/109/363 283/356/368 379/452/366 +f 379/452/366 283/356/368 97/134/369 286/359/370 +f 192/265/367 379/452/366 286/359/370 73/110/371 +f 72/109/363 189/262/362 380/453/372 283/356/368 +f 189/262/362 19/37/12 141/209/10 380/453/372 +f 380/453/372 141/209/10 48/83/5 284/357/373 +f 283/356/368 380/453/372 284/357/373 97/134/369 +f 97/134/369 284/357/373 381/454/374 285/358/375 +f 284/357/373 48/83/5 142/211/2 381/454/374 +f 381/454/374 142/211/2 1/3/1 107/153/308 +f 285/358/375 381/454/374 107/153/308 31/55/304 +f 73/110/371 286/359/370 382/455/376 191/264/377 +f 286/359/370 97/134/369 285/358/375 382/455/376 +f 382/455/376 285/358/375 31/55/304 108/155/302 +f 191/264/377 382/455/376 108/155/302 11/24/290 +f 10/22/226 193/266/353 383/456/378 104/147/238 +f 193/266/353 74/111/351 287/360/379 383/456/378 +f 383/456/378 287/360/379 98/135/380 290/363/381 +f 104/147/238 383/456/378 290/363/381 29/51/240 +f 74/111/351 194/267/350 384/457/382 287/360/379 +f 194/267/350 26/45/349 192/265/367 384/457/382 +f 384/457/382 192/265/367 73/110/371 288/361/383 +f 287/360/379 384/457/382 288/361/383 98/135/380 +f 98/135/380 288/361/383 385/458/384 289/362/385 +f 288/361/383 73/110/371 191/264/377 385/458/384 +f 385/458/384 191/264/377 11/24/290 109/157/288 +f 289/362/385 385/458/384 109/157/288 32/57/284 +f 29/51/240 290/363/381 386/459/386 103/145/244 +f 290/363/381 98/135/380 289/362/385 386/459/386 +f 386/459/386 289/362/385 32/57/284 110/159/282 +f 103/145/244 386/459/386 110/159/282 2/6/246 diff --git a/data/games/garage/mods/testnodes/models/testnodes_marble_metal.obj b/data/games/garage/mods/testnodes/models/testnodes_marble_metal.obj new file mode 100644 index 0000000..c3bc70c --- /dev/null +++ b/data/games/garage/mods/testnodes/models/testnodes_marble_metal.obj @@ -0,0 +1,2470 @@ +# Blender 3.3.1 +# www.blender.org +o Sphere +v 0.000000 0.465623 -0.311119 +v 0.000000 0.395980 -0.395980 +v 0.000000 0.214303 -0.517373 +v 0.000000 0.109251 -0.549240 +v 0.000000 0.000000 -0.560000 +v 0.000000 -0.109251 -0.549240 +v 0.000000 -0.214303 -0.517373 +v 0.000000 -0.311119 -0.465623 +v 0.000000 -0.465623 -0.311119 +v 0.021314 0.549240 -0.107151 +v 0.041808 0.517373 -0.210185 +v 0.060696 0.465623 -0.305141 +v 0.077252 0.395980 -0.388371 +v 0.090839 0.311119 -0.456676 +v 0.100934 0.214303 -0.507431 +v 0.107151 0.109251 -0.538686 +v 0.109251 0.000000 -0.549240 +v 0.107151 -0.109251 -0.538686 +v 0.100934 -0.214303 -0.507431 +v 0.090839 -0.311119 -0.456676 +v 0.077252 -0.395980 -0.388371 +v 0.060696 -0.465623 -0.305141 +v 0.041808 -0.517373 -0.210185 +v 0.021314 -0.549240 -0.107151 +v 0.041808 0.549240 -0.100934 +v 0.082010 0.517373 -0.197990 +v 0.119060 0.465623 -0.287437 +v 0.151535 0.395980 -0.365838 +v 0.178186 0.311119 -0.430180 +v 0.197990 0.214303 -0.477990 +v 0.210185 0.109251 -0.507431 +v 0.214303 0.000000 -0.517373 +v 0.210185 -0.109251 -0.507431 +v 0.197990 -0.214303 -0.477990 +v 0.178186 -0.311119 -0.430180 +v 0.151535 -0.395980 -0.365838 +v 0.119060 -0.465623 -0.287437 +v 0.082010 -0.517373 -0.197990 +v 0.041808 -0.549240 -0.100934 +v 0.060696 0.549240 -0.090839 +v 0.119060 0.517373 -0.178186 +v 0.172849 0.465623 -0.258686 +v 0.219995 0.395980 -0.329245 +v 0.258686 0.311119 -0.387151 +v 0.287437 0.214303 -0.430180 +v 0.305141 0.109251 -0.456676 +v 0.311119 0.000000 -0.465623 +v 0.305141 -0.109251 -0.456676 +v 0.287437 -0.214303 -0.430180 +v 0.258686 -0.311119 -0.387151 +v 0.219995 -0.395980 -0.329245 +v 0.172849 -0.465623 -0.258686 +v 0.119060 -0.517373 -0.178186 +v 0.060696 -0.549240 -0.090839 +v 0.000000 -0.560000 0.000000 +v 0.077252 0.549240 -0.077252 +v 0.151535 0.517373 -0.151535 +v 0.219995 0.465623 -0.219995 +v 0.280000 0.395980 -0.280000 +v 0.329245 0.311119 -0.329245 +v 0.365838 0.214303 -0.365838 +v 0.388371 0.109251 -0.388371 +v 0.395980 0.000000 -0.395980 +v 0.388371 -0.109251 -0.388371 +v 0.365838 -0.214303 -0.365838 +v 0.329245 -0.311119 -0.329245 +v 0.280000 -0.395980 -0.280000 +v 0.219995 -0.465623 -0.219995 +v 0.151535 -0.517373 -0.151535 +v 0.077252 -0.549240 -0.077252 +v 0.090839 0.549240 -0.060696 +v 0.178186 0.517373 -0.119060 +v 0.258686 0.465623 -0.172849 +v 0.329245 0.395980 -0.219995 +v 0.387151 0.311119 -0.258686 +v 0.430180 0.214303 -0.287437 +v 0.456676 0.109251 -0.305141 +v 0.465623 0.000000 -0.311119 +v 0.456676 -0.109251 -0.305141 +v 0.430180 -0.214303 -0.287437 +v 0.387151 -0.311119 -0.258686 +v 0.329245 -0.395980 -0.219995 +v 0.258686 -0.465623 -0.172849 +v 0.178186 -0.517373 -0.119060 +v 0.090839 -0.549240 -0.060696 +v 0.100934 0.549240 -0.041808 +v 0.197990 0.517373 -0.082010 +v 0.287437 0.465623 -0.119060 +v 0.365838 0.395980 -0.151535 +v 0.430179 0.311119 -0.178186 +v 0.477990 0.214303 -0.197990 +v 0.507431 0.109251 -0.210185 +v 0.517372 0.000000 -0.214303 +v 0.507431 -0.109251 -0.210185 +v 0.477990 -0.214303 -0.197990 +v 0.430179 -0.311119 -0.178186 +v 0.365838 -0.395980 -0.151535 +v 0.287437 -0.465623 -0.119060 +v 0.197990 -0.517373 -0.082010 +v 0.100934 -0.549240 -0.041808 +v 0.107151 0.549240 -0.021314 +v 0.210185 0.517373 -0.041808 +v 0.305141 0.465623 -0.060696 +v 0.388371 0.395980 -0.077252 +v 0.456676 0.311119 -0.090838 +v 0.507431 0.214303 -0.100934 +v 0.538686 0.109251 -0.107151 +v 0.549240 0.000000 -0.109251 +v 0.538686 -0.109251 -0.107151 +v 0.507431 -0.214303 -0.100934 +v 0.456676 -0.311119 -0.090838 +v 0.388371 -0.395980 -0.077252 +v 0.305141 -0.465623 -0.060696 +v 0.210185 -0.517373 -0.041808 +v 0.107151 -0.549240 -0.021314 +v 0.109251 0.549240 0.000000 +v 0.214303 0.517373 0.000000 +v 0.311119 0.465623 0.000000 +v 0.395980 0.395980 0.000000 +v 0.465623 0.311119 0.000000 +v 0.517372 0.214303 0.000000 +v 0.549240 0.109251 0.000000 +v 0.560000 0.000000 -0.000000 +v 0.549240 -0.109251 0.000000 +v 0.517372 -0.214303 0.000000 +v 0.465623 -0.311119 0.000000 +v 0.395980 -0.395980 0.000000 +v 0.311119 -0.465623 0.000000 +v 0.214303 -0.517373 0.000000 +v 0.109251 -0.549240 0.000000 +v 0.107151 0.549240 0.021314 +v 0.210185 0.517373 0.041808 +v 0.305141 0.465623 0.060696 +v 0.388371 0.395980 0.077252 +v 0.456676 0.311119 0.090839 +v 0.507431 0.214303 0.100934 +v 0.538686 0.109251 0.107151 +v 0.549240 0.000000 0.109251 +v 0.538686 -0.109251 0.107151 +v 0.507431 -0.214303 0.100934 +v 0.456676 -0.311119 0.090839 +v 0.388371 -0.395980 0.077252 +v 0.305141 -0.465623 0.060696 +v 0.210185 -0.517373 0.041808 +v 0.107151 -0.549240 0.021314 +v 0.100934 0.549240 0.041808 +v 0.197990 0.517373 0.082010 +v 0.287437 0.465623 0.119060 +v 0.365838 0.395980 0.151535 +v 0.430179 0.311119 0.178186 +v 0.477990 0.214303 0.197990 +v 0.507431 0.109251 0.210185 +v 0.517372 0.000000 0.214303 +v 0.507431 -0.109251 0.210185 +v 0.477990 -0.214303 0.197990 +v 0.430179 -0.311119 0.178186 +v 0.365838 -0.395980 0.151535 +v 0.287437 -0.465623 0.119060 +v 0.197990 -0.517373 0.082010 +v 0.100934 -0.549240 0.041808 +v 0.090839 0.549240 0.060696 +v 0.178186 0.517373 0.119060 +v 0.258686 0.465623 0.172849 +v 0.329245 0.395980 0.219995 +v 0.387151 0.311119 0.258686 +v 0.430179 0.214303 0.287437 +v 0.456676 0.109251 0.305141 +v 0.465623 0.000000 0.311119 +v 0.456676 -0.109251 0.305141 +v 0.430179 -0.214303 0.287437 +v 0.387151 -0.311119 0.258686 +v 0.329245 -0.395980 0.219995 +v 0.258686 -0.465623 0.172849 +v 0.178186 -0.517373 0.119060 +v 0.090839 -0.549240 0.060696 +v 0.077252 0.549240 0.077252 +v 0.151535 0.517373 0.151535 +v 0.219995 0.465623 0.219995 +v 0.280000 0.395980 0.280000 +v 0.329245 0.311119 0.329245 +v 0.365838 0.214303 0.365838 +v 0.388371 0.109251 0.388371 +v 0.395980 0.000000 0.395980 +v 0.388371 -0.109251 0.388371 +v 0.365838 -0.214303 0.365838 +v 0.329245 -0.311119 0.329245 +v 0.280000 -0.395980 0.280000 +v 0.219995 -0.465623 0.219995 +v 0.151535 -0.517373 0.151535 +v 0.077252 -0.549240 0.077252 +v 0.060696 0.549240 0.090839 +v 0.119060 0.517373 0.178186 +v 0.172849 0.465623 0.258686 +v 0.219995 0.395980 0.329245 +v 0.258686 0.311119 0.387151 +v 0.287437 0.214303 0.430179 +v 0.305141 0.109251 0.456676 +v 0.311119 0.000000 0.465623 +v 0.305141 -0.109251 0.456676 +v 0.287437 -0.214303 0.430179 +v 0.258686 -0.311119 0.387151 +v 0.219995 -0.395980 0.329245 +v 0.172849 -0.465623 0.258686 +v 0.119060 -0.517373 0.178186 +v 0.060696 -0.549240 0.090839 +v 0.041808 0.549240 0.100934 +v 0.082010 0.517373 0.197990 +v 0.119060 0.465623 0.287437 +v 0.151535 0.395980 0.365838 +v 0.178186 0.311119 0.430179 +v 0.197990 0.214303 0.477990 +v 0.210185 0.109251 0.507431 +v 0.214303 0.000000 0.517372 +v 0.210185 -0.109251 0.507431 +v 0.197990 -0.214303 0.477990 +v 0.178186 -0.311119 0.430179 +v 0.151535 -0.395980 0.365838 +v 0.119060 -0.465623 0.287437 +v 0.082010 -0.517373 0.197990 +v 0.041808 -0.549240 0.100934 +v 0.021314 0.549240 0.107151 +v 0.041808 0.517373 0.210185 +v 0.060696 0.465623 0.305141 +v 0.077252 0.395980 0.388371 +v 0.090838 0.311119 0.456676 +v 0.100934 0.214303 0.507431 +v 0.107151 0.109251 0.538686 +v 0.109251 0.000000 0.549240 +v 0.107151 -0.109251 0.538686 +v 0.100934 -0.214303 0.507431 +v 0.090838 -0.311119 0.456676 +v 0.077252 -0.395980 0.388371 +v 0.060696 -0.465623 0.305141 +v 0.041808 -0.517373 0.210185 +v 0.021314 -0.549240 0.107151 +v -0.000000 0.549240 0.109251 +v -0.000000 0.517373 0.214303 +v -0.000000 0.465623 0.311119 +v 0.000000 0.395980 0.395980 +v -0.000000 0.311119 0.465623 +v -0.000000 0.214303 0.517372 +v -0.000000 0.109251 0.549240 +v -0.000000 0.000000 0.560000 +v -0.000000 -0.109251 0.549240 +v -0.000000 -0.214303 0.517372 +v -0.000000 -0.311119 0.465623 +v 0.000000 -0.395980 0.395980 +v -0.000000 -0.465623 0.311119 +v -0.000000 -0.517373 0.214303 +v -0.000000 -0.549240 0.109251 +v 0.000000 0.560000 0.000000 +v -0.021314 0.549240 0.107151 +v -0.041808 0.517373 0.210185 +v -0.060696 0.465623 0.305141 +v -0.077252 0.395980 0.388371 +v -0.090839 0.311119 0.456676 +v -0.100934 0.214303 0.507431 +v -0.107151 0.109251 0.538686 +v -0.109251 0.000000 0.549240 +v -0.107151 -0.109251 0.538686 +v -0.100934 -0.214303 0.507431 +v -0.090839 -0.311119 0.456676 +v -0.077252 -0.395980 0.388371 +v -0.060696 -0.465623 0.305141 +v -0.041808 -0.517373 0.210185 +v -0.021314 -0.549240 0.107151 +v -0.041808 0.549240 0.100934 +v -0.082010 0.517373 0.197990 +v -0.119060 0.465623 0.287437 +v -0.151535 0.395980 0.365838 +v -0.178186 0.311119 0.430179 +v -0.197990 0.214303 0.477990 +v -0.210185 0.109251 0.507431 +v -0.214303 0.000000 0.517372 +v -0.210185 -0.109251 0.507431 +v -0.197990 -0.214303 0.477990 +v -0.178186 -0.311119 0.430179 +v -0.151535 -0.395980 0.365838 +v -0.119060 -0.465623 0.287437 +v -0.082010 -0.517373 0.197990 +v -0.041808 -0.549240 0.100934 +v -0.060696 0.549240 0.090838 +v -0.119060 0.517373 0.178186 +v -0.172849 0.465623 0.258686 +v -0.219995 0.395980 0.329245 +v -0.258686 0.311119 0.387151 +v -0.287437 0.214303 0.430179 +v -0.305141 0.109251 0.456676 +v -0.311119 0.000000 0.465623 +v -0.305141 -0.109251 0.456676 +v -0.287437 -0.214303 0.430179 +v -0.258686 -0.311119 0.387151 +v -0.219995 -0.395980 0.329245 +v -0.172849 -0.465623 0.258686 +v -0.119060 -0.517373 0.178186 +v -0.060696 -0.549240 0.090838 +v -0.077252 0.549240 0.077252 +v -0.151535 0.517373 0.151535 +v -0.219995 0.465623 0.219994 +v -0.280000 0.395980 0.280000 +v -0.329245 0.311119 0.329245 +v -0.365838 0.214303 0.365837 +v -0.388371 0.109251 0.388371 +v -0.395980 0.000000 0.395980 +v -0.388371 -0.109251 0.388371 +v -0.365838 -0.214303 0.365837 +v -0.329245 -0.311119 0.329245 +v -0.280000 -0.395980 0.280000 +v -0.219995 -0.465623 0.219994 +v -0.151535 -0.517373 0.151535 +v -0.077252 -0.549240 0.077252 +v -0.090838 0.549240 0.060696 +v -0.178186 0.517373 0.119060 +v -0.258686 0.465623 0.172849 +v -0.329245 0.395980 0.219995 +v -0.387151 0.311119 0.258686 +v -0.430179 0.214303 0.287437 +v -0.456676 0.109251 0.305141 +v -0.465623 0.000000 0.311119 +v -0.456676 -0.109251 0.305141 +v -0.430179 -0.214303 0.287437 +v -0.387151 -0.311119 0.258686 +v -0.329245 -0.395980 0.219995 +v -0.258686 -0.465623 0.172849 +v -0.178186 -0.517373 0.119060 +v -0.090838 -0.549240 0.060696 +v -0.100934 0.549240 0.041808 +v -0.197990 0.517373 0.082010 +v -0.287437 0.465623 0.119060 +v -0.365838 0.395980 0.151535 +v -0.430179 0.311119 0.178186 +v -0.477990 0.214303 0.197990 +v -0.507431 0.109251 0.210185 +v -0.517372 0.000000 0.214303 +v -0.507431 -0.109251 0.210185 +v -0.477990 -0.214303 0.197990 +v -0.430179 -0.311119 0.178186 +v -0.365838 -0.395980 0.151535 +v -0.287437 -0.465623 0.119060 +v -0.197990 -0.517373 0.082010 +v -0.100934 -0.549240 0.041808 +v -0.107151 0.549240 0.021314 +v -0.210185 0.517373 0.041808 +v -0.305141 0.465623 0.060696 +v -0.388371 0.395980 0.077252 +v -0.456676 0.311119 0.090838 +v -0.507431 0.214303 0.100934 +v -0.538686 0.109251 0.107151 +v -0.549240 0.000000 0.109251 +v -0.538686 -0.109251 0.107151 +v -0.507431 -0.214303 0.100934 +v -0.456676 -0.311119 0.090838 +v -0.388371 -0.395980 0.077252 +v -0.305141 -0.465623 0.060696 +v -0.210185 -0.517373 0.041808 +v -0.107151 -0.549240 0.021314 +v -0.109251 0.549240 -0.000000 +v -0.214303 0.517373 -0.000000 +v -0.311119 0.465623 -0.000000 +v -0.395980 0.395980 -0.000000 +v -0.465623 0.311119 -0.000000 +v -0.517372 0.214303 -0.000000 +v -0.549240 0.109251 -0.000000 +v -0.560000 0.000000 -0.000000 +v -0.549240 -0.109251 -0.000000 +v -0.517372 -0.214303 -0.000000 +v -0.465623 -0.311119 -0.000000 +v -0.395980 -0.395980 -0.000000 +v -0.311119 -0.465623 -0.000000 +v -0.214303 -0.517373 -0.000000 +v -0.109251 -0.549240 -0.000000 +v -0.107151 0.549240 -0.021314 +v -0.210185 0.517373 -0.041808 +v -0.305141 0.465623 -0.060696 +v -0.388371 0.395980 -0.077252 +v -0.456676 0.311119 -0.090839 +v -0.507431 0.214303 -0.100934 +v -0.538686 0.109251 -0.107151 +v -0.549240 0.000000 -0.109251 +v -0.538686 -0.109251 -0.107151 +v -0.507431 -0.214303 -0.100934 +v -0.456676 -0.311119 -0.090839 +v -0.388371 -0.395980 -0.077252 +v -0.305141 -0.465623 -0.060696 +v -0.210185 -0.517373 -0.041808 +v -0.107151 -0.549240 -0.021314 +v -0.100934 0.549240 -0.041808 +v -0.197990 0.517373 -0.082010 +v -0.287437 0.465623 -0.119060 +v -0.365837 0.395980 -0.151535 +v -0.430179 0.311119 -0.178186 +v -0.477990 0.214303 -0.197990 +v -0.507431 0.109251 -0.210185 +v -0.517372 0.000000 -0.214303 +v -0.507431 -0.109251 -0.210185 +v -0.477990 -0.214303 -0.197990 +v -0.430179 -0.311119 -0.178186 +v -0.365837 -0.395980 -0.151535 +v -0.287437 -0.465623 -0.119060 +v -0.197990 -0.517373 -0.082010 +v -0.100934 -0.549240 -0.041808 +v -0.090838 0.549240 -0.060696 +v -0.178186 0.517373 -0.119060 +v -0.258686 0.465623 -0.172849 +v -0.329245 0.395980 -0.219995 +v -0.387151 0.311119 -0.258686 +v -0.430179 0.214303 -0.287437 +v -0.456676 0.109251 -0.305141 +v -0.465623 0.000000 -0.311119 +v -0.456676 -0.109251 -0.305141 +v -0.430179 -0.214303 -0.287437 +v -0.387151 -0.311119 -0.258686 +v -0.329245 -0.395980 -0.219995 +v -0.258686 -0.465623 -0.172849 +v -0.178186 -0.517373 -0.119060 +v -0.090838 -0.549240 -0.060696 +v -0.077252 0.549240 -0.077252 +v -0.151535 0.517373 -0.151535 +v -0.219994 0.465623 -0.219994 +v -0.280000 0.395980 -0.280000 +v -0.329245 0.311119 -0.329245 +v -0.365837 0.214303 -0.365838 +v -0.388371 0.109251 -0.388371 +v -0.395980 0.000000 -0.395980 +v -0.388371 -0.109251 -0.388371 +v -0.365837 -0.214303 -0.365838 +v -0.329245 -0.311119 -0.329245 +v -0.280000 -0.395980 -0.280000 +v -0.219994 -0.465623 -0.219994 +v -0.151535 -0.517373 -0.151535 +v -0.077252 -0.549240 -0.077252 +v -0.060696 0.549240 -0.090838 +v -0.119060 0.517373 -0.178186 +v -0.172848 0.465623 -0.258686 +v -0.219994 0.395980 -0.329245 +v -0.258686 0.311119 -0.387151 +v -0.287437 0.214303 -0.430179 +v -0.305141 0.109251 -0.456676 +v -0.311119 0.000000 -0.465623 +v -0.305141 -0.109251 -0.456676 +v -0.287437 -0.214303 -0.430179 +v -0.258686 -0.311119 -0.387151 +v -0.219994 -0.395980 -0.329245 +v -0.172848 -0.465623 -0.258686 +v -0.119060 -0.517373 -0.178186 +v -0.060696 -0.549240 -0.090838 +v -0.041808 0.549240 -0.100934 +v -0.082010 0.517373 -0.197990 +v -0.119060 0.465623 -0.287437 +v -0.151535 0.395980 -0.365837 +v -0.178186 0.311119 -0.430179 +v -0.197990 0.214303 -0.477990 +v -0.210185 0.109251 -0.507431 +v -0.214303 0.000000 -0.517372 +v -0.210185 -0.109251 -0.507431 +v -0.197990 -0.214303 -0.477990 +v -0.178186 -0.311119 -0.430179 +v -0.151535 -0.395980 -0.365837 +v -0.119060 -0.465623 -0.287437 +v -0.082010 -0.517373 -0.197990 +v -0.041808 -0.549240 -0.100934 +v -0.021314 0.549240 -0.107151 +v -0.041808 0.517373 -0.210185 +v -0.060696 0.465623 -0.305141 +v -0.077252 0.395980 -0.388371 +v -0.090838 0.311119 -0.456676 +v -0.100934 0.214303 -0.507431 +v -0.107151 0.109251 -0.538686 +v -0.109250 0.000000 -0.549240 +v -0.107151 -0.109251 -0.538686 +v -0.100934 -0.214303 -0.507431 +v -0.090838 -0.311119 -0.456676 +v -0.077252 -0.395980 -0.388371 +v -0.060696 -0.465623 -0.305141 +v -0.041808 -0.517373 -0.210185 +v -0.021314 -0.549240 -0.107151 +v 0.000000 0.549240 -0.109250 +v 0.000000 0.517373 -0.214303 +v 0.000000 0.311119 -0.465623 +v 0.000000 -0.395980 -0.395980 +v 0.000000 -0.517373 -0.214303 +v 0.000000 -0.549240 -0.109250 +vn -0.0000 -0.8286 -0.5598 +vn 0.0757 -0.9217 -0.3804 +vn -0.0000 -0.9217 -0.3879 +vn -0.0000 0.1939 -0.9810 +vn 0.1804 0.3805 -0.9070 +vn 0.1914 0.1939 -0.9622 +vn 0.0392 -0.9796 -0.1971 +vn -0.0000 -0.9796 -0.2010 +vn -0.0000 -0.0000 -1.0000 +vn 0.1951 -0.0000 -0.9808 +vn -0.0000 0.9796 -0.2010 +vn -0.0000 1.0000 -0.0000 +vn 0.0392 0.9796 -0.1971 +vn -0.0000 -1.0000 -0.0000 +vn 0.1914 -0.1939 -0.9622 +vn -0.0000 -0.1939 -0.9810 +vn -0.0000 0.9217 -0.3879 +vn 0.0757 0.9217 -0.3804 +vn 0.1804 -0.3805 -0.9070 +vn -0.0000 -0.3805 -0.9248 +vn -0.0000 0.8286 -0.5598 +vn 0.1092 0.8286 -0.5490 +vn 0.1626 -0.5528 -0.8173 +vn -0.0000 -0.5528 -0.8333 +vn -0.0000 0.7041 -0.7101 +vn 0.1385 0.7041 -0.6965 +vn 0.1385 -0.7041 -0.6965 +vn -0.0000 -0.7041 -0.7101 +vn -0.0000 0.5528 -0.8333 +vn 0.1626 0.5528 -0.8173 +vn 0.1092 -0.8286 -0.5490 +vn -0.0000 0.3805 -0.9248 +vn 0.2142 -0.8286 -0.5172 +vn 0.3189 0.5528 -0.7699 +vn 0.3539 0.3805 -0.8544 +vn 0.1484 -0.9217 -0.3584 +vn 0.3754 0.1939 -0.9063 +vn 0.0769 -0.9796 -0.1857 +vn 0.3827 -0.0000 -0.9239 +vn 0.0769 0.9796 -0.1857 +vn 0.3754 -0.1939 -0.9063 +vn 0.1484 0.9217 -0.3584 +vn 0.3539 -0.3805 -0.8544 +vn 0.2142 0.8286 -0.5172 +vn 0.3189 -0.5528 -0.7699 +vn 0.2718 0.7041 -0.6561 +vn 0.2718 -0.7041 -0.6561 +vn 0.1117 0.9796 -0.1671 +vn 0.2155 0.9217 -0.3225 +vn 0.5138 -0.3805 -0.7689 +vn 0.3110 0.8286 -0.4654 +vn 0.4630 -0.5528 -0.6929 +vn 0.3945 0.7041 -0.5905 +vn 0.3945 -0.7041 -0.5905 +vn 0.4630 0.5528 -0.6929 +vn 0.3110 -0.8286 -0.4654 +vn 0.5138 0.3805 -0.7689 +vn 0.2155 -0.9217 -0.3225 +vn 0.5450 0.1939 -0.8157 +vn 0.1117 -0.9796 -0.1671 +vn 0.5556 -0.0000 -0.8315 +vn 0.5450 -0.1939 -0.8157 +vn 0.5893 0.5528 -0.5893 +vn 0.6539 0.3805 -0.6539 +vn 0.2743 -0.9217 -0.2743 +vn 0.6937 0.1939 -0.6937 +vn 0.1421 -0.9796 -0.1421 +vn 0.7071 -0.0000 -0.7071 +vn 0.1421 0.9796 -0.1421 +vn 0.6937 -0.1939 -0.6937 +vn 0.2743 0.9217 -0.2743 +vn 0.6539 -0.3805 -0.6539 +vn 0.3958 0.8286 -0.3958 +vn 0.5893 -0.5528 -0.5893 +vn 0.5021 0.7041 -0.5021 +vn 0.5021 -0.7041 -0.5021 +vn 0.3958 -0.8286 -0.3958 +vn 0.7689 -0.3805 -0.5138 +vn 0.3225 0.9217 -0.2155 +vn 0.4654 0.8286 -0.3110 +vn 0.6929 -0.5528 -0.4630 +vn 0.5905 0.7041 -0.3945 +vn 0.5905 -0.7041 -0.3945 +vn 0.6929 0.5528 -0.4630 +vn 0.4654 -0.8286 -0.3110 +vn 0.7689 0.3805 -0.5138 +vn 0.3225 -0.9217 -0.2155 +vn 0.8157 0.1939 -0.5450 +vn 0.1671 -0.9796 -0.1117 +vn 0.8315 -0.0000 -0.5556 +vn 0.1671 0.9796 -0.1117 +vn 0.8157 -0.1939 -0.5450 +vn 0.3584 -0.9217 -0.1484 +vn 0.8544 0.3805 -0.3539 +vn 0.9063 0.1939 -0.3754 +vn 0.1857 -0.9796 -0.0769 +vn 0.9239 -0.0000 -0.3827 +vn 0.1857 0.9796 -0.0769 +vn 0.9063 -0.1939 -0.3754 +vn 0.3584 0.9217 -0.1484 +vn 0.8544 -0.3805 -0.3539 +vn 0.5172 0.8286 -0.2142 +vn 0.7699 -0.5528 -0.3189 +vn 0.6561 0.7041 -0.2718 +vn 0.6561 -0.7041 -0.2718 +vn 0.7699 0.5528 -0.3189 +vn 0.5172 -0.8286 -0.2142 +vn 0.8173 -0.5528 -0.1626 +vn 0.5490 0.8286 -0.1092 +vn 0.6965 0.7041 -0.1385 +vn 0.6965 -0.7041 -0.1385 +vn 0.8173 0.5528 -0.1626 +vn 0.5490 -0.8286 -0.1092 +vn 0.9070 0.3805 -0.1804 +vn 0.3804 -0.9217 -0.0757 +vn 0.9622 0.1939 -0.1914 +vn 0.1971 -0.9796 -0.0392 +vn 0.9808 -0.0000 -0.1951 +vn 0.1971 0.9796 -0.0392 +vn 0.9622 -0.1939 -0.1914 +vn 0.3804 0.9217 -0.0757 +vn 0.9070 -0.3805 -0.1804 +vn 0.2010 -0.9796 -0.0000 +vn 0.9810 0.1939 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn 0.2010 0.9796 -0.0000 +vn 0.9810 -0.1939 -0.0000 +vn 0.3879 0.9217 -0.0000 +vn 0.9248 -0.3805 -0.0000 +vn 0.5598 0.8286 -0.0000 +vn 0.8333 -0.5528 -0.0000 +vn 0.7101 0.7041 -0.0000 +vn 0.7101 -0.7041 -0.0000 +vn 0.8333 0.5528 -0.0000 +vn 0.5598 -0.8286 -0.0000 +vn 0.9248 0.3805 -0.0000 +vn 0.3879 -0.9217 -0.0000 +vn 0.6965 0.7041 0.1385 +vn 0.8173 -0.5528 0.1626 +vn 0.6965 -0.7041 0.1385 +vn 0.8173 0.5528 0.1626 +vn 0.5490 -0.8286 0.1092 +vn 0.9070 0.3805 0.1804 +vn 0.3804 -0.9217 0.0757 +vn 0.9622 0.1939 0.1914 +vn 0.1971 -0.9796 0.0392 +vn 0.9808 -0.0000 0.1951 +vn 0.1971 0.9796 0.0392 +vn 0.9622 -0.1939 0.1914 +vn 0.3804 0.9217 0.0757 +vn 0.9070 -0.3805 0.1804 +vn 0.5490 0.8286 0.1092 +vn 0.9239 -0.0000 0.3827 +vn 0.1857 0.9796 0.0769 +vn 0.1857 -0.9796 0.0769 +vn 0.9063 -0.1939 0.3754 +vn 0.3584 0.9217 0.1484 +vn 0.8544 -0.3805 0.3539 +vn 0.5172 0.8286 0.2142 +vn 0.7699 -0.5528 0.3189 +vn 0.6561 0.7041 0.2718 +vn 0.6561 -0.7041 0.2718 +vn 0.7699 0.5528 0.3189 +vn 0.5172 -0.8286 0.2142 +vn 0.8544 0.3805 0.3539 +vn 0.3584 -0.9217 0.1484 +vn 0.9063 0.1939 0.3754 +vn 0.6929 -0.5528 0.4630 +vn 0.5905 -0.7041 0.3945 +vn 0.6929 0.5528 0.4630 +vn 0.4654 -0.8286 0.3110 +vn 0.7689 0.3805 0.5138 +vn 0.3225 -0.9217 0.2155 +vn 0.8157 0.1939 0.5450 +vn 0.1671 -0.9796 0.1117 +vn 0.8315 -0.0000 0.5556 +vn 0.1671 0.9796 0.1117 +vn 0.8157 -0.1939 0.5450 +vn 0.3225 0.9217 0.2155 +vn 0.7689 -0.3805 0.5138 +vn 0.4654 0.8286 0.3110 +vn 0.5905 0.7041 0.3945 +vn 0.1421 0.9796 0.1421 +vn 0.1421 -0.9796 0.1421 +vn 0.7071 -0.0000 0.7071 +vn 0.6937 -0.1939 0.6937 +vn 0.2743 0.9217 0.2743 +vn 0.6539 -0.3805 0.6539 +vn 0.3958 0.8286 0.3958 +vn 0.5893 -0.5528 0.5893 +vn 0.5021 0.7041 0.5021 +vn 0.5021 -0.7041 0.5021 +vn 0.5893 0.5528 0.5893 +vn 0.3958 -0.8286 0.3958 +vn 0.6539 0.3805 0.6539 +vn 0.2743 -0.9217 0.2743 +vn 0.6937 0.1939 0.6937 +vn 0.4630 0.5528 0.6929 +vn 0.3945 -0.7041 0.5905 +vn 0.3110 -0.8286 0.4654 +vn 0.5138 0.3805 0.7689 +vn 0.2155 -0.9217 0.3225 +vn 0.5450 0.1939 0.8157 +vn 0.1117 -0.9796 0.1671 +vn 0.5556 -0.0000 0.8315 +vn 0.1117 0.9796 0.1671 +vn 0.5450 -0.1939 0.8157 +vn 0.2155 0.9217 0.3225 +vn 0.5138 -0.3805 0.7689 +vn 0.3110 0.8286 0.4654 +vn 0.4630 -0.5528 0.6929 +vn 0.3945 0.7041 0.5905 +vn 0.3827 -0.0000 0.9239 +vn 0.3754 -0.1939 0.9063 +vn 0.1484 0.9217 0.3584 +vn 0.3539 -0.3805 0.8544 +vn 0.2142 0.8286 0.5172 +vn 0.3189 -0.5528 0.7699 +vn 0.2718 0.7041 0.6561 +vn 0.2718 -0.7041 0.6561 +vn 0.3189 0.5528 0.7699 +vn 0.2142 -0.8286 0.5172 +vn 0.3539 0.3805 0.8544 +vn 0.1484 -0.9217 0.3584 +vn 0.3754 0.1939 0.9063 +vn 0.0769 -0.9796 0.1857 +vn 0.0769 0.9796 0.1857 +vn 0.1385 -0.7041 0.6965 +vn 0.1092 -0.8286 0.5490 +vn 0.1804 0.3805 0.9070 +vn 0.0757 -0.9217 0.3804 +vn 0.1914 0.1939 0.9622 +vn 0.0392 -0.9796 0.1971 +vn 0.1951 -0.0000 0.9808 +vn 0.0392 0.9796 0.1971 +vn 0.1914 -0.1939 0.9622 +vn 0.0757 0.9217 0.3804 +vn 0.1804 -0.3805 0.9070 +vn 0.1092 0.8286 0.5490 +vn 0.1626 -0.5528 0.8173 +vn 0.1385 0.7041 0.6965 +vn 0.1626 0.5528 0.8173 +vn -0.0000 -0.1939 0.9810 +vn -0.0000 -0.3805 0.9248 +vn -0.0000 0.8286 0.5598 +vn -0.0000 -0.5528 0.8333 +vn -0.0000 0.7041 0.7101 +vn -0.0000 -0.7041 0.7101 +vn -0.0000 0.5528 0.8333 +vn -0.0000 -0.8286 0.5598 +vn -0.0000 0.3805 0.9248 +vn -0.0000 -0.9217 0.3879 +vn -0.0000 0.1939 0.9810 +vn -0.0000 -0.9796 0.2010 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 0.9796 0.2010 +vn -0.0000 0.9217 0.3879 +vn -0.0757 -0.9217 0.3804 +vn -0.1804 0.3805 0.9070 +vn -0.1914 0.1939 0.9622 +vn -0.0392 -0.9796 0.1971 +vn -0.1951 -0.0000 0.9808 +vn -0.0392 0.9796 0.1971 +vn -0.1914 -0.1939 0.9622 +vn -0.0757 0.9217 0.3804 +vn -0.1804 -0.3805 0.9070 +vn -0.1092 0.8286 0.5490 +vn -0.1626 -0.5528 0.8173 +vn -0.1385 0.7041 0.6965 +vn -0.1385 -0.7041 0.6965 +vn -0.1626 0.5528 0.8173 +vn -0.1092 -0.8286 0.5490 +vn -0.1484 0.9217 0.3584 +vn -0.2142 0.8286 0.5172 +vn -0.3189 -0.5528 0.7699 +vn -0.2718 0.7041 0.6561 +vn -0.2718 -0.7041 0.6561 +vn -0.3189 0.5528 0.7699 +vn -0.2142 -0.8286 0.5172 +vn -0.3539 0.3805 0.8544 +vn -0.1484 -0.9217 0.3584 +vn -0.3754 0.1939 0.9063 +vn -0.0769 -0.9796 0.1857 +vn -0.3827 -0.0000 0.9239 +vn -0.0769 0.9796 0.1857 +vn -0.3754 -0.1939 0.9063 +vn -0.3539 -0.3805 0.8544 +vn -0.5138 0.3805 0.7689 +vn -0.5450 0.1939 0.8157 +vn -0.1117 -0.9796 0.1671 +vn -0.5556 -0.0000 0.8315 +vn -0.1117 0.9796 0.1671 +vn -0.5450 -0.1939 0.8157 +vn -0.2155 0.9217 0.3225 +vn -0.5138 -0.3805 0.7689 +vn -0.3110 0.8286 0.4654 +vn -0.4630 -0.5528 0.6929 +vn -0.3945 0.7041 0.5905 +vn -0.3945 -0.7041 0.5905 +vn -0.4630 0.5528 0.6929 +vn -0.3110 -0.8286 0.4654 +vn -0.2155 -0.9217 0.3225 +vn -0.5893 -0.5528 0.5893 +vn -0.3958 0.8286 0.3958 +vn -0.5021 0.7041 0.5021 +vn -0.5021 -0.7041 0.5021 +vn -0.5893 0.5528 0.5893 +vn -0.3958 -0.8286 0.3958 +vn -0.6539 0.3805 0.6539 +vn -0.2743 -0.9217 0.2743 +vn -0.6937 0.1939 0.6937 +vn -0.1421 -0.9796 0.1421 +vn -0.7071 -0.0000 0.7071 +vn -0.1421 0.9796 0.1421 +vn -0.6937 -0.1939 0.6937 +vn -0.2743 0.9217 0.2743 +vn -0.6539 -0.3805 0.6539 +vn -0.1671 -0.9796 0.1117 +vn -0.8157 0.1939 0.5450 +vn -0.8315 -0.0000 0.5556 +vn -0.1671 0.9796 0.1117 +vn -0.8157 -0.1939 0.5450 +vn -0.3225 0.9217 0.2155 +vn -0.7689 -0.3805 0.5138 +vn -0.4654 0.8286 0.3110 +vn -0.6929 -0.5528 0.4630 +vn -0.5905 0.7041 0.3945 +vn -0.5905 -0.7041 0.3945 +vn -0.6929 0.5528 0.4630 +vn -0.4654 -0.8286 0.3110 +vn -0.7689 0.3805 0.5138 +vn -0.3225 -0.9217 0.2155 +vn -0.5172 0.8286 0.2142 +vn -0.6561 0.7041 0.2718 +vn -0.6561 -0.7041 0.2718 +vn -0.7699 0.5528 0.3189 +vn -0.5172 -0.8286 0.2142 +vn -0.8544 0.3805 0.3539 +vn -0.3584 -0.9217 0.1484 +vn -0.9063 0.1939 0.3754 +vn -0.1857 -0.9796 0.0769 +vn -0.9239 -0.0000 0.3827 +vn -0.1857 0.9796 0.0769 +vn -0.9063 -0.1939 0.3754 +vn -0.3584 0.9217 0.1484 +vn -0.8544 -0.3805 0.3539 +vn -0.7699 -0.5528 0.3189 +vn -0.9622 0.1939 0.1914 +vn -0.9808 -0.0000 0.1951 +vn -0.1971 0.9796 0.0392 +vn -0.1971 -0.9796 0.0392 +vn -0.9622 -0.1939 0.1914 +vn -0.3804 0.9217 0.0757 +vn -0.9070 -0.3805 0.1804 +vn -0.5490 0.8286 0.1092 +vn -0.8173 -0.5528 0.1626 +vn -0.6965 0.7041 0.1385 +vn -0.6965 -0.7041 0.1385 +vn -0.8173 0.5528 0.1626 +vn -0.5490 -0.8286 0.1092 +vn -0.9070 0.3805 0.1804 +vn -0.3804 -0.9217 0.0757 +vn -0.7101 -0.7041 -0.0000 +vn -0.7101 0.7041 -0.0000 +vn -0.8333 0.5528 -0.0000 +vn -0.5598 -0.8286 -0.0000 +vn -0.9248 0.3805 -0.0000 +vn -0.3879 -0.9217 -0.0000 +vn -0.9810 0.1939 -0.0000 +vn -0.2010 -0.9796 -0.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.2010 0.9796 -0.0000 +vn -0.9810 -0.1939 -0.0000 +vn -0.3879 0.9217 -0.0000 +vn -0.9248 -0.3805 -0.0000 +vn -0.5598 0.8286 -0.0000 +vn -0.8333 -0.5528 -0.0000 +vn -0.1971 -0.9796 -0.0392 +vn -0.9808 -0.0000 -0.1951 +vn -0.9622 -0.1939 -0.1914 +vn -0.3804 0.9217 -0.0757 +vn -0.9070 -0.3805 -0.1804 +vn -0.5490 0.8286 -0.1092 +vn -0.8173 -0.5528 -0.1626 +vn -0.6965 0.7041 -0.1385 +vn -0.6965 -0.7041 -0.1385 +vn -0.8173 0.5528 -0.1626 +vn -0.5490 -0.8286 -0.1092 +vn -0.9070 0.3805 -0.1804 +vn -0.3804 -0.9217 -0.0757 +vn -0.9622 0.1939 -0.1914 +vn -0.1971 0.9796 -0.0392 +vn -0.6561 -0.7041 -0.2718 +vn -0.5172 -0.8286 -0.2142 +vn -0.8544 0.3805 -0.3539 +vn -0.3584 -0.9217 -0.1484 +vn -0.9063 0.1939 -0.3754 +vn -0.1857 -0.9796 -0.0769 +vn -0.9239 -0.0000 -0.3827 +vn -0.1857 0.9796 -0.0769 +vn -0.9063 -0.1939 -0.3754 +vn -0.3584 0.9217 -0.1484 +vn -0.8544 -0.3805 -0.3539 +vn -0.5172 0.8286 -0.2142 +vn -0.7699 -0.5528 -0.3189 +vn -0.6561 0.7041 -0.2718 +vn -0.7699 0.5528 -0.3189 +vn -0.3225 0.9217 -0.2155 +vn -0.8157 -0.1939 -0.5450 +vn -0.7689 -0.3805 -0.5138 +vn -0.4654 0.8286 -0.3110 +vn -0.6929 -0.5528 -0.4630 +vn -0.5905 0.7041 -0.3945 +vn -0.5905 -0.7041 -0.3945 +vn -0.6929 0.5528 -0.4630 +vn -0.4654 -0.8286 -0.3110 +vn -0.7689 0.3805 -0.5138 +vn -0.3225 -0.9217 -0.2155 +vn -0.8157 0.1939 -0.5450 +vn -0.1671 -0.9796 -0.1117 +vn -0.8315 -0.0000 -0.5556 +vn -0.1671 0.9796 -0.1117 +vn -0.6539 0.3805 -0.6539 +vn -0.3958 -0.8286 -0.3958 +vn -0.2743 -0.9217 -0.2743 +vn -0.6937 0.1939 -0.6937 +vn -0.1421 -0.9796 -0.1421 +vn -0.7071 -0.0000 -0.7071 +vn -0.1421 0.9796 -0.1421 +vn -0.6937 -0.1939 -0.6937 +vn -0.2743 0.9217 -0.2743 +vn -0.6539 -0.3805 -0.6539 +vn -0.3958 0.8286 -0.3958 +vn -0.5893 -0.5528 -0.5893 +vn -0.5021 0.7041 -0.5021 +vn -0.5021 -0.7041 -0.5021 +vn -0.5893 0.5528 -0.5893 +vn -0.5450 -0.1939 -0.8157 +vn -0.5138 -0.3805 -0.7689 +vn -0.3110 0.8286 -0.4654 +vn -0.4630 -0.5528 -0.6929 +vn -0.3945 0.7041 -0.5905 +vn -0.3945 -0.7041 -0.5905 +vn -0.4630 0.5528 -0.6929 +vn -0.3110 -0.8286 -0.4654 +vn -0.5138 0.3805 -0.7689 +vn -0.2155 -0.9217 -0.3225 +vn -0.5450 0.1939 -0.8157 +vn -0.1117 -0.9796 -0.1671 +vn -0.5556 -0.0000 -0.8315 +vn -0.1117 0.9796 -0.1671 +vn -0.2155 0.9217 -0.3225 +vn -0.2142 -0.8286 -0.5172 +vn -0.1484 -0.9217 -0.3584 +vn -0.3754 0.1939 -0.9063 +vn -0.0769 -0.9796 -0.1857 +vn -0.3827 -0.0000 -0.9239 +vn -0.0769 0.9796 -0.1857 +vn -0.3754 -0.1939 -0.9063 +vn -0.1484 0.9217 -0.3584 +vn -0.3539 -0.3805 -0.8544 +vn -0.2142 0.8286 -0.5172 +vn -0.3189 -0.5528 -0.7699 +vn -0.2718 0.7041 -0.6561 +vn -0.2718 -0.7041 -0.6561 +vn -0.3189 0.5528 -0.7699 +vn -0.3539 0.3805 -0.8544 +vn -0.1092 0.8286 -0.5490 +vn -0.1804 -0.3805 -0.9070 +vn -0.1626 -0.5528 -0.8173 +vn -0.1385 0.7041 -0.6965 +vn -0.1385 -0.7041 -0.6965 +vn -0.1626 0.5528 -0.8173 +vn -0.1092 -0.8286 -0.5490 +vn -0.1804 0.3805 -0.9070 +vn -0.0757 -0.9217 -0.3804 +vn -0.1914 0.1939 -0.9622 +vn -0.0392 -0.9796 -0.1971 +vn -0.1951 -0.0000 -0.9808 +vn -0.0392 0.9796 -0.1971 +vn -0.1914 -0.1939 -0.9622 +vn -0.0757 0.9217 -0.3804 +vt 0.500000 0.812500 +vt 0.500000 0.750000 +vt 0.500000 0.625000 +vt 0.500000 0.562500 +vt 0.500000 0.500000 +vt 0.500000 0.437500 +vt 0.500000 0.375000 +vt 0.500000 0.312500 +vt 0.500000 0.187500 +vt 0.437500 0.937500 +vt 0.437500 0.875000 +vt 0.437500 0.812500 +vt 0.437500 0.750000 +vt 0.437500 0.687500 +vt 0.437500 0.625000 +vt 0.437500 0.562500 +vt 0.437500 0.500000 +vt 0.437500 0.437500 +vt 0.437500 0.375000 +vt 0.437500 0.312500 +vt 0.437500 0.250000 +vt 0.437500 0.187500 +vt 0.437500 0.125000 +vt 0.437500 0.062500 +vt 0.375000 0.937500 +vt 0.375000 0.875000 +vt 0.375000 0.812500 +vt 0.375000 0.750000 +vt 0.375000 0.687500 +vt 0.375000 0.625000 +vt 0.375000 0.562500 +vt 0.375000 0.500000 +vt 0.375000 0.437500 +vt 0.375000 0.375000 +vt 0.375000 0.312500 +vt 0.375000 0.250000 +vt 0.375000 0.187500 +vt 0.375000 0.125000 +vt 0.375000 0.062500 +vt 0.312500 0.937500 +vt 0.312500 0.875000 +vt 0.312500 0.812500 +vt 0.312500 0.750000 +vt 0.312500 0.687500 +vt 0.312500 0.625000 +vt 0.312500 0.562500 +vt 0.312500 0.500000 +vt 0.312500 0.437500 +vt 0.312500 0.375000 +vt 0.312500 0.312500 +vt 0.312500 0.250000 +vt 0.312500 0.187500 +vt 0.312500 0.125000 +vt 0.312500 0.062500 +vt 0.468750 0.000000 +vt 0.406250 0.000000 +vt 0.343750 0.000000 +vt 0.281250 0.000000 +vt 0.218750 0.000000 +vt 0.156250 0.000000 +vt 0.093750 0.000000 +vt 0.031250 0.000000 +vt 0.968750 0.000000 +vt 0.906250 0.000000 +vt 0.843750 0.000000 +vt 0.781250 0.000000 +vt 0.718750 0.000000 +vt 0.656250 0.000000 +vt 0.593750 0.000000 +vt 0.531250 0.000000 +vt 0.250000 0.937500 +vt 0.250000 0.875000 +vt 0.250000 0.812500 +vt 0.250000 0.750000 +vt 0.250000 0.687500 +vt 0.250000 0.625000 +vt 0.250000 0.562500 +vt 0.250000 0.500000 +vt 0.250000 0.437500 +vt 0.250000 0.375000 +vt 0.250000 0.312500 +vt 0.250000 0.250000 +vt 0.250000 0.187500 +vt 0.250000 0.125000 +vt 0.250000 0.062500 +vt 0.187500 0.937500 +vt 0.187500 0.875000 +vt 0.187500 0.812500 +vt 0.187500 0.750000 +vt 0.187500 0.687500 +vt 0.187500 0.625000 +vt 0.187500 0.562500 +vt 0.187500 0.500000 +vt 0.187500 0.437500 +vt 0.187500 0.375000 +vt 0.187500 0.312500 +vt 0.187500 0.250000 +vt 0.187500 0.187500 +vt 0.187500 0.125000 +vt 0.187500 0.062500 +vt 0.125000 0.937500 +vt 0.125000 0.875000 +vt 0.125000 0.812500 +vt 0.125000 0.750000 +vt 0.125000 0.687500 +vt 0.125000 0.625000 +vt 0.125000 0.562500 +vt 0.125000 0.500000 +vt 0.125000 0.437500 +vt 0.125000 0.375000 +vt 0.125000 0.312500 +vt 0.125000 0.250000 +vt 0.125000 0.187500 +vt 0.125000 0.125000 +vt 0.125000 0.062500 +vt 0.062500 0.937500 +vt 0.062500 0.875000 +vt 0.062500 0.812500 +vt 0.062500 0.750000 +vt 0.062500 0.687500 +vt 0.062500 0.625000 +vt 0.062500 0.562500 +vt 0.062500 0.500000 +vt 0.062500 0.437500 +vt 0.062500 0.375000 +vt 0.062500 0.312500 +vt 0.062500 0.250000 +vt 0.062500 0.187500 +vt 0.062500 0.125000 +vt 0.062500 0.062500 +vt 0.000000 0.937500 +vt 1.000000 0.937500 +vt -0.000000 0.875000 +vt 1.000000 0.875000 +vt 0.000000 0.812500 +vt 1.000000 0.812500 +vt 0.000000 0.750000 +vt 1.000000 0.750000 +vt 0.000000 0.687500 +vt 1.000000 0.687500 +vt 0.000000 0.625000 +vt 1.000000 0.625000 +vt 0.000000 0.562500 +vt 1.000000 0.562500 +vt 1.000000 0.500000 +vt 0.000000 0.500000 +vt 1.000000 0.437500 +vt 0.000000 0.437500 +vt 1.000000 0.375000 +vt 0.000000 0.375000 +vt 1.000000 0.312500 +vt 0.000000 0.312500 +vt 1.000000 0.250000 +vt 0.000000 0.250000 +vt 1.000000 0.187500 +vt 0.000000 0.187500 +vt 1.000000 0.125000 +vt -0.000000 0.125000 +vt 1.000000 0.062500 +vt 0.000000 0.062500 +vt 0.937500 0.937500 +vt 0.937500 0.875000 +vt 0.937500 0.812500 +vt 0.937500 0.750000 +vt 0.937500 0.687500 +vt 0.937500 0.625000 +vt 0.937500 0.562500 +vt 0.937500 0.500000 +vt 0.937500 0.437500 +vt 0.937500 0.375000 +vt 0.937500 0.312500 +vt 0.937500 0.250000 +vt 0.937500 0.187500 +vt 0.937500 0.125000 +vt 0.937500 0.062500 +vt 0.875000 0.937500 +vt 0.875000 0.875000 +vt 0.875000 0.812500 +vt 0.875000 0.750000 +vt 0.875000 0.687500 +vt 0.875000 0.625000 +vt 0.875000 0.562500 +vt 0.875000 0.500000 +vt 0.875000 0.437500 +vt 0.875000 0.375000 +vt 0.875000 0.312500 +vt 0.875000 0.250000 +vt 0.875000 0.187500 +vt 0.875000 0.125000 +vt 0.875000 0.062500 +vt 0.812500 0.937500 +vt 0.812500 0.875000 +vt 0.812500 0.812500 +vt 0.812500 0.750000 +vt 0.812500 0.687500 +vt 0.812500 0.625000 +vt 0.812500 0.562500 +vt 0.812500 0.500000 +vt 0.812500 0.437500 +vt 0.812500 0.375000 +vt 0.812500 0.312500 +vt 0.812500 0.250000 +vt 0.812500 0.187500 +vt 0.812500 0.125000 +vt 0.812500 0.062500 +vt 0.750000 0.937500 +vt 0.750000 0.875000 +vt 0.750000 0.812500 +vt 0.750000 0.750000 +vt 0.750000 0.687500 +vt 0.750000 0.625000 +vt 0.750000 0.562500 +vt 0.750000 0.500000 +vt 0.750000 0.437500 +vt 0.750000 0.375000 +vt 0.750000 0.312500 +vt 0.750000 0.250000 +vt 0.750000 0.187500 +vt 0.750000 0.125000 +vt 0.750000 0.062500 +vt 0.687500 0.937500 +vt 0.687500 0.875000 +vt 0.687500 0.812500 +vt 0.687500 0.750000 +vt 0.687500 0.687500 +vt 0.687500 0.625000 +vt 0.687500 0.562500 +vt 0.687500 0.500000 +vt 0.687500 0.437500 +vt 0.687500 0.375000 +vt 0.687500 0.312500 +vt 0.687500 0.250000 +vt 0.687500 0.187500 +vt 0.687500 0.125000 +vt 0.687500 0.062500 +vt 0.625000 0.937500 +vt 0.625000 0.875000 +vt 0.625000 0.812500 +vt 0.625000 0.750000 +vt 0.625000 0.687500 +vt 0.625000 0.625000 +vt 0.625000 0.562500 +vt 0.625000 0.500000 +vt 0.625000 0.437500 +vt 0.625000 0.375000 +vt 0.625000 0.312500 +vt 0.625000 0.250000 +vt 0.625000 0.187500 +vt 0.625000 0.125000 +vt 0.625000 0.062500 +vt 0.562500 0.937500 +vt 0.562500 0.875000 +vt 0.562500 0.812500 +vt 0.562500 0.750000 +vt 0.562500 0.687500 +vt 0.562500 0.625000 +vt 0.562500 0.562500 +vt 0.562500 0.500000 +vt 0.562500 0.437500 +vt 0.562500 0.375000 +vt 0.562500 0.312500 +vt 0.562500 0.250000 +vt 0.562500 0.187500 +vt 0.562500 0.125000 +vt 0.562500 0.062500 +vt 0.500000 0.937500 +vt 0.500000 0.875000 +vt 0.500000 0.812500 +vt 0.500000 0.750000 +vt 0.500000 0.687500 +vt 0.500000 0.625000 +vt 0.500000 0.562500 +vt 0.500000 0.500000 +vt 0.500000 0.437500 +vt 0.500000 0.375000 +vt 0.500000 0.312500 +vt 0.500000 0.250000 +vt 0.500000 0.187500 +vt 0.500000 0.125000 +vt 0.500000 0.062500 +vt 0.468750 1.000000 +vt 0.406250 1.000000 +vt 0.343750 1.000000 +vt 0.281250 1.000000 +vt 0.218750 1.000000 +vt 0.156250 1.000000 +vt 0.093750 1.000000 +vt 0.031250 1.000000 +vt 0.968750 1.000000 +vt 0.906250 1.000000 +vt 0.843750 1.000000 +vt 0.781250 1.000000 +vt 0.718750 1.000000 +vt 0.656250 1.000000 +vt 0.593750 1.000000 +vt 0.531250 1.000000 +vt 0.437500 0.937500 +vt 0.437500 0.875000 +vt 0.437500 0.812500 +vt 0.437500 0.750000 +vt 0.437500 0.687500 +vt 0.437500 0.625000 +vt 0.437500 0.562500 +vt 0.437500 0.500000 +vt 0.437500 0.437500 +vt 0.437500 0.375000 +vt 0.437500 0.312500 +vt 0.437500 0.250000 +vt 0.437500 0.187500 +vt 0.437500 0.125000 +vt 0.437500 0.062500 +vt 0.375000 0.937500 +vt 0.375000 0.875000 +vt 0.375000 0.812500 +vt 0.375000 0.750000 +vt 0.375000 0.687500 +vt 0.375000 0.625000 +vt 0.375000 0.562500 +vt 0.375000 0.500000 +vt 0.375000 0.437500 +vt 0.375000 0.375000 +vt 0.375000 0.312500 +vt 0.375000 0.250000 +vt 0.375000 0.187500 +vt 0.375000 0.125000 +vt 0.375000 0.062500 +vt 0.312500 0.937500 +vt 0.312500 0.875000 +vt 0.312500 0.812500 +vt 0.312500 0.750000 +vt 0.312500 0.687500 +vt 0.312500 0.625000 +vt 0.312500 0.562500 +vt 0.312500 0.500000 +vt 0.312500 0.437500 +vt 0.312500 0.375000 +vt 0.312500 0.312500 +vt 0.312500 0.250000 +vt 0.312500 0.187500 +vt 0.312500 0.125000 +vt 0.312500 0.062500 +vt 0.250000 0.937500 +vt 0.250000 0.875000 +vt 0.250000 0.812500 +vt 0.250000 0.750000 +vt 0.250000 0.687500 +vt 0.250000 0.625000 +vt 0.250000 0.562500 +vt 0.250000 0.500000 +vt 0.250000 0.437500 +vt 0.250000 0.375000 +vt 0.250000 0.312500 +vt 0.250000 0.250000 +vt 0.250000 0.187500 +vt 0.250000 0.125000 +vt 0.250000 0.062500 +vt 0.187500 0.937500 +vt 0.187500 0.875000 +vt 0.187500 0.812500 +vt 0.187500 0.750000 +vt 0.187500 0.687500 +vt 0.187500 0.625000 +vt 0.187500 0.562500 +vt 0.187500 0.500000 +vt 0.187500 0.437500 +vt 0.187500 0.375000 +vt 0.187500 0.312500 +vt 0.187500 0.250000 +vt 0.187500 0.187500 +vt 0.187500 0.125000 +vt 0.187500 0.062500 +vt 0.125000 0.937500 +vt 0.125000 0.875000 +vt 0.125000 0.812500 +vt 0.125000 0.750000 +vt 0.125000 0.687500 +vt 0.125000 0.625000 +vt 0.125000 0.562500 +vt 0.125000 0.500000 +vt 0.125000 0.437500 +vt 0.125000 0.375000 +vt 0.125000 0.312500 +vt 0.125000 0.250000 +vt 0.125000 0.187500 +vt 0.125000 0.125000 +vt 0.125000 0.062500 +vt 0.062500 0.937500 +vt 0.062500 0.875000 +vt 0.062500 0.812500 +vt 0.062500 0.750000 +vt 0.062500 0.687500 +vt 0.062500 0.625000 +vt 0.062500 0.562500 +vt 0.062500 0.500000 +vt 0.062500 0.437500 +vt 0.062500 0.375000 +vt 0.062500 0.312500 +vt 0.062500 0.250000 +vt 0.062500 0.187500 +vt 0.062500 0.125000 +vt 0.062500 0.062500 +vt 0.000000 0.937500 +vt 1.000000 0.937500 +vt 0.000000 0.875000 +vt 1.000000 0.875000 +vt 0.000000 0.812500 +vt 1.000000 0.812500 +vt 0.000000 0.750000 +vt 1.000000 0.750000 +vt 0.000000 0.687500 +vt 1.000000 0.687500 +vt 0.000000 0.625000 +vt 1.000000 0.625000 +vt 0.000000 0.562500 +vt 1.000000 0.562500 +vt 1.000000 0.500000 +vt 0.000000 0.500000 +vt 1.000000 0.437500 +vt 0.000000 0.437500 +vt 1.000000 0.375000 +vt 0.000000 0.375000 +vt 1.000000 0.312500 +vt 0.000000 0.312500 +vt 1.000000 0.250000 +vt 0.000000 0.250000 +vt 1.000000 0.187500 +vt 0.000000 0.187500 +vt 1.000000 0.125000 +vt 0.000000 0.125000 +vt 1.000000 0.062500 +vt 0.000000 0.062500 +vt 0.937500 0.937500 +vt 0.937500 0.875000 +vt 0.937500 0.812500 +vt 0.937500 0.750000 +vt 0.937500 0.687500 +vt 0.937500 0.625000 +vt 0.937500 0.562500 +vt 0.937500 0.500000 +vt 0.937500 0.437500 +vt 0.937500 0.375000 +vt 0.937500 0.312500 +vt 0.937500 0.250000 +vt 0.937500 0.187500 +vt 0.937500 0.125000 +vt 0.937500 0.062500 +vt 0.875000 0.937500 +vt 0.875000 0.875000 +vt 0.875000 0.812500 +vt 0.875000 0.750000 +vt 0.875000 0.687500 +vt 0.875000 0.625000 +vt 0.875000 0.562500 +vt 0.875000 0.500000 +vt 0.875000 0.437500 +vt 0.875000 0.375000 +vt 0.875000 0.312500 +vt 0.875000 0.250000 +vt 0.875000 0.187500 +vt 0.875000 0.125000 +vt 0.875000 0.062500 +vt 0.812500 0.937500 +vt 0.812500 0.875000 +vt 0.812500 0.812500 +vt 0.812500 0.750000 +vt 0.812500 0.687500 +vt 0.812500 0.625000 +vt 0.812500 0.562500 +vt 0.812500 0.500000 +vt 0.812500 0.437500 +vt 0.812500 0.375000 +vt 0.812500 0.312500 +vt 0.812500 0.250000 +vt 0.812500 0.187500 +vt 0.812500 0.125000 +vt 0.812500 0.062500 +vt 0.750000 0.937500 +vt 0.750000 0.875000 +vt 0.750000 0.812500 +vt 0.750000 0.750000 +vt 0.750000 0.687500 +vt 0.750000 0.625000 +vt 0.750000 0.562500 +vt 0.750000 0.500000 +vt 0.750000 0.437500 +vt 0.750000 0.375000 +vt 0.750000 0.312500 +vt 0.750000 0.250000 +vt 0.750000 0.187500 +vt 0.750000 0.125000 +vt 0.750000 0.062500 +vt 0.687500 0.937500 +vt 0.687500 0.875000 +vt 0.687500 0.812500 +vt 0.687500 0.750000 +vt 0.687500 0.687500 +vt 0.687500 0.625000 +vt 0.687500 0.562500 +vt 0.687500 0.500000 +vt 0.687500 0.437500 +vt 0.687500 0.375000 +vt 0.687500 0.312500 +vt 0.687500 0.250000 +vt 0.687500 0.187500 +vt 0.687500 0.125000 +vt 0.687500 0.062500 +vt 0.625000 0.937500 +vt 0.625000 0.875000 +vt 0.625000 0.812500 +vt 0.625000 0.750000 +vt 0.625000 0.687500 +vt 0.625000 0.625000 +vt 0.625000 0.562500 +vt 0.625000 0.500000 +vt 0.625000 0.437500 +vt 0.625000 0.375000 +vt 0.625000 0.312500 +vt 0.625000 0.250000 +vt 0.625000 0.187500 +vt 0.625000 0.125000 +vt 0.625000 0.062500 +vt 0.562500 0.937500 +vt 0.562500 0.875000 +vt 0.562500 0.812500 +vt 0.562500 0.750000 +vt 0.562500 0.687500 +vt 0.562500 0.625000 +vt 0.562500 0.562500 +vt 0.562500 0.500000 +vt 0.562500 0.437500 +vt 0.562500 0.375000 +vt 0.562500 0.312500 +vt 0.562500 0.250000 +vt 0.562500 0.187500 +vt 0.562500 0.125000 +vt 0.562500 0.062500 +vt 0.500000 0.937500 +vt 0.500000 0.875000 +vt 0.500000 0.687500 +vt 0.500000 0.250000 +vt 0.500000 0.125000 +vt 0.500000 0.062500 +s 1 +f 9/9/1 23/23/2 481/541/3 +f 4/4/4 15/15/5 16/16/6 +f 481/541/3 24/24/7 482/542/8 +f 5/5/9 16/16/6 17/17/10 +f 477/537/11 251/281/12 10/10/13 +f 55/55/14 482/542/8 24/24/7 +f 5/5/9 18/18/15 6/6/16 +f 478/538/17 10/10/13 11/11/18 +f 6/6/16 19/19/19 7/7/20 +f 1/1/21 11/11/18 12/12/22 +f 7/7/20 20/20/23 8/8/24 +f 2/2/25 12/12/22 13/13/26 +f 8/8/24 21/21/27 480/540/28 +f 479/539/29 13/13/26 14/14/30 +f 480/540/28 22/22/31 9/9/1 +f 3/3/32 14/14/30 15/15/5 +f 21/21/27 37/37/33 22/22/31 +f 15/15/5 29/29/34 30/30/35 +f 22/22/31 38/38/36 23/23/2 +f 16/16/6 30/30/35 31/31/37 +f 23/23/2 39/39/38 24/24/7 +f 17/17/10 31/31/37 32/32/39 +f 10/10/13 251/282/12 25/25/40 +f 55/56/14 24/24/7 39/39/38 +f 17/17/10 33/33/41 18/18/15 +f 11/11/18 25/25/40 26/26/42 +f 18/18/15 34/34/43 19/19/19 +f 12/12/22 26/26/42 27/27/44 +f 19/19/19 35/35/45 20/20/23 +f 13/13/26 27/27/44 28/28/46 +f 20/20/23 36/36/47 21/21/27 +f 14/14/30 28/28/46 29/29/34 +f 26/26/42 40/40/48 41/41/49 +f 33/33/41 49/49/50 34/34/43 +f 27/27/44 41/41/49 42/42/51 +f 34/34/43 50/50/52 35/35/45 +f 28/28/46 42/42/51 43/43/53 +f 35/35/45 51/51/54 36/36/47 +f 29/29/34 43/43/53 44/44/55 +f 36/36/47 52/52/56 37/37/33 +f 30/30/35 44/44/55 45/45/57 +f 37/37/33 53/53/58 38/38/36 +f 31/31/37 45/45/57 46/46/59 +f 38/38/36 54/54/60 39/39/38 +f 32/32/39 46/46/59 47/47/61 +f 25/25/40 251/283/12 40/40/48 +f 55/57/14 39/39/38 54/54/60 +f 32/32/39 48/48/62 33/33/41 +f 45/45/57 60/75/63 61/76/64 +f 52/52/56 69/84/65 53/53/58 +f 46/46/59 61/76/64 62/77/66 +f 53/53/58 70/85/67 54/54/60 +f 47/47/61 62/77/66 63/78/68 +f 40/40/48 251/284/12 56/71/69 +f 55/58/14 54/54/60 70/85/67 +f 47/47/61 64/79/70 48/48/62 +f 41/41/49 56/71/69 57/72/71 +f 48/48/62 65/80/72 49/49/50 +f 42/42/51 57/72/71 58/73/73 +f 49/49/50 66/81/74 50/50/52 +f 43/43/53 58/73/73 59/74/75 +f 50/50/52 67/82/76 51/51/54 +f 44/44/55 59/74/75 60/75/63 +f 51/51/54 68/83/77 52/52/56 +f 64/79/70 80/95/78 65/80/72 +f 58/73/73 72/87/79 73/88/80 +f 65/80/72 81/96/81 66/81/74 +f 59/74/75 73/88/80 74/89/82 +f 66/81/74 82/97/83 67/82/76 +f 60/75/63 74/89/82 75/90/84 +f 67/82/76 83/98/85 68/83/77 +f 61/76/64 75/90/84 76/91/86 +f 68/83/77 84/99/87 69/84/65 +f 62/77/66 76/91/86 77/92/88 +f 69/84/65 85/100/89 70/85/67 +f 63/78/68 77/92/88 78/93/90 +f 56/71/69 251/285/12 71/86/91 +f 55/59/14 70/85/67 85/100/89 +f 63/78/68 79/94/92 64/79/70 +f 57/72/71 71/86/91 72/87/79 +f 83/98/85 99/114/93 84/99/87 +f 77/92/88 91/106/94 92/107/95 +f 84/99/87 100/115/96 85/100/89 +f 78/93/90 92/107/95 93/108/97 +f 71/86/91 251/286/12 86/101/98 +f 55/60/14 85/100/89 100/115/96 +f 78/93/90 94/109/99 79/94/92 +f 72/87/79 86/101/98 87/102/100 +f 79/94/92 95/110/101 80/95/78 +f 73/88/80 87/102/100 88/103/102 +f 80/95/78 96/111/103 81/96/81 +f 74/89/82 88/103/102 89/104/104 +f 81/96/81 97/112/105 82/97/83 +f 75/90/84 89/104/104 90/105/106 +f 82/97/83 98/113/107 83/98/85 +f 76/91/86 90/105/106 91/106/94 +f 95/110/101 111/126/108 96/111/103 +f 89/104/104 103/118/109 104/119/110 +f 96/111/103 112/127/111 97/112/105 +f 90/105/106 104/119/110 105/120/112 +f 97/112/105 113/128/113 98/113/107 +f 91/106/94 105/120/112 106/121/114 +f 98/113/107 114/129/115 99/114/93 +f 92/107/95 106/121/114 107/122/116 +f 99/114/93 115/130/117 100/115/96 +f 93/108/97 107/122/116 108/123/118 +f 86/101/98 251/287/12 101/116/119 +f 55/61/14 100/115/96 115/130/117 +f 93/108/97 109/124/120 94/109/99 +f 87/102/100 101/116/119 102/117/121 +f 94/109/99 110/125/122 95/110/101 +f 88/103/102 102/117/121 103/118/109 +f 114/129/115 130/160/123 115/130/117 +f 108/123/118 122/143/124 123/146/125 +f 101/116/119 251/288/12 116/131/126 +f 55/62/14 115/130/117 130/160/123 +f 108/123/118 124/148/127 109/124/120 +f 102/117/121 116/131/126 117/133/128 +f 109/124/120 125/150/129 110/125/122 +f 103/118/109 117/133/128 118/135/130 +f 110/125/122 126/152/131 111/126/108 +f 104/119/110 118/135/130 119/137/132 +f 111/126/108 127/154/133 112/127/111 +f 105/120/112 119/137/132 120/139/134 +f 112/127/111 128/156/135 113/128/113 +f 106/121/114 120/139/134 121/141/136 +f 113/128/113 129/158/137 114/129/115 +f 107/122/116 121/141/136 122/143/124 +f 118/136/130 134/164/138 119/138/132 +f 127/153/133 141/171/139 142/172/140 +f 119/138/132 135/165/141 120/140/134 +f 128/155/135 142/172/140 143/173/142 +f 120/140/134 136/166/143 121/142/136 +f 129/157/137 143/173/142 144/174/144 +f 121/142/136 137/167/145 122/144/124 +f 130/159/123 144/174/144 145/175/146 +f 122/144/124 138/168/147 123/145/125 +f 116/132/126 251/289/12 131/161/148 +f 55/63/14 130/159/123 145/175/146 +f 124/147/127 138/168/147 139/169/149 +f 116/132/126 132/162/150 117/134/128 +f 125/149/129 139/169/149 140/170/151 +f 117/134/128 133/163/152 118/136/130 +f 126/151/131 140/170/151 141/171/139 +f 137/167/145 153/183/153 138/168/147 +f 131/161/148 251/290/12 146/176/154 +f 55/64/14 145/175/146 160/190/155 +f 139/169/149 153/183/153 154/184/156 +f 131/161/148 147/177/157 132/162/150 +f 140/170/151 154/184/156 155/185/158 +f 132/162/150 148/178/159 133/163/152 +f 141/171/139 155/185/158 156/186/160 +f 133/163/152 149/179/161 134/164/138 +f 142/172/140 156/186/160 157/187/162 +f 134/164/138 150/180/163 135/165/141 +f 143/173/142 157/187/162 158/188/164 +f 135/165/141 151/181/165 136/166/143 +f 144/174/144 158/188/164 159/189/166 +f 136/166/143 152/182/167 137/167/145 +f 145/175/146 159/189/166 160/190/155 +f 157/187/162 171/201/168 172/202/169 +f 149/179/161 165/195/170 150/180/163 +f 158/188/164 172/202/169 173/203/171 +f 150/180/163 166/196/172 151/181/165 +f 159/189/166 173/203/171 174/204/173 +f 151/181/165 167/197/174 152/182/167 +f 160/190/155 174/204/173 175/205/175 +f 152/182/167 168/198/176 153/183/153 +f 146/176/154 251/291/12 161/191/177 +f 55/65/14 160/190/155 175/205/175 +f 154/184/156 168/198/176 169/199/178 +f 146/176/154 162/192/179 147/177/157 +f 155/185/158 169/199/178 170/200/180 +f 147/177/157 163/193/181 148/178/159 +f 156/186/160 170/200/180 171/201/168 +f 148/178/159 164/194/182 149/179/161 +f 161/191/177 251/292/12 176/206/183 +f 55/66/14 175/205/175 190/220/184 +f 169/199/178 183/213/185 184/214/186 +f 161/191/177 177/207/187 162/192/179 +f 170/200/180 184/214/186 185/215/188 +f 162/192/179 178/208/189 163/193/181 +f 171/201/168 185/215/188 186/216/190 +f 163/193/181 179/209/191 164/194/182 +f 172/202/169 186/216/190 187/217/192 +f 164/194/182 180/210/193 165/195/170 +f 173/203/171 187/217/192 188/218/194 +f 165/195/170 181/211/195 166/196/172 +f 174/204/173 188/218/194 189/219/196 +f 166/196/172 182/212/197 167/197/174 +f 175/205/175 189/219/196 190/220/184 +f 167/197/174 183/213/185 168/198/176 +f 179/209/191 195/225/198 180/210/193 +f 188/218/194 202/232/199 203/233/200 +f 180/210/193 196/226/201 181/211/195 +f 189/219/196 203/233/200 204/234/202 +f 181/211/195 197/227/203 182/212/197 +f 190/220/184 204/234/202 205/235/204 +f 182/212/197 198/228/205 183/213/185 +f 176/206/183 251/293/12 191/221/206 +f 55/67/14 190/220/184 205/235/204 +f 184/214/186 198/228/205 199/229/207 +f 176/206/183 192/222/208 177/207/187 +f 185/215/188 199/229/207 200/230/209 +f 177/207/187 193/223/210 178/208/189 +f 186/216/190 200/230/209 201/231/211 +f 178/208/189 194/224/212 179/209/191 +f 187/217/192 201/231/211 202/232/199 +f 199/229/207 213/243/213 214/244/214 +f 191/221/206 207/237/215 192/222/208 +f 200/230/209 214/244/214 215/245/216 +f 192/222/208 208/238/217 193/223/210 +f 201/231/211 215/245/216 216/246/218 +f 193/223/210 209/239/219 194/224/212 +f 202/232/199 216/246/218 217/247/220 +f 194/224/212 210/240/221 195/225/198 +f 203/233/200 217/247/220 218/248/222 +f 195/225/198 211/241/223 196/226/201 +f 204/234/202 218/248/222 219/249/224 +f 196/226/201 212/242/225 197/227/203 +f 205/235/204 219/249/224 220/250/226 +f 197/227/203 213/243/213 198/228/205 +f 191/221/206 251/294/12 206/236/227 +f 55/68/14 205/235/204 220/250/226 +f 218/248/222 232/262/228 233/263/229 +f 210/240/221 226/256/230 211/241/223 +f 219/249/224 233/263/229 234/264/231 +f 211/241/223 227/257/232 212/242/225 +f 220/250/226 234/264/231 235/265/233 +f 212/242/225 228/258/234 213/243/213 +f 206/236/227 251/295/12 221/251/235 +f 55/69/14 220/250/226 235/265/233 +f 214/244/214 228/258/234 229/259/236 +f 206/236/227 222/252/237 207/237/215 +f 215/245/216 229/259/236 230/260/238 +f 207/237/215 223/253/239 208/238/217 +f 216/246/218 230/260/238 231/261/240 +f 208/238/217 224/254/241 209/239/219 +f 217/247/220 231/261/240 232/262/228 +f 209/239/219 225/255/242 210/240/221 +f 230/260/238 244/274/243 245/275/244 +f 222/252/237 238/268/245 223/253/239 +f 231/261/240 245/275/244 246/276/246 +f 223/253/239 239/269/247 224/254/241 +f 232/262/228 246/276/246 247/277/248 +f 224/254/241 240/270/249 225/255/242 +f 233/263/229 247/277/248 248/278/250 +f 225/255/242 241/271/251 226/256/230 +f 234/264/231 248/278/250 249/279/252 +f 226/256/230 242/272/253 227/257/232 +f 235/265/233 249/279/252 250/280/254 +f 227/257/232 243/273/255 228/258/234 +f 221/251/235 251/296/12 236/266/256 +f 55/70/14 235/265/233 250/280/254 +f 229/259/236 243/273/255 244/274/243 +f 221/251/235 237/267/257 222/252/237 +f 248/278/250 265/310/258 249/279/252 +f 242/272/253 257/302/259 258/303/260 +f 249/279/252 266/311/261 250/280/254 +f 243/273/255 258/303/260 259/304/262 +f 236/266/256 251/281/12 252/297/263 +f 55/55/14 250/280/254 266/311/261 +f 243/273/255 260/305/264 244/274/243 +f 237/267/257 252/297/263 253/298/265 +f 244/274/243 261/306/266 245/275/244 +f 238/268/245 253/298/265 254/299/267 +f 245/275/244 262/307/268 246/276/246 +f 239/269/247 254/299/267 255/300/269 +f 246/276/246 263/308/270 247/277/248 +f 240/270/249 255/300/269 256/301/271 +f 247/277/248 264/309/272 248/278/250 +f 241/271/251 256/301/271 257/302/259 +f 254/299/267 268/313/273 269/314/274 +f 261/306/266 277/322/275 262/307/268 +f 255/300/269 269/314/274 270/315/276 +f 262/307/268 278/323/277 263/308/270 +f 256/301/271 270/315/276 271/316/278 +f 263/308/270 279/324/279 264/309/272 +f 257/302/259 271/316/278 272/317/280 +f 264/309/272 280/325/281 265/310/258 +f 258/303/260 272/317/280 273/318/282 +f 265/310/258 281/326/283 266/311/261 +f 259/304/262 273/318/282 274/319/284 +f 252/297/263 251/282/12 267/312/285 +f 55/56/14 266/311/261 281/326/283 +f 259/304/262 275/320/286 260/305/264 +f 253/298/265 267/312/285 268/313/273 +f 260/305/264 276/321/287 261/306/266 +f 273/318/282 287/332/288 288/333/289 +f 280/325/281 296/341/290 281/326/283 +f 274/319/284 288/333/289 289/334/291 +f 267/312/285 251/283/12 282/327/292 +f 55/57/14 281/326/283 296/341/290 +f 274/319/284 290/335/293 275/320/286 +f 268/313/273 282/327/292 283/328/294 +f 275/320/286 291/336/295 276/321/287 +f 269/314/274 283/328/294 284/329/296 +f 276/321/287 292/337/297 277/322/275 +f 270/315/276 284/329/296 285/330/298 +f 277/322/275 293/338/299 278/323/277 +f 271/316/278 285/330/298 286/331/300 +f 278/323/277 294/339/301 279/324/279 +f 272/317/280 286/331/300 287/332/288 +f 279/324/279 295/340/302 280/325/281 +f 291/336/295 307/352/303 292/337/297 +f 285/330/298 299/344/304 300/345/305 +f 292/337/297 308/353/306 293/338/299 +f 286/331/300 300/345/305 301/346/307 +f 293/338/299 309/354/308 294/339/301 +f 287/332/288 301/346/307 302/347/309 +f 294/339/301 310/355/310 295/340/302 +f 288/333/289 302/347/309 303/348/311 +f 295/340/302 311/356/312 296/341/290 +f 289/334/291 303/348/311 304/349/313 +f 282/327/292 251/284/12 297/342/314 +f 55/58/14 296/341/290 311/356/312 +f 289/334/291 305/350/315 290/335/293 +f 283/328/294 297/342/314 298/343/316 +f 290/335/293 306/351/317 291/336/295 +f 284/329/296 298/343/316 299/344/304 +f 310/355/310 326/371/318 311/356/312 +f 304/349/313 318/363/319 319/364/320 +f 297/342/314 251/285/12 312/357/321 +f 55/59/14 311/356/312 326/371/318 +f 304/349/313 320/365/322 305/350/315 +f 298/343/316 312/357/321 313/358/323 +f 305/350/315 321/366/324 306/351/317 +f 299/344/304 313/358/323 314/359/325 +f 306/351/317 322/367/326 307/352/303 +f 300/345/305 314/359/325 315/360/327 +f 307/352/303 323/368/328 308/353/306 +f 301/346/307 315/360/327 316/361/329 +f 308/353/306 324/369/330 309/354/308 +f 302/347/309 316/361/329 317/362/331 +f 309/354/308 325/370/332 310/355/310 +f 303/348/311 317/362/331 318/363/319 +f 315/360/327 329/374/333 330/375/334 +f 322/367/326 338/383/335 323/368/328 +f 316/361/329 330/375/334 331/376/336 +f 323/368/328 339/384/337 324/369/330 +f 317/362/331 331/376/336 332/377/338 +f 324/369/330 340/385/339 325/370/332 +f 318/363/319 332/377/338 333/378/340 +f 325/370/332 341/386/341 326/371/318 +f 319/364/320 333/378/340 334/379/342 +f 312/357/321 251/286/12 327/372/343 +f 55/60/14 326/371/318 341/386/341 +f 319/364/320 335/380/344 320/365/322 +f 313/358/323 327/372/343 328/373/345 +f 320/365/322 336/381/346 321/366/324 +f 314/359/325 328/373/345 329/374/333 +f 321/366/324 337/382/347 322/367/326 +f 334/379/342 348/393/348 349/394/349 +f 327/372/343 251/287/12 342/387/350 +f 55/61/14 341/386/341 356/401/351 +f 334/379/342 350/395/352 335/380/344 +f 328/373/345 342/387/350 343/388/353 +f 335/380/344 351/396/354 336/381/346 +f 329/374/333 343/388/353 344/389/355 +f 336/381/346 352/397/356 337/382/347 +f 330/375/334 344/389/355 345/390/357 +f 337/382/347 353/398/358 338/383/335 +f 331/376/336 345/390/357 346/391/359 +f 338/383/335 354/399/360 339/384/337 +f 332/377/338 346/391/359 347/392/361 +f 339/384/337 355/400/362 340/385/339 +f 333/378/340 347/392/361 348/393/348 +f 340/385/339 356/401/351 341/386/341 +f 352/397/356 368/425/363 353/398/358 +f 346/391/359 360/408/364 361/410/365 +f 353/398/358 369/427/366 354/399/360 +f 347/392/361 361/410/365 362/412/367 +f 354/399/360 370/429/368 355/400/362 +f 348/393/348 362/412/367 363/414/369 +f 355/400/362 371/431/370 356/401/351 +f 349/394/349 363/414/369 364/417/371 +f 342/387/350 251/288/12 357/402/372 +f 55/62/14 356/401/351 371/431/370 +f 349/394/349 365/419/373 350/395/352 +f 343/388/353 357/402/372 358/404/374 +f 350/395/352 366/421/375 351/396/354 +f 344/389/355 358/404/374 359/406/376 +f 351/396/354 367/423/377 352/397/356 +f 345/390/357 359/406/376 360/408/364 +f 55/63/14 371/430/370 386/446/378 +f 365/418/373 379/439/379 380/440/380 +f 357/403/372 373/433/381 358/405/374 +f 366/420/375 380/440/380 381/441/382 +f 358/405/374 374/434/383 359/407/376 +f 367/422/377 381/441/382 382/442/384 +f 359/407/376 375/435/385 360/409/364 +f 368/424/363 382/442/384 383/443/386 +f 360/409/364 376/436/387 361/411/365 +f 369/426/366 383/443/386 384/444/388 +f 361/411/365 377/437/389 362/413/367 +f 370/428/368 384/444/388 385/445/390 +f 362/413/367 378/438/391 363/415/369 +f 371/430/370 385/445/390 386/446/378 +f 363/415/369 379/439/379 364/416/371 +f 357/403/372 251/289/12 372/432/392 +f 384/444/388 398/458/393 399/459/394 +f 376/436/387 392/452/395 377/437/389 +f 385/445/390 399/459/394 400/460/396 +f 377/437/389 393/453/397 378/438/391 +f 386/446/378 400/460/396 401/461/398 +f 378/438/391 394/454/399 379/439/379 +f 372/432/392 251/290/12 387/447/400 +f 55/64/14 386/446/378 401/461/398 +f 380/440/380 394/454/399 395/455/401 +f 372/432/392 388/448/402 373/433/381 +f 381/441/382 395/455/401 396/456/403 +f 373/433/381 389/449/404 374/434/383 +f 382/442/384 396/456/403 397/457/405 +f 374/434/383 390/450/406 375/435/385 +f 383/443/386 397/457/405 398/458/393 +f 375/435/385 391/451/407 376/436/387 +f 387/447/400 403/463/408 388/448/402 +f 396/456/403 410/470/409 411/471/410 +f 388/448/402 404/464/411 389/449/404 +f 397/457/405 411/471/410 412/472/412 +f 389/449/404 405/465/413 390/450/406 +f 398/458/393 412/472/412 413/473/414 +f 390/450/406 406/466/415 391/451/407 +f 399/459/394 413/473/414 414/474/416 +f 391/451/407 407/467/417 392/452/395 +f 400/460/396 414/474/416 415/475/418 +f 392/452/395 408/468/419 393/453/397 +f 401/461/398 415/475/418 416/476/420 +f 393/453/397 409/469/421 394/454/399 +f 387/447/400 251/291/12 402/462/422 +f 55/65/14 401/461/398 416/476/420 +f 395/455/401 409/469/421 410/470/409 +f 406/466/415 422/482/423 407/467/417 +f 415/475/418 429/489/424 430/490/425 +f 407/467/417 423/483/426 408/468/419 +f 416/476/420 430/490/425 431/491/427 +f 408/468/419 424/484/428 409/469/421 +f 402/462/422 251/292/12 417/477/429 +f 55/66/14 416/476/420 431/491/427 +f 410/470/409 424/484/428 425/485/430 +f 402/462/422 418/478/431 403/463/408 +f 411/471/410 425/485/430 426/486/432 +f 403/463/408 419/479/433 404/464/411 +f 412/472/412 426/486/432 427/487/434 +f 404/464/411 420/480/435 405/465/413 +f 413/473/414 427/487/434 428/488/436 +f 405/465/413 421/481/437 406/466/415 +f 414/474/416 428/488/436 429/489/424 +f 426/486/432 440/500/438 441/501/439 +f 418/478/431 434/494/440 419/479/433 +f 427/487/434 441/501/439 442/502/441 +f 419/479/433 435/495/442 420/480/435 +f 428/488/436 442/502/441 443/503/443 +f 420/480/435 436/496/444 421/481/437 +f 429/489/424 443/503/443 444/504/445 +f 421/481/437 437/497/446 422/482/423 +f 430/490/425 444/504/445 445/505/447 +f 422/482/423 438/498/448 423/483/426 +f 431/491/427 445/505/447 446/506/449 +f 423/483/426 439/499/450 424/484/428 +f 417/477/429 251/293/12 432/492/451 +f 55/67/14 431/491/427 446/506/449 +f 425/485/430 439/499/450 440/500/438 +f 417/477/429 433/493/452 418/478/431 +f 445/505/447 459/519/453 460/520/454 +f 437/497/446 453/513/455 438/498/448 +f 446/506/449 460/520/454 461/521/456 +f 438/498/448 454/514/457 439/499/450 +f 432/492/451 251/294/12 447/507/458 +f 55/68/14 446/506/449 461/521/456 +f 440/500/438 454/514/457 455/515/459 +f 432/492/451 448/508/460 433/493/452 +f 441/501/439 455/515/459 456/516/461 +f 433/493/452 449/509/462 434/494/440 +f 442/502/441 456/516/461 457/517/463 +f 434/494/440 450/510/464 435/495/442 +f 443/503/443 457/517/463 458/518/465 +f 435/495/442 451/511/466 436/496/444 +f 444/504/445 458/518/465 459/519/453 +f 436/496/444 452/512/467 437/497/446 +f 448/508/460 464/524/468 449/509/462 +f 457/517/463 471/531/469 472/532/470 +f 449/509/462 465/525/471 450/510/464 +f 458/518/465 472/532/470 473/533/472 +f 450/510/464 466/526/473 451/511/466 +f 459/519/453 473/533/472 474/534/474 +f 451/511/466 467/527/475 452/512/467 +f 460/520/454 474/534/474 475/535/476 +f 452/512/467 468/528/477 453/513/455 +f 461/521/456 475/535/476 476/536/478 +f 453/513/455 469/529/479 454/514/457 +f 447/507/458 251/295/12 462/522/480 +f 55/69/14 461/521/456 476/536/478 +f 455/515/459 469/529/479 470/530/481 +f 447/507/458 463/523/482 448/508/460 +f 456/516/461 470/530/481 471/531/469 +f 467/527/475 4/4/4 468/528/477 +f 476/536/478 481/541/3 482/542/8 +f 468/528/477 5/5/9 469/529/479 +f 462/522/480 251/296/12 477/537/11 +f 55/70/14 476/536/478 482/542/8 +f 470/530/481 5/5/9 6/6/16 +f 462/522/480 478/538/17 463/523/482 +f 471/531/469 6/6/16 7/7/20 +f 463/523/482 1/1/21 464/524/468 +f 472/532/470 7/7/20 8/8/24 +f 464/524/468 2/2/25 465/525/471 +f 473/533/472 8/8/24 480/540/28 +f 465/525/471 479/539/29 466/526/473 +f 474/534/474 480/540/28 9/9/1 +f 466/526/473 3/3/32 467/527/475 +f 475/535/476 9/9/1 481/541/3 +f 4/4/4 3/3/32 15/15/5 +f 5/5/9 4/4/4 16/16/6 +f 478/538/17 477/537/11 10/10/13 +f 1/1/21 478/538/17 11/11/18 +f 2/2/25 1/1/21 12/12/22 +f 479/539/29 2/2/25 13/13/26 +f 3/3/32 479/539/29 14/14/30 +f 15/15/5 14/14/30 29/29/34 +f 16/16/6 15/15/5 30/30/35 +f 17/17/10 16/16/6 31/31/37 +f 11/11/18 10/10/13 25/25/40 +f 12/12/22 11/11/18 26/26/42 +f 13/13/26 12/12/22 27/27/44 +f 14/14/30 13/13/26 28/28/46 +f 26/26/42 25/25/40 40/40/48 +f 27/27/44 26/26/42 41/41/49 +f 28/28/46 27/27/44 42/42/51 +f 29/29/34 28/28/46 43/43/53 +f 30/30/35 29/29/34 44/44/55 +f 31/31/37 30/30/35 45/45/57 +f 32/32/39 31/31/37 46/46/59 +f 45/45/57 44/44/55 60/75/63 +f 46/46/59 45/45/57 61/76/64 +f 47/47/61 46/46/59 62/77/66 +f 41/41/49 40/40/48 56/71/69 +f 42/42/51 41/41/49 57/72/71 +f 43/43/53 42/42/51 58/73/73 +f 44/44/55 43/43/53 59/74/75 +f 58/73/73 57/72/71 72/87/79 +f 59/74/75 58/73/73 73/88/80 +f 60/75/63 59/74/75 74/89/82 +f 61/76/64 60/75/63 75/90/84 +f 62/77/66 61/76/64 76/91/86 +f 63/78/68 62/77/66 77/92/88 +f 57/72/71 56/71/69 71/86/91 +f 77/92/88 76/91/86 91/106/94 +f 78/93/90 77/92/88 92/107/95 +f 72/87/79 71/86/91 86/101/98 +f 73/88/80 72/87/79 87/102/100 +f 74/89/82 73/88/80 88/103/102 +f 75/90/84 74/89/82 89/104/104 +f 76/91/86 75/90/84 90/105/106 +f 89/104/104 88/103/102 103/118/109 +f 90/105/106 89/104/104 104/119/110 +f 91/106/94 90/105/106 105/120/112 +f 92/107/95 91/106/94 106/121/114 +f 93/108/97 92/107/95 107/122/116 +f 87/102/100 86/101/98 101/116/119 +f 88/103/102 87/102/100 102/117/121 +f 108/123/118 107/122/116 122/143/124 +f 102/117/121 101/116/119 116/131/126 +f 103/118/109 102/117/121 117/133/128 +f 104/119/110 103/118/109 118/135/130 +f 105/120/112 104/119/110 119/137/132 +f 106/121/114 105/120/112 120/139/134 +f 107/122/116 106/121/114 121/141/136 +f 127/153/133 126/151/131 141/171/139 +f 128/155/135 127/153/133 142/172/140 +f 129/157/137 128/155/135 143/173/142 +f 130/159/123 129/157/137 144/174/144 +f 124/147/127 123/145/125 138/168/147 +f 125/149/129 124/147/127 139/169/149 +f 126/151/131 125/149/129 140/170/151 +f 139/169/149 138/168/147 153/183/153 +f 140/170/151 139/169/149 154/184/156 +f 141/171/139 140/170/151 155/185/158 +f 142/172/140 141/171/139 156/186/160 +f 143/173/142 142/172/140 157/187/162 +f 144/174/144 143/173/142 158/188/164 +f 145/175/146 144/174/144 159/189/166 +f 157/187/162 156/186/160 171/201/168 +f 158/188/164 157/187/162 172/202/169 +f 159/189/166 158/188/164 173/203/171 +f 160/190/155 159/189/166 174/204/173 +f 154/184/156 153/183/153 168/198/176 +f 155/185/158 154/184/156 169/199/178 +f 156/186/160 155/185/158 170/200/180 +f 169/199/178 168/198/176 183/213/185 +f 170/200/180 169/199/178 184/214/186 +f 171/201/168 170/200/180 185/215/188 +f 172/202/169 171/201/168 186/216/190 +f 173/203/171 172/202/169 187/217/192 +f 174/204/173 173/203/171 188/218/194 +f 175/205/175 174/204/173 189/219/196 +f 188/218/194 187/217/192 202/232/199 +f 189/219/196 188/218/194 203/233/200 +f 190/220/184 189/219/196 204/234/202 +f 184/214/186 183/213/185 198/228/205 +f 185/215/188 184/214/186 199/229/207 +f 186/216/190 185/215/188 200/230/209 +f 187/217/192 186/216/190 201/231/211 +f 199/229/207 198/228/205 213/243/213 +f 200/230/209 199/229/207 214/244/214 +f 201/231/211 200/230/209 215/245/216 +f 202/232/199 201/231/211 216/246/218 +f 203/233/200 202/232/199 217/247/220 +f 204/234/202 203/233/200 218/248/222 +f 205/235/204 204/234/202 219/249/224 +f 218/248/222 217/247/220 232/262/228 +f 219/249/224 218/248/222 233/263/229 +f 220/250/226 219/249/224 234/264/231 +f 214/244/214 213/243/213 228/258/234 +f 215/245/216 214/244/214 229/259/236 +f 216/246/218 215/245/216 230/260/238 +f 217/247/220 216/246/218 231/261/240 +f 230/260/238 229/259/236 244/274/243 +f 231/261/240 230/260/238 245/275/244 +f 232/262/228 231/261/240 246/276/246 +f 233/263/229 232/262/228 247/277/248 +f 234/264/231 233/263/229 248/278/250 +f 235/265/233 234/264/231 249/279/252 +f 229/259/236 228/258/234 243/273/255 +f 242/272/253 241/271/251 257/302/259 +f 243/273/255 242/272/253 258/303/260 +f 237/267/257 236/266/256 252/297/263 +f 238/268/245 237/267/257 253/298/265 +f 239/269/247 238/268/245 254/299/267 +f 240/270/249 239/269/247 255/300/269 +f 241/271/251 240/270/249 256/301/271 +f 254/299/267 253/298/265 268/313/273 +f 255/300/269 254/299/267 269/314/274 +f 256/301/271 255/300/269 270/315/276 +f 257/302/259 256/301/271 271/316/278 +f 258/303/260 257/302/259 272/317/280 +f 259/304/262 258/303/260 273/318/282 +f 253/298/265 252/297/263 267/312/285 +f 273/318/282 272/317/280 287/332/288 +f 274/319/284 273/318/282 288/333/289 +f 268/313/273 267/312/285 282/327/292 +f 269/314/274 268/313/273 283/328/294 +f 270/315/276 269/314/274 284/329/296 +f 271/316/278 270/315/276 285/330/298 +f 272/317/280 271/316/278 286/331/300 +f 285/330/298 284/329/296 299/344/304 +f 286/331/300 285/330/298 300/345/305 +f 287/332/288 286/331/300 301/346/307 +f 288/333/289 287/332/288 302/347/309 +f 289/334/291 288/333/289 303/348/311 +f 283/328/294 282/327/292 297/342/314 +f 284/329/296 283/328/294 298/343/316 +f 304/349/313 303/348/311 318/363/319 +f 298/343/316 297/342/314 312/357/321 +f 299/344/304 298/343/316 313/358/323 +f 300/345/305 299/344/304 314/359/325 +f 301/346/307 300/345/305 315/360/327 +f 302/347/309 301/346/307 316/361/329 +f 303/348/311 302/347/309 317/362/331 +f 315/360/327 314/359/325 329/374/333 +f 316/361/329 315/360/327 330/375/334 +f 317/362/331 316/361/329 331/376/336 +f 318/363/319 317/362/331 332/377/338 +f 319/364/320 318/363/319 333/378/340 +f 313/358/323 312/357/321 327/372/343 +f 314/359/325 313/358/323 328/373/345 +f 334/379/342 333/378/340 348/393/348 +f 328/373/345 327/372/343 342/387/350 +f 329/374/333 328/373/345 343/388/353 +f 330/375/334 329/374/333 344/389/355 +f 331/376/336 330/375/334 345/390/357 +f 332/377/338 331/376/336 346/391/359 +f 333/378/340 332/377/338 347/392/361 +f 346/391/359 345/390/357 360/408/364 +f 347/392/361 346/391/359 361/410/365 +f 348/393/348 347/392/361 362/412/367 +f 349/394/349 348/393/348 363/414/369 +f 343/388/353 342/387/350 357/402/372 +f 344/389/355 343/388/353 358/404/374 +f 345/390/357 344/389/355 359/406/376 +f 365/418/373 364/416/371 379/439/379 +f 366/420/375 365/418/373 380/440/380 +f 367/422/377 366/420/375 381/441/382 +f 368/424/363 367/422/377 382/442/384 +f 369/426/366 368/424/363 383/443/386 +f 370/428/368 369/426/366 384/444/388 +f 371/430/370 370/428/368 385/445/390 +f 384/444/388 383/443/386 398/458/393 +f 385/445/390 384/444/388 399/459/394 +f 386/446/378 385/445/390 400/460/396 +f 380/440/380 379/439/379 394/454/399 +f 381/441/382 380/440/380 395/455/401 +f 382/442/384 381/441/382 396/456/403 +f 383/443/386 382/442/384 397/457/405 +f 396/456/403 395/455/401 410/470/409 +f 397/457/405 396/456/403 411/471/410 +f 398/458/393 397/457/405 412/472/412 +f 399/459/394 398/458/393 413/473/414 +f 400/460/396 399/459/394 414/474/416 +f 401/461/398 400/460/396 415/475/418 +f 395/455/401 394/454/399 409/469/421 +f 415/475/418 414/474/416 429/489/424 +f 416/476/420 415/475/418 430/490/425 +f 410/470/409 409/469/421 424/484/428 +f 411/471/410 410/470/409 425/485/430 +f 412/472/412 411/471/410 426/486/432 +f 413/473/414 412/472/412 427/487/434 +f 414/474/416 413/473/414 428/488/436 +f 426/486/432 425/485/430 440/500/438 +f 427/487/434 426/486/432 441/501/439 +f 428/488/436 427/487/434 442/502/441 +f 429/489/424 428/488/436 443/503/443 +f 430/490/425 429/489/424 444/504/445 +f 431/491/427 430/490/425 445/505/447 +f 425/485/430 424/484/428 439/499/450 +f 445/505/447 444/504/445 459/519/453 +f 446/506/449 445/505/447 460/520/454 +f 440/500/438 439/499/450 454/514/457 +f 441/501/439 440/500/438 455/515/459 +f 442/502/441 441/501/439 456/516/461 +f 443/503/443 442/502/441 457/517/463 +f 444/504/445 443/503/443 458/518/465 +f 457/517/463 456/516/461 471/531/469 +f 458/518/465 457/517/463 472/532/470 +f 459/519/453 458/518/465 473/533/472 +f 460/520/454 459/519/453 474/534/474 +f 461/521/456 460/520/454 475/535/476 +f 455/515/459 454/514/457 469/529/479 +f 456/516/461 455/515/459 470/530/481 +f 476/536/478 475/535/476 481/541/3 +f 470/530/481 469/529/479 5/5/9 +f 471/531/469 470/530/481 6/6/16 +f 472/532/470 471/531/469 7/7/20 +f 473/533/472 472/532/470 8/8/24 +f 474/534/474 473/533/472 480/540/28 +f 475/535/476 474/534/474 9/9/1 +f 9/9/1 22/22/31 23/23/2 +f 481/541/3 23/23/2 24/24/7 +f 5/5/9 17/17/10 18/18/15 +f 6/6/16 18/18/15 19/19/19 +f 7/7/20 19/19/19 20/20/23 +f 8/8/24 20/20/23 21/21/27 +f 480/540/28 21/21/27 22/22/31 +f 21/21/27 36/36/47 37/37/33 +f 22/22/31 37/37/33 38/38/36 +f 23/23/2 38/38/36 39/39/38 +f 17/17/10 32/32/39 33/33/41 +f 18/18/15 33/33/41 34/34/43 +f 19/19/19 34/34/43 35/35/45 +f 20/20/23 35/35/45 36/36/47 +f 33/33/41 48/48/62 49/49/50 +f 34/34/43 49/49/50 50/50/52 +f 35/35/45 50/50/52 51/51/54 +f 36/36/47 51/51/54 52/52/56 +f 37/37/33 52/52/56 53/53/58 +f 38/38/36 53/53/58 54/54/60 +f 32/32/39 47/47/61 48/48/62 +f 52/52/56 68/83/77 69/84/65 +f 53/53/58 69/84/65 70/85/67 +f 47/47/61 63/78/68 64/79/70 +f 48/48/62 64/79/70 65/80/72 +f 49/49/50 65/80/72 66/81/74 +f 50/50/52 66/81/74 67/82/76 +f 51/51/54 67/82/76 68/83/77 +f 64/79/70 79/94/92 80/95/78 +f 65/80/72 80/95/78 81/96/81 +f 66/81/74 81/96/81 82/97/83 +f 67/82/76 82/97/83 83/98/85 +f 68/83/77 83/98/85 84/99/87 +f 69/84/65 84/99/87 85/100/89 +f 63/78/68 78/93/90 79/94/92 +f 83/98/85 98/113/107 99/114/93 +f 84/99/87 99/114/93 100/115/96 +f 78/93/90 93/108/97 94/109/99 +f 79/94/92 94/109/99 95/110/101 +f 80/95/78 95/110/101 96/111/103 +f 81/96/81 96/111/103 97/112/105 +f 82/97/83 97/112/105 98/113/107 +f 95/110/101 110/125/122 111/126/108 +f 96/111/103 111/126/108 112/127/111 +f 97/112/105 112/127/111 113/128/113 +f 98/113/107 113/128/113 114/129/115 +f 99/114/93 114/129/115 115/130/117 +f 93/108/97 108/123/118 109/124/120 +f 94/109/99 109/124/120 110/125/122 +f 114/129/115 129/158/137 130/160/123 +f 108/123/118 123/146/125 124/148/127 +f 109/124/120 124/148/127 125/150/129 +f 110/125/122 125/150/129 126/152/131 +f 111/126/108 126/152/131 127/154/133 +f 112/127/111 127/154/133 128/156/135 +f 113/128/113 128/156/135 129/158/137 +f 118/136/130 133/163/152 134/164/138 +f 119/138/132 134/164/138 135/165/141 +f 120/140/134 135/165/141 136/166/143 +f 121/142/136 136/166/143 137/167/145 +f 122/144/124 137/167/145 138/168/147 +f 116/132/126 131/161/148 132/162/150 +f 117/134/128 132/162/150 133/163/152 +f 137/167/145 152/182/167 153/183/153 +f 131/161/148 146/176/154 147/177/157 +f 132/162/150 147/177/157 148/178/159 +f 133/163/152 148/178/159 149/179/161 +f 134/164/138 149/179/161 150/180/163 +f 135/165/141 150/180/163 151/181/165 +f 136/166/143 151/181/165 152/182/167 +f 149/179/161 164/194/182 165/195/170 +f 150/180/163 165/195/170 166/196/172 +f 151/181/165 166/196/172 167/197/174 +f 152/182/167 167/197/174 168/198/176 +f 146/176/154 161/191/177 162/192/179 +f 147/177/157 162/192/179 163/193/181 +f 148/178/159 163/193/181 164/194/182 +f 161/191/177 176/206/183 177/207/187 +f 162/192/179 177/207/187 178/208/189 +f 163/193/181 178/208/189 179/209/191 +f 164/194/182 179/209/191 180/210/193 +f 165/195/170 180/210/193 181/211/195 +f 166/196/172 181/211/195 182/212/197 +f 167/197/174 182/212/197 183/213/185 +f 179/209/191 194/224/212 195/225/198 +f 180/210/193 195/225/198 196/226/201 +f 181/211/195 196/226/201 197/227/203 +f 182/212/197 197/227/203 198/228/205 +f 176/206/183 191/221/206 192/222/208 +f 177/207/187 192/222/208 193/223/210 +f 178/208/189 193/223/210 194/224/212 +f 191/221/206 206/236/227 207/237/215 +f 192/222/208 207/237/215 208/238/217 +f 193/223/210 208/238/217 209/239/219 +f 194/224/212 209/239/219 210/240/221 +f 195/225/198 210/240/221 211/241/223 +f 196/226/201 211/241/223 212/242/225 +f 197/227/203 212/242/225 213/243/213 +f 210/240/221 225/255/242 226/256/230 +f 211/241/223 226/256/230 227/257/232 +f 212/242/225 227/257/232 228/258/234 +f 206/236/227 221/251/235 222/252/237 +f 207/237/215 222/252/237 223/253/239 +f 208/238/217 223/253/239 224/254/241 +f 209/239/219 224/254/241 225/255/242 +f 222/252/237 237/267/257 238/268/245 +f 223/253/239 238/268/245 239/269/247 +f 224/254/241 239/269/247 240/270/249 +f 225/255/242 240/270/249 241/271/251 +f 226/256/230 241/271/251 242/272/253 +f 227/257/232 242/272/253 243/273/255 +f 221/251/235 236/266/256 237/267/257 +f 248/278/250 264/309/272 265/310/258 +f 249/279/252 265/310/258 266/311/261 +f 243/273/255 259/304/262 260/305/264 +f 244/274/243 260/305/264 261/306/266 +f 245/275/244 261/306/266 262/307/268 +f 246/276/246 262/307/268 263/308/270 +f 247/277/248 263/308/270 264/309/272 +f 261/306/266 276/321/287 277/322/275 +f 262/307/268 277/322/275 278/323/277 +f 263/308/270 278/323/277 279/324/279 +f 264/309/272 279/324/279 280/325/281 +f 265/310/258 280/325/281 281/326/283 +f 259/304/262 274/319/284 275/320/286 +f 260/305/264 275/320/286 276/321/287 +f 280/325/281 295/340/302 296/341/290 +f 274/319/284 289/334/291 290/335/293 +f 275/320/286 290/335/293 291/336/295 +f 276/321/287 291/336/295 292/337/297 +f 277/322/275 292/337/297 293/338/299 +f 278/323/277 293/338/299 294/339/301 +f 279/324/279 294/339/301 295/340/302 +f 291/336/295 306/351/317 307/352/303 +f 292/337/297 307/352/303 308/353/306 +f 293/338/299 308/353/306 309/354/308 +f 294/339/301 309/354/308 310/355/310 +f 295/340/302 310/355/310 311/356/312 +f 289/334/291 304/349/313 305/350/315 +f 290/335/293 305/350/315 306/351/317 +f 310/355/310 325/370/332 326/371/318 +f 304/349/313 319/364/320 320/365/322 +f 305/350/315 320/365/322 321/366/324 +f 306/351/317 321/366/324 322/367/326 +f 307/352/303 322/367/326 323/368/328 +f 308/353/306 323/368/328 324/369/330 +f 309/354/308 324/369/330 325/370/332 +f 322/367/326 337/382/347 338/383/335 +f 323/368/328 338/383/335 339/384/337 +f 324/369/330 339/384/337 340/385/339 +f 325/370/332 340/385/339 341/386/341 +f 319/364/320 334/379/342 335/380/344 +f 320/365/322 335/380/344 336/381/346 +f 321/366/324 336/381/346 337/382/347 +f 334/379/342 349/394/349 350/395/352 +f 335/380/344 350/395/352 351/396/354 +f 336/381/346 351/396/354 352/397/356 +f 337/382/347 352/397/356 353/398/358 +f 338/383/335 353/398/358 354/399/360 +f 339/384/337 354/399/360 355/400/362 +f 340/385/339 355/400/362 356/401/351 +f 352/397/356 367/423/377 368/425/363 +f 353/398/358 368/425/363 369/427/366 +f 354/399/360 369/427/366 370/429/368 +f 355/400/362 370/429/368 371/431/370 +f 349/394/349 364/417/371 365/419/373 +f 350/395/352 365/419/373 366/421/375 +f 351/396/354 366/421/375 367/423/377 +f 357/403/372 372/432/392 373/433/381 +f 358/405/374 373/433/381 374/434/383 +f 359/407/376 374/434/383 375/435/385 +f 360/409/364 375/435/385 376/436/387 +f 361/411/365 376/436/387 377/437/389 +f 362/413/367 377/437/389 378/438/391 +f 363/415/369 378/438/391 379/439/379 +f 376/436/387 391/451/407 392/452/395 +f 377/437/389 392/452/395 393/453/397 +f 378/438/391 393/453/397 394/454/399 +f 372/432/392 387/447/400 388/448/402 +f 373/433/381 388/448/402 389/449/404 +f 374/434/383 389/449/404 390/450/406 +f 375/435/385 390/450/406 391/451/407 +f 387/447/400 402/462/422 403/463/408 +f 388/448/402 403/463/408 404/464/411 +f 389/449/404 404/464/411 405/465/413 +f 390/450/406 405/465/413 406/466/415 +f 391/451/407 406/466/415 407/467/417 +f 392/452/395 407/467/417 408/468/419 +f 393/453/397 408/468/419 409/469/421 +f 406/466/415 421/481/437 422/482/423 +f 407/467/417 422/482/423 423/483/426 +f 408/468/419 423/483/426 424/484/428 +f 402/462/422 417/477/429 418/478/431 +f 403/463/408 418/478/431 419/479/433 +f 404/464/411 419/479/433 420/480/435 +f 405/465/413 420/480/435 421/481/437 +f 418/478/431 433/493/452 434/494/440 +f 419/479/433 434/494/440 435/495/442 +f 420/480/435 435/495/442 436/496/444 +f 421/481/437 436/496/444 437/497/446 +f 422/482/423 437/497/446 438/498/448 +f 423/483/426 438/498/448 439/499/450 +f 417/477/429 432/492/451 433/493/452 +f 437/497/446 452/512/467 453/513/455 +f 438/498/448 453/513/455 454/514/457 +f 432/492/451 447/507/458 448/508/460 +f 433/493/452 448/508/460 449/509/462 +f 434/494/440 449/509/462 450/510/464 +f 435/495/442 450/510/464 451/511/466 +f 436/496/444 451/511/466 452/512/467 +f 448/508/460 463/523/482 464/524/468 +f 449/509/462 464/524/468 465/525/471 +f 450/510/464 465/525/471 466/526/473 +f 451/511/466 466/526/473 467/527/475 +f 452/512/467 467/527/475 468/528/477 +f 453/513/455 468/528/477 469/529/479 +f 447/507/458 462/522/480 463/523/482 +f 467/527/475 3/3/32 4/4/4 +f 468/528/477 4/4/4 5/5/9 +f 462/522/480 477/537/11 478/538/17 +f 463/523/482 478/538/17 1/1/21 +f 464/524/468 1/1/21 2/2/25 +f 465/525/471 2/2/25 479/539/29 +f 466/526/473 479/539/29 3/3/32 diff --git a/data/games/garage/mods/testnodes/models/testnodes_ocorner.obj b/data/games/garage/mods/testnodes/models/testnodes_ocorner.obj new file mode 100644 index 0000000..231d705 --- /dev/null +++ b/data/games/garage/mods/testnodes/models/testnodes_ocorner.obj @@ -0,0 +1,23 @@ +# Blender v2.73 (sub 0) OBJ File: 'slope_test_ocorner_onetexture.blend' +# www.blender.org +o Cube_Cube.002 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -0.000000 1.000000 +vn -0.707100 0.707100 0.000000 +vn 0.000000 0.707100 -0.707100 +s off +f 3/1/1 2/2/1 4/3/1 5/4/1 +f 1/2/2 3/3/2 5/4/2 +f 1/1/3 2/3/3 3/4/3 +f 1/1/4 4/3/4 2/4/4 +f 1/2/5 5/3/5 4/4/5 diff --git a/data/games/garage/mods/testnodes/models/testnodes_pyramid.obj b/data/games/garage/mods/testnodes/models/testnodes_pyramid.obj new file mode 100644 index 0000000..b305af2 --- /dev/null +++ b/data/games/garage/mods/testnodes/models/testnodes_pyramid.obj @@ -0,0 +1,24 @@ +# Blender v2.73 (sub 0) OBJ File: 'slope_test_pyramid_onetexture.blend' +# www.blender.org +o Cube +v 0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.000000 0.500000 -0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.500000 1.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.894400 0.447200 -0.000000 +vn 0.000000 0.447200 -0.894400 +vn 0.894400 0.447200 0.000000 +vn -0.000000 0.447200 0.894400 +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 +f 3/4/2 5/5/2 4/3/2 +f 5/5/3 1/3/3 4/4/3 +f 1/4/4 5/5/4 2/3/4 +f 2/4/5 5/5/5 3/3/5 diff --git a/data/games/garage/mods/testnodes/nodeboxes.lua b/data/games/garage/mods/testnodes/nodeboxes.lua new file mode 100644 index 0000000..0025ee0 --- /dev/null +++ b/data/games/garage/mods/testnodes/nodeboxes.lua @@ -0,0 +1,242 @@ +local S = core.get_translator("testnodes") + +-- Nodebox examples and tests. + +-- An simple example nodebox with one centered box +core.register_node("testnodes:nodebox_fixed", { + description = S("Fixed Nodebox Test Node").."\n".. + S("Nodebox is always the same"), + tiles = {"testnodes_nodebox.png"}, + drawtype = "nodebox", + paramtype = "light", + node_box = { + type = "fixed", + fixed = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, + }, + + groups = {dig_immediate=3}, +}) + +-- 50% higher than a regular node +core.register_node("testnodes:nodebox_overhigh", { + description = S("+50% high Nodebox Test Node"), + tiles = {"testnodes_nodebox.png"}, + drawtype = "nodebox", + paramtype = "light", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 1, 0.5}, + }, + + groups = {dig_immediate=3}, +}) + +-- 95% higher than a regular node +core.register_node("testnodes:nodebox_overhigh2", { + description = S("+95% high Nodebox Test Node"), + tiles = {"testnodes_nodebox.png"}, + drawtype = "nodebox", + paramtype = "light", + node_box = { + type = "fixed", + -- Y max: more is possible, but glitchy + fixed = {-0.5, -0.5, -0.5, 0.5, 1.45, 0.5}, + }, + + groups = {dig_immediate=3}, +}) + +-- Height of nodebox changes with its param2 value +core.register_node("testnodes:nodebox_leveled", { + description = S("Leveled Nodebox Test Node").."\n".. + S("param2 = height (0..127)"), + tiles = {"testnodes_nodebox.png^[colorize:#0F0:32"}, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "leveled", + node_box = { + type = "leveled", + fixed = {-0.5, 0.0, -0.5, 0.5, -0.499, 0.5}, + }, + + groups = {dig_immediate=3}, +}) + + +local nodebox_wall = { + type = "connected", + fixed = {-0.125, -0.500, -0.125, 0.125, 0.500, 0.125}, + connect_front = {-0.125, -0.500, -0.500, 0.125, 0.400, -0.125}, + connect_back = {-0.125, -0.500, 0.125, 0.125, 0.400, 0.500}, + connect_left = {-0.500, -0.500, -0.125, -0.125, 0.400, 0.125}, + connect_right = {0.125, -0.500, -0.125, 0.500, 0.400, 0.125}, +} + +local nodebox_cable = { + type = "connected", + fixed = {-2/16, -2/16, -2/16, 2/16, 2/16, 2/16}, + connect_front = {-1/16, -1/16, -8/16, 1/16, 1/16, -2/16}, + connect_back = {-1/16, -1/16, 2/16, 1/16, 1/16, 8/16}, + connect_left = {-8/16, -1/16, -1/16, -2/16, 1/16, 1/16}, + connect_right = { 2/16, -1/16, -1/16, 8/16, 1/16, 1/16}, + connect_bottom = {-1/16, -8/16, -1/16, 1/16, -2/16, 1/16}, + connect_top = {-1/16, 2/16, -1/16, 1/16, 8/16, 1/16}, +} + +local nodebox_wall_thick = { + type = "connected", + fixed = {-0.25, -0.500, -0.25, 0.25, 0.500, 0.25}, + connect_front = {-0.25, -0.500, -0.500, 0.25, 0.400, -0.25}, + connect_back = {-0.25, -0.500, 0.25, 0.25, 0.400, 0.500}, + connect_left = {-0.500, -0.500, -0.25, -0.25, 0.400, 0.25}, + connect_right = {0.25, -0.500, -0.25, 0.500, 0.400, 0.25}, +} + +-- Wall-like nodebox that connects to 4 neighbors +core.register_node("testnodes:nodebox_connected", { + description = S("Connected Nodebox Test Node (4 Side Wall)").."\n".. + S("Connects to 4 neighbors sideways"), + tiles = {"testnodes_nodebox.png^[colorize:#F00:32"}, + groups = {connected_nodebox=1, dig_immediate=3}, + drawtype = "nodebox", + paramtype = "light", + connects_to = {"group:connected_nodebox"}, + connect_sides = {"front", "back", "left", "right"}, + node_box = nodebox_wall, +}) + +-- Cable-like nodebox that connects to 6 neighbors +core.register_node("testnodes:nodebox_connected_6side", { + description = S("Connected Nodebox Test Node (6 Side Cable)").."\n".. + S("Connects to 6 neighbors"), + tiles = {"testnodes_nodebox.png^[colorize:#F00:32"}, + groups = {connected_nodebox=1, dig_immediate=3}, + drawtype = "nodebox", + paramtype = "light", + connects_to = {"group:connected_nodebox"}, + connect_sides = {"front", "back", "left", "right", "top", "bottom"}, + node_box = nodebox_cable, +}) + +-- More walls +core.register_node("testnodes:nodebox_connected_facedir", { + description = S("Facedir Connected Nodebox Test Node (4 Side Wall)").."\n".. + S("Connects to neighbors").."\n".. + S("param2 = facedir rotation of textures (not of the nodebox!)"), + tiles = { + "testnodes_1.png", + "testnodes_2.png", + "testnodes_3.png", + "testnodes_4.png", + "testnodes_5.png", + "testnodes_6.png", + }, + groups = {connected_nodebox=1, dig_immediate=3}, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + connects_to = {"group:connected_nodebox"}, + connect_sides = {"front", "back", "left", "right"}, + node_box = nodebox_wall_thick, +}) + +core.register_node("testnodes:nodebox_connected_4dir", { + description = S("4Dir Connected Nodebox Test Node").."\n".. + S("Connects to neighbors").."\n".. + S("param2 = 4dir rotation of textures (not of the nodebox!)"), + tiles = { + "testnodes_1f.png", + "testnodes_2f.png", + "testnodes_3f.png", + "testnodes_4f.png", + "testnodes_5f.png", + "testnodes_6f.png", + }, + groups = {connected_nodebox=1, dig_immediate=3}, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "4dir", + connects_to = {"group:connected_nodebox"}, + connect_sides = {"front", "back", "left", "right"}, + node_box = nodebox_wall_thick, +}) + +-- Doesn't connect, but lets other nodes connect +core.register_node("testnodes:facedir_to_connect_to", { + description = S("Facedir Node that connected Nodeboxes connect to").."\n".. + S("Neighbors connect only to left (blue 4) and top (yellow 1) face").."\n".. + S("(Currently broken for param2 >= 4, see FIXME in nodedef.cpp)").."\n".. + S("param2 = facedir"), + tiles = { + "testnodes_1.png", + "testnodes_2.png", + "testnodes_3.png", + "testnodes_4.png", + "testnodes_5.png", + "testnodes_6.png", + }, + groups = {connected_nodebox=1, dig_immediate=3}, + drawtype = "normal", + paramtype2 = "facedir", + connect_sides = {"left", "top"}, +}) + +-- 3D sign and button: +-- These are example nodes for more realistic example uses +-- of wallmounted_rotate_vertical +core.register_node("testnodes:sign3d", { + description = S("Nodebox Sign, Nodebox Type \"fixed\""), + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + sunlight_propagates = true, + walkable = false, + tiles = { + "testnodes_sign3d.png", + }, + groups = { dig_immediate = 3 }, + node_box = { + type = "fixed", + fixed = {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125}, + }, +}) + +core.register_node("testnodes:sign3d_wallmounted", { + description = S("Nodebox Sign, Nodebox Type \"wallmounted\""), + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + sunlight_propagates = true, + walkable = false, + tiles = { + "testnodes_sign3d.png^[colorize:#ff0000:127", + }, + groups = { dig_immediate = 3 }, + node_box = { + type = "wallmounted", + wall_top = {-0.4375, 0.4375, -0.3125, 0.4375, 0.5, 0.3125}, + wall_bottom = {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125}, + wall_side = {-0.5, -0.3125, -0.4375, -0.4375, 0.3125, 0.4375}, + }, +}) + +core.register_node("testnodes:button", { + description = S("Button Nodebox Test Node"), + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + sunlight_propagates = true, + walkable = false, + tiles = { + "testnodes_nodebox.png", + }, + groups = { dig_immediate = 3 }, + node_box = { + type = "fixed", + fixed = { -4/16, -8/16, -2/16, 4/16, -6/16, 2/16 }, + }, +}) + diff --git a/data/games/garage/mods/testnodes/overlays.lua b/data/games/garage/mods/testnodes/overlays.lua new file mode 100644 index 0000000..7f7a1cd --- /dev/null +++ b/data/games/garage/mods/testnodes/overlays.lua @@ -0,0 +1,93 @@ +local S = core.get_translator("testnodes") + +core.register_node("testnodes:overlay", { + description = S("Texture Overlay Test Node") .. "\n" .. + S("Uncolorized"), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_color_all", { + description = S("Texture Overlay Test Node, Colorized") .. "\n" .. + S("param2 changes color"), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_color_overlay", { + description = S("Texture Overlay Test Node, Colorized Overlay") .. "\n" .. + S("param2 changes color of overlay"), + tiles = {{name = "testnodes_overlayable.png", color="white"}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_color_overlayed", { + description = S("Texture Overlay Test Node, Colorized Base") .. "\n" .. + S("param2 changes color of base texture"), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png", color="white"}}, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) + +local global_overlay_color = "#FF2000" +core.register_node("testnodes:overlay_global", { + description = S("Texture Overlay Test Node, Global Color") .. "\n" .. + S("Global color = @1", global_overlay_color), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + color = global_overlay_color, + + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_global_color_all", { + description = S("Texture Overlay Test Node, Global Color + Colorized") .. "\n" .. + S("Global color = @1", global_overlay_color) .. "\n" .. + S("param2 changes color"), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + color = global_overlay_color, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_global_color_overlay", { + description = S("Texture Overlay Test Node, Global Color + Colorized Overlay") .. "\n" .. + S("Global color = @1", global_overlay_color) .. "\n" .. + S("param2 changes color of overlay"), + tiles = {{name = "testnodes_overlayable.png", color=global_overlay_color}}, + overlay_tiles = {{name = "testnodes_overlay.png"}}, + color = global_overlay_color, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:overlay_global_color_overlayed", { + description = S("Texture Overlay Test Node, Global Color + Colorized Base") .. "\n" .. + S("Global color = @1", global_overlay_color) .. "\n" .. + S("param2 changes color of base texture"), + tiles = {{name = "testnodes_overlayable.png"}}, + overlay_tiles = {{name = "testnodes_overlay.png", color=global_overlay_color}}, + color = global_overlay_color, + paramtype2 = "color", + palette = "testnodes_palette_full.png", + + + groups = { dig_immediate = 2 }, +}) diff --git a/data/games/garage/mods/testnodes/param2.lua b/data/games/garage/mods/testnodes/param2.lua new file mode 100644 index 0000000..aec1fd4 --- /dev/null +++ b/data/games/garage/mods/testnodes/param2.lua @@ -0,0 +1,321 @@ +-- This file is for misc. param2 tests that aren't covered in drawtypes.lua already. + +local S = core.get_translator("testnodes") + +core.register_node("testnodes:facedir", { + description = S("Facedir Test Node").."\n".. + S("param2 = facedir rotation (0..23)"), + paramtype2 = "facedir", + tiles = { + "testnodes_1.png", + "testnodes_2.png", + "testnodes_3.png", + "testnodes_4.png", + "testnodes_5.png", + "testnodes_6.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:4dir", { + description = S("4dir Test Node").."\n".. + S("param2 = 4dir rotation (0..3)"), + paramtype2 = "4dir", + tiles = { + "testnodes_1f.png", + "testnodes_2f.png", + "testnodes_3f.png", + "testnodes_4f.png", + "testnodes_5f.png", + "testnodes_6f.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:facedir_nodebox", { + description = S("Facedir Nodebox Test Node").."\n".. + S("param2 = facedir rotation (0..23)"), + tiles = { + "testnodes_1.png", + "testnodes_2.png", + "testnodes_3.png", + "testnodes_4.png", + "testnodes_5.png", + "testnodes_6.png", + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.2, 0.2, 0.2}, + }, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:4dir_nodebox", { + description = S("4dir Nodebox Test Node").."\n".. + S("param2 = 4dir rotation (0..3)"), + tiles = { + "testnodes_1f.png", + "testnodes_2f.png", + "testnodes_3f.png", + "testnodes_4f.png", + "testnodes_5f.png", + "testnodes_6f.png", + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "4dir", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.2, 0.2, 0.2}, + }, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:4dir_nodebox_stair", { + description = S("4dir Nodebox Stair Test Node").."\n".. + S("param2 = 4dir rotation (0..3)"), + tiles = { + "testnodes_1f.png", + "testnodes_2f.png", + "testnodes_3f.png", + "testnodes_4f.png", + "testnodes_5f.png", + "testnodes_6f.png", + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "4dir", + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + {-0.5, 0, 0, 0.5, 0.5, 0.5}, + }, + }, + + groups = { dig_immediate = 3 }, +}) + + +core.register_node("testnodes:wallmounted", { + description = S("Wallmounted Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + paramtype2 = "wallmounted", + tiles = { + "testnodes_1w.png", + "testnodes_2w.png", + "testnodes_3w.png", + "testnodes_4w.png", + "testnodes_5w.png", + "testnodes_6w.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:wallmounted_rot", { + description = S("Wallmounted Rotatable Test Node"), + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + tiles = { + "testnodes_1w.png^[colorize:#FFFF00:40", + "testnodes_2w.png^[colorize:#FFFF00:40", + "testnodes_3w.png^[colorize:#FFFF00:40", + "testnodes_4w.png^[colorize:#FFFF00:40", + "testnodes_5w.png^[colorize:#FFFF00:40", + "testnodes_6w.png^[colorize:#FFFF00:40", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:wallmounted_nodebox", { + description = S("Wallmounted Nodebox Test Node").."\n".. + S("param2 = wallmounted rotation (0..7)"), + paramtype2 = "wallmounted", + paramtype = "light", + tiles = { + "testnodes_1w.png", + "testnodes_2w.png", + "testnodes_3w.png", + "testnodes_4w.png", + "testnodes_5w.png", + "testnodes_6w.png", + }, + drawtype = "nodebox", + node_box = { + type = "wallmounted", + wall_top = { -0.5, 0, -0.5, 0.5, 0.5, 0.5 }, + wall_bottom = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }, + wall_side = { -0.5, -0.5, -0.5, 0, 0.5, 0.5 }, + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:wallmounted_nodebox_rot", { + description = S("Wallmounted Rotatable Nodebox Test Node"), + paramtype2 = "wallmounted", + wallmounted_rotate_vertical = true, + paramtype = "light", + tiles = { + "testnodes_1w.png^[colorize:#FFFF00:40", + "testnodes_2w.png^[colorize:#FFFF00:40", + "testnodes_3w.png^[colorize:#FFFF00:40", + "testnodes_4w.png^[colorize:#FFFF00:40", + "testnodes_5w.png^[colorize:#FFFF00:40", + "testnodes_6w.png^[colorize:#FFFF00:40", + }, + drawtype = "nodebox", + node_box = { + type = "wallmounted", + wall_top = { -0.5, 0, -0.5, 0.5, 0.5, 0.5 }, + wall_bottom = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }, + wall_side = { -0.5, -0.5, -0.5, 0, 0.5, 0.5 }, + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:color", { + description = S("Color Test Node").."\n".. + S("param2 = color (0..255)"), + paramtype2 = "color", + palette = "testnodes_palette_full.png", + tiles = { + "testnodes_node.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:colorfacedir", { + description = S("Color Facedir Test Node").."\n".. + S("param2 = color + facedir rotation (0..23, 32..55, ...)"), + paramtype2 = "colorfacedir", + palette = "testnodes_palette_facedir.png", + tiles = { + "testnodes_1g.png", + "testnodes_2g.png", + "testnodes_3g.png", + "testnodes_4g.png", + "testnodes_5g.png", + "testnodes_6g.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:colorfacedir_nodebox", { + description = S("Color Facedir Nodebox Test Node").."\n".. + S("param2 = color + facedir rotation (0..23, 32..55, ...)"), + tiles = { + "testnodes_1g.png", + "testnodes_2g.png", + "testnodes_3g.png", + "testnodes_4g.png", + "testnodes_5g.png", + "testnodes_6g.png", + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "colorfacedir", + palette = "testnodes_palette_facedir.png", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.2, 0.2, 0.2}, + }, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:color4dir", { + description = S("Color 4dir Test Node").."\n".. + S("param2 = color + 4dir rotation (0..255)"), + paramtype2 = "color4dir", + palette = "testnodes_palette_4dir.png", + tiles = { + "testnodes_1fg.png", + "testnodes_2fg.png", + "testnodes_3fg.png", + "testnodes_4fg.png", + "testnodes_5fg.png", + "testnodes_6fg.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:color4dir_nodebox", { + description = S("Color 4dir Nodebox Test Node").."\n".. + S("param2 = color + 4dir rotation (0..255)"), + tiles = { + "testnodes_1fg.png", + "testnodes_2fg.png", + "testnodes_3fg.png", + "testnodes_4fg.png", + "testnodes_5fg.png", + "testnodes_6fg.png", + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "color4dir", + palette = "testnodes_palette_4dir.png", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.2, 0.2, 0.2}, + }, + + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:colorwallmounted", { + description = S("Color Wallmounted Test Node").."\n".. + S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"), + paramtype2 = "colorwallmounted", + paramtype = "light", + palette = "testnodes_palette_wallmounted.png", + tiles = { + "testnodes_1wg.png", + "testnodes_2wg.png", + "testnodes_3wg.png", + "testnodes_4wg.png", + "testnodes_5wg.png", + "testnodes_6wg.png", + }, + + groups = { dig_immediate = 3 }, +}) + +core.register_node("testnodes:colorwallmounted_nodebox", { + description = S("Color Wallmounted Nodebox Test Node").."\n".. + S("param2 = color + wallmounted rotation (0..7, 8..15, ...)"), + paramtype2 = "colorwallmounted", + paramtype = "light", + palette = "testnodes_palette_wallmounted.png", + tiles = { + "testnodes_1wg.png", + "testnodes_2wg.png", + "testnodes_3wg.png", + "testnodes_4wg.png", + "testnodes_5wg.png", + "testnodes_6wg.png", + }, + drawtype = "nodebox", + node_box = { + type = "wallmounted", + wall_top = { -0.5, 0, -0.5, 0.5, 0.5, 0.5 }, + wall_bottom = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }, + wall_side = { -0.5, -0.5, -0.5, 0, 0.5, 0.5 }, + }, + + groups = { dig_immediate = 3 }, +}) + diff --git a/data/games/garage/mods/testnodes/performance_test_nodes.lua b/data/games/garage/mods/testnodes/performance_test_nodes.lua new file mode 100644 index 0000000..fc22db3 --- /dev/null +++ b/data/games/garage/mods/testnodes/performance_test_nodes.lua @@ -0,0 +1,59 @@ +-- Performance test mesh nodes + +local S = core.get_translator("testnodes") + +-- Complex mesh +core.register_node("testnodes:performance_mesh_clip", { + description = S("Performance Test Node") .. "\n" .. S("Marble with 'clip' transparency"), + drawtype = "mesh", + mesh = "testnodes_marble_glass.obj", + tiles = {"testnodes_marble_glass.png"}, + paramtype = "light", + use_texture_alpha = "clip", + + groups = {dig_immediate=3}, +}) + +-- Complex mesh, alpha blending +core.register_node("testnodes:performance_mesh_blend", { + description = S("Performance Test Node") .. "\n" .. S("Marble with 'blend' transparency"), + drawtype = "mesh", + mesh = "testnodes_marble_glass.obj", + tiles = {"testnodes_marble_glass.png"}, + paramtype = "light", + use_texture_alpha = "blend", + + groups = {dig_immediate=3}, +}) + +-- Overlay +core.register_node("testnodes:performance_overlay_clip", { + description = S("Performance Test Node") .. "\n" .. S("Marble with overlay with 'clip' transparency") .. "\n" .. S("Palette for demonstration"), + drawtype = "mesh", + mesh = "testnodes_marble_metal.obj", + tiles = {"testnodes_marble_metal.png"}, + overlay_tiles = {{name = "testnodes_marble_metal_overlay.png", color = "white"}}, + paramtype = "light", + paramtype2 = "color", + palette = "testnodes_palette_metal.png", + color = "#705216"; + use_texture_alpha = "clip", + + groups = {dig_immediate=3}, +}) + +-- Overlay +core.register_node("testnodes:performance_overlay_blend", { + description = S("Performance Test Node") .. "\n" .. S("Marble with overlay with 'blend' transparency") .. "\n" .. S("Palette for demonstration"), + drawtype = "mesh", + mesh = "testnodes_marble_metal.obj", + tiles = {"testnodes_marble_metal.png"}, + overlay_tiles = {{name = "testnodes_marble_metal_overlay.png", color = "white"}}, + paramtype = "light", + paramtype2 = "color", + palette = "testnodes_palette_metal.png", + color = "#705216"; + use_texture_alpha = "blend", + + groups = {dig_immediate=3}, +}) diff --git a/data/games/garage/mods/testnodes/properties.lua b/data/games/garage/mods/testnodes/properties.lua new file mode 100644 index 0000000..29dc14a --- /dev/null +++ b/data/games/garage/mods/testnodes/properties.lua @@ -0,0 +1,685 @@ +-- Test Nodes: Node property tests + +local S = core.get_translator("testnodes") + +-- Is supposed to fall when it doesn't rest on solid ground +core.register_node("testnodes:falling", { + description = S("Falling Node").."\n".. + S("Falls down if no node below"), + tiles = { + "testnodes_node.png", + "testnodes_node.png", + "testnodes_node_falling.png", + }, + groups = { falling_node = 1, dig_immediate = 3 }, +}) + +core.register_node("testnodes:falling_facedir", { + description = S("Falling Facedir Node").."\n".. + S("Falls down if no node below").."\n".. + S("param2 = facedir rotation"), + tiles = { + "testnodes_node_falling_1.png", + "testnodes_node_falling_2.png", + "testnodes_node_falling_3.png", + "testnodes_node_falling_4.png", + "testnodes_node_falling_5.png", + "testnodes_node_falling_6.png", + }, + paramtype2 = "facedir", + groups = { falling_node = 1, dig_immediate = 3 }, +}) + +-- Same as falling node, but will stop falling on top of liquids +core.register_node("testnodes:falling_float", { + description = S("Falling+Floating Node").."\n".. + S("Falls down if no node below, floats on liquids (liquidtype ~= \"none\")"), + groups = { falling_node = 1, float = 1, dig_immediate = 3 }, + + + tiles = { + "testnodes_node.png", + "testnodes_node.png", + "testnodes_node_falling.png", + }, + color = "cyan", +}) + +-- This node attaches to the floor and drops as item +-- when the floor is gone. +core.register_node("testnodes:attached", { + description = S("Floor-Attached Node").."\n".. + S("Drops as item if no solid node below"), + tiles = { + "testnodes_attached_top.png", + "testnodes_attached_bottom.png", + "testnodes_attached_side.png", + }, + groups = { attached_node = 1, dig_immediate = 3 }, +}) +-- This node attaches to the side of a node and drops as item +-- when the node it attaches to is gone. +core.register_node("testnodes:attached_wallmounted", { + description = S("Wallmounted Attached Node").."\n".. + S("Attaches to solid node it was placed on; drops as item if neighbor node is gone").."\n".. + S("param2 = wallmounted rotation (0..7)"), + paramtype2 = "wallmounted", + tiles = { + "testnodes_attachedw_top.png", + "testnodes_attachedw_bottom.png", + "testnodes_attachedw_side.png", + }, + groups = { attached_node = 1, dig_immediate = 3 }, +}) + +-- This node attaches to the side of a node and drops as item +-- when the node it attaches to is gone. +-- Also adds vertical 90° rotation variants. +core.register_node("testnodes:attached_wallmounted_rot", { + description = S("Rotatable Wallmounted Attached Node").."\n".. + S("Attaches to solid node it was placed on; drops as item if neighbor node is gone").."\n".. + S("param2 = wallmounted rotation (0..7)").."\n".. + S("May be rotated by 90° if placed at floor or ceiling"), + paramtype2 = "wallmounted", + tiles = { + "testnodes_attachedwr_top.png", + "testnodes_attachedwr_bottom.png", + "testnodes_attachedwr_side.png", + }, + wallmounted_rotate_vertical = true, + groups = { attached_node = 1, dig_immediate = 3 }, +}) + +-- Wallmounted node that always attaches to the floor +core.register_node("testnodes:attached_wallmounted_floor", { + description = S("Floor-Attached Wallmounted Node").."\n".. + S("Drops as item if no solid node below (regardless of rotation)").."\n".. + S("param2 = wallmounted rotation (visual only) (0..7)"), + paramtype2 = "wallmounted", + tiles = { + "testnodes_attached_top.png", + "testnodes_attached_bottom.png", + "testnodes_attached_side.png", + }, + groups = { attached_node = 3, dig_immediate = 3 }, + color = "#FF8080", +}) + +-- Wallmounted node that always attaches to the floor. +-- Also adds 90° rotation variants. +core.register_node("testnodes:attached_wallmounted_floor_rot", { + description = S("Rotatable Floor-Attached Wallmounted Node").."\n".. + S("Drops as item if no solid node below (regardless of rotation)").."\n".. + S("param2 = wallmounted rotation (visual only) (0..7)").."\n".. + S("May be rotated by 90° if placed at floor or ceiling"), + paramtype2 = "wallmounted", + tiles = { + "testnodes_attachedfr_top.png", + "testnodes_attachedfr_bottom.png", + "testnodes_attachedfr_side.png", + }, + wallmounted_rotate_vertical = true, + groups = { attached_node = 3, dig_immediate = 3 }, +}) + +-- This node attaches to the ceiling and drops as item +-- when the ceiling is gone. +core.register_node("testnodes:attached_top", { + description = S("Ceiling-Attached Node").."\n".. + S("Drops as item if no solid node above"), + tiles = { + "testnodes_attached_bottom.png", + "testnodes_attached_top.png", + "testnodes_attached_side.png^[transformR180", + }, + groups = { attached_node = 4, dig_immediate = 3 }, +}) + +-- Same as wallmounted attached, but for facedir +core.register_node("testnodes:attached_facedir", { + description = S("Facedir Attached Node").."\n".. + S("Attaches to a neighboring solid node; drops as item if that node is gone").."\n".. + S("param2 = facedir rotation (0..23)"), + paramtype2 = "facedir", + tiles = { + "testnodes_attachedf_side.png^[transformR180", + "testnodes_attachedf_side.png", + "testnodes_attachedf_side.png^[transformR90", + "testnodes_attachedf_side.png^[transformR270", + "testnodes_attachedf_bottom.png", + "testnodes_attachedf_top.png", + }, + groups = { attached_node = 2, dig_immediate = 3 }, +}) + +-- Same as facedir attached, but for 4dir +core.register_node("testnodes:attached_4dir", { + description = S("4dir Attached Node").."\n".. + S("Attaches to the side of a solid node; drops as item if that node is gone").."\n".. + S("param2 = 4dir rotation (0..3)"), + paramtype2 = "4dir", + tiles = { + "testnodes_attached4_side.png^[transformR180", + "testnodes_attached4_side.png", + "testnodes_attached4_side.png^[transformR90", + "testnodes_attached4_side.png^[transformR270", + "testnodes_attached4_bottom.png", + "testnodes_attached4_top.png", + }, + groups = { attached_node = 2, dig_immediate = 3 }, +}) + +-- Jump disabled +core.register_node("testnodes:nojump", { + description = S("Non-jumping Node").."\n".. + S("You can't jump on it"), + groups = {disable_jump=1, dig_immediate=3}, + tiles = {"testnodes_nojump_top.png", "testnodes_nojump_side.png"}, +}) + +-- Jump disabled plant +core.register_node("testnodes:nojump_walkable", { + description = S("Non-jumping Plant Node").."\n".. + S("You can't jump while your feet are in it"), + drawtype = "plantlike", + groups = {disable_jump=1, dig_immediate=3}, + walkable = false, + tiles = {"testnodes_nojump_top.png"}, +}) + +local climbable_nodebox = { + type = "regular", +} + +-- Climbable up and down with jump and sneak keys +core.register_node("testnodes:climbable", { + description = S("Climbable Node").."\n".. + S("You can climb up and down"), + climbable = true, + walkable = false, + + + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + tiles = {"testnodes_climbable_top.png","testnodes_climbable_top.png","testnodes_climbable_side.png"}, + use_texture_alpha = "clip", + drawtype = "nodebox", + node_box = climbable_nodebox, + groups = {dig_immediate=3}, +}) + +-- Climbable only downwards with sneak key +core.register_node("testnodes:climbable_nojump", { + description = S("Downwards-climbable Node").."\n".. + S("You can climb only downwards"), + climbable = true, + walkable = false, + + groups = {disable_jump=1, dig_immediate=3}, + drawtype = "nodebox", + node_box = climbable_nodebox, + tiles = {"testnodes_climbable_nojump_top.png","testnodes_climbable_nojump_top.png","testnodes_climbable_nojump_side.png"}, + use_texture_alpha = "clip", + paramtype = "light", + sunlight_propagates = true, +}) + + +core.register_node("testnodes:climbable_nodescend", { + description = S("Upwards-climbable Node"), + climbable = true, + walkable = false, + + groups = {disable_descend=1, dig_immediate=3}, + drawtype = "nodebox", + node_box = climbable_nodebox, + tiles = {"testnodes_climbable_nodescend_top.png","testnodes_climbable_nodescend_top.png","testnodes_climbable_nodescend_side.png"}, + use_texture_alpha = "clip", + paramtype = "light", + sunlight_propagates = true, +}) + +core.register_node("testnodes:climbable_nodescend_nojump", { + description = S("Horizontal-only Climbable Node"), + climbable = true, + walkable = false, + + groups = {disable_jump=1, disable_descend=1, dig_immediate=3}, + drawtype = "nodebox", + node_box = climbable_nodebox, + tiles = {"testnodes_climbable_noclimb_top.png","testnodes_climbable_noclimb_top.png","testnodes_climbable_noclimb_side.png"}, + use_texture_alpha = "clip", + paramtype = "light", + sunlight_propagates = true, +}) + +-- A liquid in which you can't rise +core.register_node("testnodes:liquid_nojump", { + description = S("Non-jumping Liquid Source Node").."\n".. + S("Swimmable liquid, but you can't swim upwards"), + liquidtype = "source", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_nojump", + liquid_alternative_source = "testnodes:liquid_nojump", + liquid_renewable = false, + groups = {disable_jump=1, dig_immediate=3}, + walkable = false, + + drawtype = "liquid", + tiles = {"testnodes_liquidsource.png^[colorize:#FF0000:127"}, + special_tiles = { + {name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = false}, + {name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + pointable = false, + liquids_pointable = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 0, b = 200}, +}) + +-- A liquid in which you can't rise (flowing variant) +core.register_node("testnodes:liquidflowing_nojump", { + description = S("Non-jumping Flowing Liquid Node").."\n".. + S("Swimmable liquid, but you can't swim upwards"), + liquidtype = "flowing", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_nojump", + liquid_alternative_source = "testnodes:liquid_nojump", + liquid_renewable = false, + groups = {disable_jump=1, dig_immediate=3}, + walkable = false, + + + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing.png^[colorize:#FF0000:127"}, + special_tiles = { + {name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false}, + {name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + pointable = false, + liquids_pointable = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 0, b = 200}, +}) + +-- A liquid which doesn't have liquid movement physics (source variant) +core.register_node("testnodes:liquid_noswim", { + description = S("No-swim Liquid Source Node").."\n".. + S("Liquid node, but swimming is disabled"), + liquidtype = "source", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_noswim", + liquid_alternative_source = "testnodes:liquid_noswim", + liquid_renewable = false, + liquid_move_physics = false, + groups = {dig_immediate=3}, + walkable = false, + + drawtype = "liquid", + tiles = {"testnodes_liquidsource.png^[colorize:#FF00FF:127"}, + special_tiles = { + {name = "testnodes_liquidsource.png^[colorize:#FF00FF:127", backface_culling = false}, + {name = "testnodes_liquidsource.png^[colorize:#FF00FF:127", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + pointable = false, + liquids_pointable = true, + buildable_to = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 200, b = 200}, +}) + +-- A liquid which doen't have liquid movement physics (flowing variant) +core.register_node("testnodes:liquidflowing_noswim", { + description = S("No-swim Flowing Liquid Node").."\n".. + S("Liquid node, but swimming is disabled"), + liquidtype = "flowing", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_noswim", + liquid_alternative_source = "testnodes:liquid_noswim", + liquid_renewable = false, + liquid_move_physics = false, + groups = {dig_immediate=3}, + walkable = false, + + + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing.png^[colorize:#FF00FF:127"}, + special_tiles = { + {name = "testnodes_liquidflowing.png^[colorize:#FF00FF:127", backface_culling = false}, + {name = "testnodes_liquidflowing.png^[colorize:#FF00FF:127", backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + pointable = false, + liquids_pointable = true, + buildable_to = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 200, b = 200}, +}) + +-- A liquid in which you can't actively descend. +-- Note: You'll still descend slowly by doing nothing. +core.register_node("testnodes:liquid_nodescend", { + description = S("No-descending Liquid Source Node"), + liquidtype = "source", + liquid_range = 0, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_nodescend", + liquid_alternative_source = "testnodes:liquid_nodescend", + liquid_renewable = false, + groups = {disable_descend=1, dig_immediate=3}, + walkable = false, + + drawtype = "liquid", + tiles = {"testnodes_liquidsource.png^[colorize:#FFFF00:127"}, + special_tiles = { + {name = "testnodes_liquidsource.png^[colorize:#FFFF00:127", backface_culling = false}, + {name = "testnodes_liquidsource.png^[colorize:#FFFF00:127", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + pointable = false, + liquids_pointable = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 255, b = 200}, +}) + +-- A liquid in which you can't actively descend (flowing variant) +core.register_node("testnodes:liquidflowing_nodescend", { + description = S("No-descending Flowing Liquid Node"), + liquidtype = "flowing", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_nodescend", + liquid_alternative_source = "testnodes:liquid_nodescend", + liquid_renewable = false, + groups = {disable_descend=1, dig_immediate=3}, + walkable = false, + + + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing.png^[colorize:#FFFF00:127"}, + special_tiles = { + {name = "testnodes_liquidflowing.png^[colorize:#FFFF00:127", backface_culling = false}, + {name = "testnodes_liquidflowing.png^[colorize:#FFFF00:127", backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + pointable = false, + liquids_pointable = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 255, b = 200}, +}) + +-- Nodes that modify fall damage (various damage modifiers) +for i=-100, 100, 25 do + if i ~= 0 then + local subname, descnum + if i < 0 then + subname = "NEG"..string.format("%03d", math.abs(i)) + descnum = tostring(i) + else + subname = string.format("%03d", i) + descnum = S("+@1", i) + end + local tex, color, desc + if i > 0 then + local val = math.floor((i/100)*255) + tex = "testnodes_fall_damage_plus.png" + color = { b=0, g=255-val, r=255, a=255 } + desc = S("Fall Damage Node (+@1%)", i) + else + tex = "testnodes_fall_damage_minus.png" + if i == -100 then + color = { r=0, b=0, g=255, a=255 } + else + local val = math.floor((math.abs(i)/100)*255) + color = { r=0, b=255, g=255-val, a=255 } + end + desc = S("Fall Damage Node (-@1%)", math.abs(i)) + end + core.register_node("testnodes:damage"..subname, { + description = desc, + groups = {fall_damage_add_percent=i, dig_immediate=3}, + + + tiles = { tex }, + is_ground_content = false, + color = color, + }) + end +end + +-- Bouncy nodes (various bounce levels) +local MAX_BOUNCE_JUMPY = 180 +local MAX_BOUNCE_NONJUMPY = 140 +for i=-MAX_BOUNCE_NONJUMPY, MAX_BOUNCE_JUMPY, 20 do + if i ~= 0 then + local desc + local val = math.floor((math.abs(i) - 20) / 200 * 255) + local val2 = math.max(0, 255 - val) + local num = string.format("%03d", math.abs(i)) + if i > 0 then + desc = S("Bouncy Node (@1%), jumpy", i).."\n".. + S("Sneaking/jumping affects bounce") + color = { r=val2, g=val2, b=255, a=255 } + else + desc = S("Bouncy Node (@1%), non-jumpy", math.abs(i)).."\n".. + S("Sneaking/jumping does not affect bounce") + color = { r=val2, g=255, b=val2, a=255 } + num = "NEG"..num + end + core.register_node("testnodes:bouncy"..num, { + description = desc, + groups = {bouncy=i, dig_immediate=3}, + + + color = color, + tiles ={"testnodes_bouncy.png"}, + is_ground_content = false, + }) + end +end + +-- Slippery nodes (various slippery levels) +for i=1, 5 do + core.register_node("testnodes:slippery"..i, { + description = S("Slippery Node (@1)", i), + tiles ={"testnodes_slippery.png"}, + is_ground_content = false, + groups = {slippery=i, dig_immediate=3}, + color = { r=0, g=255, b=math.floor((i/5)*255), a=255 }, + }) +end + +-- Move resistance nodes (various resistance levels) +for r=0, 7 do + if r > 0 then + core.register_node("testnodes:move_resistance"..r, { + description = S("Move-resistant Node (@1)", r).."\n".. + S("Reduces movement speed"), + walkable = false, + move_resistance = r, + + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_move_resistance.png" }, + is_ground_content = false, + groups = { dig_immediate = 3 }, + color = { b = 0, g = 255, r = math.floor((r/7)*255), a = 255 }, + }) + end + + local mdesc, mcolor + if r == 0 then + mdesc = S("Liquidlike Movement Node").."\n".. + S("Swimmable (no move resistance)") + mcolor = { b = 255, g = 255, r = 128 } + else + mdesc = S("Move-resistant Node (@1), liquidlike", r).."\n".. + S("Reduces movement speed; swimmable") + mcolor = { b = 255, g = 0, r = math.floor((r/7)*255), a = 255 } + end + + + core.register_node("testnodes:move_resistance_liquidlike"..r, { + description = mdesc, + walkable = false, + move_resistance = r, + liquid_move_physics = true, + + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_move_resistance.png" }, + is_ground_content = false, + groups = { dig_immediate = 3 }, + color = mcolor, + }) +end + +core.register_node("testnodes:climbable_move_resistance_4", { + description = S("Climbable Move-resistant Node (4)").."\n".. + S("You can climb up and down; reduced movement speed"), + walkable = false, + climbable = true, + move_resistance = 4, + + drawtype = "nodebox", + paramtype = "light", + sunlight_propagates = true, + tiles = {"testnodes_climbable_top.png","testnodes_climbable_top.png","testnodes_climbable_resistance_side.png"}, + use_texture_alpha = "clip", + is_ground_content = false, + groups = { dig_immediate = 3 }, +}) + +-- By placing something on the node, the node itself will be replaced +core.register_node("testnodes:buildable_to", { + description = S("\"buildable_to\" Node").."\n".. + S("Placing a node on it will replace it"), + buildable_to = true, + tiles = {"testnodes_buildable_to.png"}, + is_ground_content = false, + groups = {dig_immediate=3}, +}) + +-- Nodes that deal damage to players that are inside them. +-- Negative damage nodes should heal. +for d=-3,3 do + if d ~= 0 then + local sub, tile, desc + if d > 0 then + sub = tostring(d) + tile = "testnodes_damage.png" + desc = S("Damage Node (@1 damage per second)", d) + else + sub = "m" .. tostring(math.abs(d)) + tile = "testnodes_damage_neg.png" + desc = S("Healing Node (@1 HP per second)", math.abs(d)) + end + if math.abs(d) == 2 then + tile = tile .. "^[colorize:#000000:70" + elseif math.abs(d) == 3 then + tile = tile .. "^[colorize:#000000:140" + end + core.register_node("testnodes:damage_"..sub, { + description = desc, + damage_per_second = d, + + + walkable = false, + is_ground_content = false, + drawtype = "allfaces", + paramtype = "light", + sunlight_propagates = true, + tiles = { tile }, + groups = {dig_immediate=3}, + }) + end +end + +-- Causes drowning damage +core.register_node("testnodes:drowning_1", { + description = S("Drowning Node (@1 damage)", 1).."\n".. + S("You'll drown inside it"), + drowning = 1, + + + walkable = false, + is_ground_content = false, + drawtype = "allfaces", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_drowning.png" }, + groups = {dig_immediate=3}, +}) + +-- post_effect_color_shaded + +core.register_node("testnodes:post_effect_color_shaded_false", { + description = S("\"post_effect_color_shaded = false\" Node"), + + drawtype = "allfaces", + tiles = {"testnodes_post_effect_color_shaded_false.png"}, + use_texture_alpha = "blend", + paramtype = "light", + sunlight_propagates = true, + post_effect_color = {a = 128, r = 255, g = 255, b = 255}, + post_effect_color_shaded = false, + + walkable = false, + is_ground_content = false, + groups = {dig_immediate=3}, +}) + +core.register_node("testnodes:post_effect_color_shaded_true", { + description = S("\"post_effect_color_shaded = true\" Node"), + + drawtype = "allfaces", + tiles = {"testnodes_post_effect_color_shaded_true.png"}, + use_texture_alpha = "blend", + paramtype = "light", + sunlight_propagates = true, + post_effect_color = {a = 128, r = 255, g = 255, b = 255}, + post_effect_color_shaded = true, + + walkable = false, + is_ground_content = false, + groups = {dig_immediate=3}, +}) + +-- Pointability + +-- Register wrapper for compactness +local function register_pointable_test_node(name, description, pointable) + local texture = "testnodes_"..name..".png" + core.register_node("testnodes:"..name, { + description = S(description), + tiles = {texture}, + drawtype = "glasslike_framed", + paramtype = "light", + walkable = false, + pointable = pointable, + groups = {dig_immediate=3, [name.."_test"]=1}, + }) +end + +register_pointable_test_node("pointable", "Pointable Node", true) +register_pointable_test_node("not_pointable", "Not Pointable Node", false) +register_pointable_test_node("blocking_pointable", "Blocking Pointable Node", "blocking") diff --git a/data/games/garage/mods/testnodes/textures.lua b/data/games/garage/mods/testnodes/textures.lua new file mode 100644 index 0000000..0d41947 --- /dev/null +++ b/data/games/garage/mods/testnodes/textures.lua @@ -0,0 +1,369 @@ +-- Node texture tests + +local S = core.get_translator("testnodes") + +core.register_node("testnodes:6sides", { + description = S("Six Textures Test Node").."\n".. + S("Has 1 texture per face"), + tiles = { + "testnodes_normal1.png", + "testnodes_normal2.png", + "testnodes_normal3.png", + "testnodes_normal4.png", + "testnodes_normal5.png", + "testnodes_normal6.png", + }, + + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:anim", { + description = S("Animated Test Node").."\n".. + S("Tiles animate from A to D in 4s cycle"), + tiles = { + { name = "testnodes_anim.png", + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 4.0, + }, }, + }, + + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:fill_positioning", { + description = S("Fill Modifier Test Node") .. "\n" .. + S("The node should have the same look as " .. + "testnodes:fill_positioning_reference."), + drawtype = "glasslike", + paramtype = "light", + tiles = {"[fill:16x16:#ffffff^[fill:6x6:1,1:#00ffdc" .. + "^[fill:6x6:1,9:#00ffdc^[fill:6x6:9,1:#00ffdc^[fill:6x6:9,9:#00ffdc"}, + groups = {dig_immediate = 3}, +}) + +core.register_node("testnodes:fill_positioning_reference", { + description = S("Fill Modifier Test Node Reference"), + drawtype = "glasslike", + paramtype = "light", + tiles = {"testnodes_fill_positioning_reference.png"}, + groups = {dig_immediate = 3}, +}) + +core.register_node("testnodes:modifier_mask", { + description = S("[mask Modifier Test Node"), + tiles = {"testnodes_128x128_rgb.png^[mask:testnodes_mask_WRGBKW.png"}, + groups = {dig_immediate = 3}, +}) + +-- Node texture transparency test + +local alphas = { 64, 128, 191 } + +for a=1,#alphas do + local alpha = alphas[a] + + -- Transparency taken from texture + core.register_node("testnodes:alpha_texture_"..alpha, { + description = S("Texture Alpha Test Node (@1)", alpha).."\n".. + S("Semi-transparent"), + drawtype = "glasslike", + paramtype = "light", + tiles = { + "testnodes_alpha"..alpha..".png", + }, + use_texture_alpha = "blend", + + groups = { dig_immediate = 3 }, + }) + + -- Transparency set via texture modifier + core.register_node("testnodes:alpha_"..alpha, { + description = S("Alpha Test Node (@1)", alpha).."\n".. + S("Semi-transparent"), + drawtype = "glasslike", + paramtype = "light", + tiles = { + "testnodes_alpha.png^[opacity:" .. alpha, + }, + use_texture_alpha = "blend", + + groups = { dig_immediate = 3 }, + }) +end + +core.register_node("testnodes:alpha_compositing", { + description = S("Texture Overlay Test Node") .. "\n" .. + S("A regular grid should be visible where each cell contains two " .. + "texels with the same color.") .. "\n" .. + S("Texture overlay is gamma-incorrect, " .. + "and in general it does not do alpha compositing, " .. + "both for backwards compatibility."), + drawtype = "glasslike", + paramtype = "light", + tiles = {"testnodes_alpha_compositing_bottom.png^" .. + "testnodes_alpha_compositing_top.png"}, + use_texture_alpha = "blend", + groups = {dig_immediate = 3}, +}) + +-- Generate PNG textures + +local function mandelbrot(w, h, iterations) + local r = {} + for y=0, h-1 do + for x=0, w-1 do + local re = (x - w/2) * 4/w + local im = (y - h/2) * 4/h + -- zoom in on a nice view + re = re / 128 - 0.23 + im = im / 128 - 0.82 + + local px, py = 0, 0 + local i = 0 + while px*px + py*py <= 4 and i < iterations do + px, py = px*px - py*py + re, 2 * px * py + im + i = i + 1 + end + r[w*y+x+1] = i / iterations + end + end + return r +end + +local function gen_checkers(w, h, tile) + local r = {} + for y=0, h-1 do + for x=0, w-1 do + local hori = math.floor(x / tile) % 2 == 0 + local vert = math.floor(y / tile) % 2 == 0 + r[w*y+x+1] = hori ~= vert and 1 or 0 + end + end + return r +end + +-- The engine should perform color reduction of the generated PNG in certain +-- cases, so we have this helper to check the result +local function encode_and_check(w, h, ctype, data) + local ret = core.encode_png(w, h, data) + assert(ret:sub(1, 8) == "\137PNG\r\n\026\n", "missing png signature") + assert(ret:sub(9, 16) == "\000\000\000\rIHDR", "didn't find ihdr chunk") + local ctype_actual = ret:byte(26) -- Color Type (1 byte) + ctype = ({rgba=6, rgb=2, gray=0})[ctype] + assert(ctype_actual == ctype, "png should have color type " .. ctype .. + " but actually has " .. ctype_actual) + return ret +end + +local fractal = mandelbrot(512, 512, 128) +local frac_emb = mandelbrot(64, 64, 64) +local checker = gen_checkers(512, 512, 32) + +local floor = math.floor +local abs = math.abs +local data_emb = {} +local data_mb = {} +local data_ck = {} +for i=1, #frac_emb do + data_emb[i] = { + r = floor(abs(frac_emb[i] * 2 - 1) * 255), + g = floor(abs(1 - frac_emb[i]) * 255), + b = floor(frac_emb[i] * 255), + a = frac_emb[i] < 0.95 and 255 or 0, + } +end +for i=1, #fractal do + data_mb[i] = { + r = floor(fractal[i] * 255), + g = floor(abs(fractal[i] * 2 - 1) * 255), + b = floor(abs(1 - fractal[i]) * 255), + a = 255, + } + data_ck[i] = checker[i] > 0 and "#888" or "#000" +end + +fractal = nil +frac_emb = nil +checker = nil + +local textures_path = core.get_worldpath() .. "/" +core.safe_file_write( + textures_path .. "testnodes1.png", + encode_and_check(512, 512, "rgb", data_mb) +) +local png_ck = encode_and_check(512, 512, "gray", data_ck) +core.dynamic_add_media({ + filename = "testnodes_generated_mb.png", + filepath = textures_path .. "testnodes1.png" +}) +core.dynamic_add_media({ + filename = "testnodes_generated_ck.png", + filedata = png_ck, +}) + +core.register_node("testnodes:generated_png_mb", { + description = S("Generated Mandelbrot PNG Test Node"), + tiles = { "testnodes_generated_mb.png" }, + + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:generated_png_ck", { + description = S("Generated Checker PNG Test Node"), + tiles = { "testnodes_generated_ck.png" }, + + groups = { dig_immediate = 2 }, +}) + +local png_emb = "[png:" .. core.encode_base64( + encode_and_check(64, 64, "rgba", data_emb)) + +core.register_node("testnodes:generated_png_emb", { + description = S("Generated In-Band Mandelbrot PNG Test Node"), + tiles = { png_emb }, + + drawtype = "allfaces", -- required because of transparent pixels + use_texture_alpha = "clip", + paramtype = "light", + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:generated_png_src_emb", { + description = S("Generated In-Band Source Blit Mandelbrot PNG Test Node"), + tiles = { png_emb .. "^testnodes_damage_neg.png" }, + + drawtype = "allfaces", -- required because of transparent pixels + use_texture_alpha = "clip", + paramtype = "light", + groups = { dig_immediate = 2 }, +}) +core.register_node("testnodes:generated_png_dst_emb", { + description = S("Generated In-Band Dest Blit Mandelbrot PNG Test Node"), + tiles = { "testnodes_generated_ck.png^" .. png_emb }, + + groups = { dig_immediate = 2 }, +}) + +png_ck = nil +png_emb = nil +data_emb = nil +data_mb = nil +data_ck = nil + +--[[ + +The following nodes can be used to demonstrate the TGA format support. + +Luanti supports TGA types 1, 2, 3 & 10. While adding the support for +TGA type 9 (RLE-compressed, color-mapped) is easy, it is not advisable +to do so, as it is not backwards compatible with any engine version pre-5.5; +content creators should therefore either use TGA type 1 or 10, or PNG. + +TODO: Types 1, 2 & 10 should have two test nodes each (i.e. bottom-top +and top-bottom) for 16bpp (A1R5G5B5), 24bpp (B8G8R8), 32bpp (B8G8R8A8) +colors. + +Note: Luanti requires the optional TGA footer for a texture to load. +If a TGA image does not load in Luanti, append eight (8) null bytes, +then the string “TRUEVISION-XFILE.”, then another null byte. + +]]-- + +core.register_node("testnodes:tga_type1_24bpp_bt", { + description = S("TGA Type 1 (color-mapped RGB) 24bpp bottom-top Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type1_24bpp_bt.tga" }, + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type1_24bpp_tb", { + description = S("TGA Type 1 (color-mapped RGB) 24bpp top-bottom Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type1_24bpp_tb.tga" }, + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type2_16bpp_bt", { + description = S("TGA Type 2 (uncompressed RGB) 16bpp bottom-top Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type2_16bpp_bt.tga" }, + use_texture_alpha = "clip", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type2_16bpp_tb", { + description = S("TGA Type 2 (uncompressed RGB) 16bpp top-bottom Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type2_16bpp_tb.tga" }, + use_texture_alpha = "clip", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type2_32bpp_bt", { + description = S("TGA Type 2 (uncompressed RGB) 32bpp bottom-top Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type2_32bpp_bt.tga" }, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type2_32bpp_tb", { + description = S("TGA Type 2 (uncompressed RGB) 32bpp top-bottom Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type2_32bpp_tb.tga" }, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type3_16bpp_bt", { + description = S("TGA Type 3 (uncompressed grayscale) 16bpp bottom-top Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type3_16bpp_bt.tga" }, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type3_16bpp_tb", { + description = S("TGA Type 3 (uncompressed grayscale) 16bpp top-bottom Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type3_16bpp_tb.tga" }, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type10_32bpp_bt", { + description = S("TGA Type 10 (RLE-compressed RGB) 32bpp bottom-top Test Node"), + tiles = { "testnodes_tga_type10_32bpp_bt.tga" }, + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) + +core.register_node("testnodes:tga_type10_32bpp_tb", { + description = S("TGA Type 10 (RLE-compressed RGB) 32bpp top-bottom Test Node"), + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_tga_type10_32bpp_tb.tga" }, + use_texture_alpha = "blend", + groups = { dig_immediate = 2 }, +}) diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1.png b/data/games/garage/mods/testnodes/textures/testnodes_1.png new file mode 100644 index 0000000..6730997 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_128x128_rgb.png b/data/games/garage/mods/testnodes/textures/testnodes_128x128_rgb.png new file mode 100644 index 0000000..060d8e6 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_128x128_rgb.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1f.png b/data/games/garage/mods/testnodes/textures/testnodes_1f.png new file mode 100644 index 0000000..f9a1b69 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1fg.png b/data/games/garage/mods/testnodes/textures/testnodes_1fg.png new file mode 100644 index 0000000..5d0beb0 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1g.png b/data/games/garage/mods/testnodes/textures/testnodes_1g.png new file mode 100644 index 0000000..529298e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1w.png b/data/games/garage/mods/testnodes/textures/testnodes_1w.png new file mode 100644 index 0000000..d24e571 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_1wg.png b/data/games/garage/mods/testnodes/textures/testnodes_1wg.png new file mode 100644 index 0000000..b2eba0e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_1wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2.png b/data/games/garage/mods/testnodes/textures/testnodes_2.png new file mode 100644 index 0000000..6c87c86 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2f.png b/data/games/garage/mods/testnodes/textures/testnodes_2f.png new file mode 100644 index 0000000..a26f840 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2fg.png b/data/games/garage/mods/testnodes/textures/testnodes_2fg.png new file mode 100644 index 0000000..3386330 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2g.png b/data/games/garage/mods/testnodes/textures/testnodes_2g.png new file mode 100644 index 0000000..cb9060f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2w.png b/data/games/garage/mods/testnodes/textures/testnodes_2w.png new file mode 100644 index 0000000..b56874e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_2wg.png b/data/games/garage/mods/testnodes/textures/testnodes_2wg.png new file mode 100644 index 0000000..108dc87 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_2wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3.png b/data/games/garage/mods/testnodes/textures/testnodes_3.png new file mode 100644 index 0000000..05b4562 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3f.png b/data/games/garage/mods/testnodes/textures/testnodes_3f.png new file mode 100644 index 0000000..d007ee4 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3fg.png b/data/games/garage/mods/testnodes/textures/testnodes_3fg.png new file mode 100644 index 0000000..01e4276 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3g.png b/data/games/garage/mods/testnodes/textures/testnodes_3g.png new file mode 100644 index 0000000..5c84f58 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3w.png b/data/games/garage/mods/testnodes/textures/testnodes_3w.png new file mode 100644 index 0000000..8b435cf Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_3wg.png b/data/games/garage/mods/testnodes/textures/testnodes_3wg.png new file mode 100644 index 0000000..9ee9006 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_3wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4.png b/data/games/garage/mods/testnodes/textures/testnodes_4.png new file mode 100644 index 0000000..15e6ffe Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4f.png b/data/games/garage/mods/testnodes/textures/testnodes_4f.png new file mode 100644 index 0000000..7e444fd Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4fg.png b/data/games/garage/mods/testnodes/textures/testnodes_4fg.png new file mode 100644 index 0000000..6068139 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4g.png b/data/games/garage/mods/testnodes/textures/testnodes_4g.png new file mode 100644 index 0000000..8f144fa Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4w.png b/data/games/garage/mods/testnodes/textures/testnodes_4w.png new file mode 100644 index 0000000..214e0df Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_4wg.png b/data/games/garage/mods/testnodes/textures/testnodes_4wg.png new file mode 100644 index 0000000..888b3d4 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_4wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5.png b/data/games/garage/mods/testnodes/textures/testnodes_5.png new file mode 100644 index 0000000..1ef1c72 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5f.png b/data/games/garage/mods/testnodes/textures/testnodes_5f.png new file mode 100644 index 0000000..a4162db Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5fg.png b/data/games/garage/mods/testnodes/textures/testnodes_5fg.png new file mode 100644 index 0000000..f21acbb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5g.png b/data/games/garage/mods/testnodes/textures/testnodes_5g.png new file mode 100644 index 0000000..30da479 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5w.png b/data/games/garage/mods/testnodes/textures/testnodes_5w.png new file mode 100644 index 0000000..b4cb424 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_5wg.png b/data/games/garage/mods/testnodes/textures/testnodes_5wg.png new file mode 100644 index 0000000..fac9db2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_5wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6.png b/data/games/garage/mods/testnodes/textures/testnodes_6.png new file mode 100644 index 0000000..805813e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6f.png b/data/games/garage/mods/testnodes/textures/testnodes_6f.png new file mode 100644 index 0000000..be3f695 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6f.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6fg.png b/data/games/garage/mods/testnodes/textures/testnodes_6fg.png new file mode 100644 index 0000000..75df824 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6fg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6g.png b/data/games/garage/mods/testnodes/textures/testnodes_6g.png new file mode 100644 index 0000000..a88f4c9 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6g.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6w.png b/data/games/garage/mods/testnodes/textures/testnodes_6w.png new file mode 100644 index 0000000..e6bbf97 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6w.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_6wg.png b/data/games/garage/mods/testnodes/textures/testnodes_6wg.png new file mode 100644 index 0000000..29ca933 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_6wg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_airlike.png b/data/games/garage/mods/testnodes/textures/testnodes_airlike.png new file mode 100644 index 0000000..5a5664a Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_airlike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_allfaces.png b/data/games/garage/mods/testnodes/textures/testnodes_allfaces.png new file mode 100644 index 0000000..c0a7dc5 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_allfaces.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_allfaces_optional.png b/data/games/garage/mods/testnodes/textures/testnodes_allfaces_optional.png new file mode 100644 index 0000000..1f6a173 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_allfaces_optional.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha.png new file mode 100644 index 0000000..157fa73 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha128.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha128.png new file mode 100644 index 0000000..16babf6 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha128.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha191.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha191.png new file mode 100644 index 0000000..f165d28 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha191.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha64.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha64.png new file mode 100644 index 0000000..c343c32 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha64.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_bottom.png new file mode 100644 index 0000000..40d9f53 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_top.png b/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_top.png new file mode 100644 index 0000000..64b5664 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_alpha_compositing_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_anim.png b/data/games/garage/mods/testnodes/textures/testnodes_anim.png new file mode 100644 index 0000000..d321fe8 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_anim.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached4_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attached4_bottom.png new file mode 100644 index 0000000..b7a7a2f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached4_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached4_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attached4_side.png new file mode 100644 index 0000000..869e073 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached4_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached4_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attached4_top.png new file mode 100644 index 0000000..94219bc Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached4_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attached_bottom.png new file mode 100644 index 0000000..e01ae57 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attached_side.png new file mode 100644 index 0000000..9459cbb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attached_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attached_top.png new file mode 100644 index 0000000..0148b41 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attached_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedf_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_bottom.png new file mode 100644 index 0000000..f9ffdfd Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedf_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_side.png new file mode 100644 index 0000000..93c0e82 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedf_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_top.png new file mode 100644 index 0000000..978ef61 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedf_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_bottom.png new file mode 100644 index 0000000..21ece33 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_side.png new file mode 100644 index 0000000..1e6a696 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_top.png new file mode 100644 index 0000000..feef552 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedfr_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedw_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_bottom.png new file mode 100644 index 0000000..488ad23 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedw_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_side.png new file mode 100644 index 0000000..a02facb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedw_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_top.png new file mode 100644 index 0000000..1f4fc7b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedw_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_bottom.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_bottom.png new file mode 100644 index 0000000..1a2e1e9 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_bottom.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_side.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_side.png new file mode 100644 index 0000000..382e2da Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_top.png b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_top.png new file mode 100644 index 0000000..39ea67b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_attachedwr_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_blocking_pointable.png b/data/games/garage/mods/testnodes/textures/testnodes_blocking_pointable.png new file mode 100644 index 0000000..fa02104 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_blocking_pointable.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_bouncy.png b/data/games/garage/mods/testnodes/textures/testnodes_bouncy.png new file mode 100644 index 0000000..eabbbdf Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_bouncy.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_buildable_to.png b/data/games/garage/mods/testnodes/textures/testnodes_buildable_to.png new file mode 100644 index 0000000..23b5e54 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_buildable_to.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_side.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_side.png new file mode 100644 index 0000000..f0b80ee Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_top.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_top.png new file mode 100644 index 0000000..1905336 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_noclimb_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_side.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_side.png new file mode 100644 index 0000000..d1c4726 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_top.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_top.png new file mode 100644 index 0000000..63da83f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nodescend_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_side.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_side.png new file mode 100644 index 0000000..d5ca130 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_top.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_top.png new file mode 100644 index 0000000..2ea3eca Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_nojump_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_resistance_side.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_resistance_side.png new file mode 100644 index 0000000..860e998 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_resistance_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_side.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_side.png new file mode 100644 index 0000000..c56ea90 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_climbable_top.png b/data/games/garage/mods/testnodes/textures/testnodes_climbable_top.png new file mode 100644 index 0000000..f2a7e59 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_climbable_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_damage.png b/data/games/garage/mods/testnodes/textures/testnodes_damage.png new file mode 100644 index 0000000..9de2ab5 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_damage.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_damage_neg.png b/data/games/garage/mods/testnodes/textures/testnodes_damage_neg.png new file mode 100644 index 0000000..85811bc Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_damage_neg.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_drowning.png b/data/games/garage/mods/testnodes/textures/testnodes_drowning.png new file mode 100644 index 0000000..57ffc8f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_drowning.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_minus.png b/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_minus.png new file mode 100644 index 0000000..88d3bdf Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_minus.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_plus.png b/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_plus.png new file mode 100644 index 0000000..61fdec2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_fall_damage_plus.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_fencelike.png b/data/games/garage/mods/testnodes/textures/testnodes_fencelike.png new file mode 100644 index 0000000..84dea1b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_fencelike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_fill_positioning_reference.png b/data/games/garage/mods/testnodes/textures/testnodes_fill_positioning_reference.png new file mode 100644 index 0000000..2ecd969 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_fill_positioning_reference.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_firelike.png b/data/games/garage/mods/testnodes/textures/testnodes_firelike.png new file mode 100644 index 0000000..ee59b0d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_firelike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslike.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslike.png new file mode 100644 index 0000000..cf3e354 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslike_detail.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_detail.png new file mode 100644 index 0000000..30c9586 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_detail.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed.png new file mode 100644 index 0000000..8a513f2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed2.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed2.png new file mode 100644 index 0000000..4ea839c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed_optional.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed_optional.png new file mode 100644 index 0000000..37de77d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslike_framed_optional.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_glasslikeliquid.png b/data/games/garage/mods/testnodes/textures/testnodes_glasslikeliquid.png new file mode 100644 index 0000000..e1e96ff Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_glasslikeliquid.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light.png b/data/games/garage/mods/testnodes/textures/testnodes_light.png new file mode 100644 index 0000000..4ba0081 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_1.png b/data/games/garage/mods/testnodes/textures/testnodes_light_1.png new file mode 100644 index 0000000..57adf5a Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_10.png b/data/games/garage/mods/testnodes/textures/testnodes_light_10.png new file mode 100644 index 0000000..4838347 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_10.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_11.png b/data/games/garage/mods/testnodes/textures/testnodes_light_11.png new file mode 100644 index 0000000..4c423d9 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_11.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_12.png b/data/games/garage/mods/testnodes/textures/testnodes_light_12.png new file mode 100644 index 0000000..bc7946d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_12.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_13.png b/data/games/garage/mods/testnodes/textures/testnodes_light_13.png new file mode 100644 index 0000000..0b63c84 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_13.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_14.png b/data/games/garage/mods/testnodes/textures/testnodes_light_14.png new file mode 100644 index 0000000..a817bd3 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_14.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_2.png b/data/games/garage/mods/testnodes/textures/testnodes_light_2.png new file mode 100644 index 0000000..852eaef Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_3.png b/data/games/garage/mods/testnodes/textures/testnodes_light_3.png new file mode 100644 index 0000000..79fc834 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_4.png b/data/games/garage/mods/testnodes/textures/testnodes_light_4.png new file mode 100644 index 0000000..75f8c61 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_5.png b/data/games/garage/mods/testnodes/textures/testnodes_light_5.png new file mode 100644 index 0000000..b6eede0 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_6.png b/data/games/garage/mods/testnodes/textures/testnodes_light_6.png new file mode 100644 index 0000000..ef54add Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_7.png b/data/games/garage/mods/testnodes/textures/testnodes_light_7.png new file mode 100644 index 0000000..4a885b0 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_7.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_8.png b/data/games/garage/mods/testnodes/textures/testnodes_light_8.png new file mode 100644 index 0000000..b283301 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_8.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_light_9.png b/data/games/garage/mods/testnodes/textures/testnodes_light_9.png new file mode 100644 index 0000000..2aa9023 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_light_9.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_line_crossing.png b/data/games/garage/mods/testnodes/textures/testnodes_line_crossing.png new file mode 100644 index 0000000..e566f27 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_line_crossing.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_line_curved.png b/data/games/garage/mods/testnodes/textures/testnodes_line_curved.png new file mode 100644 index 0000000..ab9f8e7 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_line_curved.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_line_straight.png b/data/games/garage/mods/testnodes/textures/testnodes_line_straight.png new file mode 100644 index 0000000..4f33d9c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_line_straight.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_line_t_junction.png b/data/games/garage/mods/testnodes/textures/testnodes_line_t_junction.png new file mode 100644 index 0000000..5668f6e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_line_t_junction.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquid.png b/data/games/garage/mods/testnodes/textures/testnodes_liquid.png new file mode 100644 index 0000000..98ab270 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquid.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing.png new file mode 100644 index 0000000..1736b89 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r0.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r0.png new file mode 100644 index 0000000..e8a6103 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r0.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r1.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r1.png new file mode 100644 index 0000000..b4e45b4 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r2.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r2.png new file mode 100644 index 0000000..e064b8f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r3.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r3.png new file mode 100644 index 0000000..bef7739 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r4.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r4.png new file mode 100644 index 0000000..de1001b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r5.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r5.png new file mode 100644 index 0000000..97b422e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r6.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r6.png new file mode 100644 index 0000000..4cd8e4e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r7.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r7.png new file mode 100644 index 0000000..711dd96 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r7.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r8.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r8.png new file mode 100644 index 0000000..9cf22b8 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidflowing_r8.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource.png new file mode 100644 index 0000000..b3f29b7 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r0.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r0.png new file mode 100644 index 0000000..da0a996 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r0.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r1.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r1.png new file mode 100644 index 0000000..66bf2be Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r2.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r2.png new file mode 100644 index 0000000..fc5f65c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r3.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r3.png new file mode 100644 index 0000000..0f46e29 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r4.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r4.png new file mode 100644 index 0000000..0693a04 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r5.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r5.png new file mode 100644 index 0000000..cc9d039 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r6.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r6.png new file mode 100644 index 0000000..e276a07 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r7.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r7.png new file mode 100644 index 0000000..3534a4b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r7.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r8.png b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r8.png new file mode 100644 index 0000000..ee1a8b1 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_liquidsource_r8.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_marble_glass.png b/data/games/garage/mods/testnodes/textures/testnodes_marble_glass.png new file mode 100644 index 0000000..cd7ce5c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_marble_glass.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_marble_metal.png b/data/games/garage/mods/testnodes/textures/testnodes_marble_metal.png new file mode 100644 index 0000000..205d499 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_marble_metal.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_marble_metal_overlay.png b/data/games/garage/mods/testnodes/textures/testnodes_marble_metal_overlay.png new file mode 100644 index 0000000..e2bb431 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_marble_metal_overlay.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mask_WRGBKW.png b/data/games/garage/mods/testnodes/textures/testnodes_mask_WRGBKW.png new file mode 100644 index 0000000..03ab71e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mask_WRGBKW.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes.png new file mode 100644 index 0000000..51b8e00 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes10.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes10.png new file mode 100644 index 0000000..a6d569a Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes10.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes2.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes2.png new file mode 100644 index 0000000..9ea65c1 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes3.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes3.png new file mode 100644 index 0000000..96bc55a Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes4.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes4.png new file mode 100644 index 0000000..fca3372 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes5.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes5.png new file mode 100644 index 0000000..0f24f16 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes6.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes6.png new file mode 100644 index 0000000..46c6e50 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes7.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes7.png new file mode 100644 index 0000000..75b3bdd Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes7.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes8.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes8.png new file mode 100644 index 0000000..4b18859 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes8.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes9.png b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes9.png new file mode 100644 index 0000000..9902be8 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_mesh_stripes9.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_move_resistance.png b/data/games/garage/mods/testnodes/textures/testnodes_move_resistance.png new file mode 100644 index 0000000..cc7cb05 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_move_resistance.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node.png b/data/games/garage/mods/testnodes/textures/testnodes_node.png new file mode 100644 index 0000000..145099b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling.png new file mode 100644 index 0000000..4415318 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_1.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_1.png new file mode 100644 index 0000000..74137b3 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_2.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_2.png new file mode 100644 index 0000000..8327673 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_3.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_3.png new file mode 100644 index 0000000..975de54 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_4.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_4.png new file mode 100644 index 0000000..87fc276 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_5.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_5.png new file mode 100644 index 0000000..9a8ce2d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_node_falling_6.png b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_6.png new file mode 100644 index 0000000..9ad2f5b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_node_falling_6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_nodebox.png b/data/games/garage/mods/testnodes/textures/testnodes_nodebox.png new file mode 100644 index 0000000..66e8dd6 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_nodebox.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_nojump_side.png b/data/games/garage/mods/testnodes/textures/testnodes_nojump_side.png new file mode 100644 index 0000000..6a64cff Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_nojump_side.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_nojump_top.png b/data/games/garage/mods/testnodes/textures/testnodes_nojump_top.png new file mode 100644 index 0000000..fe77083 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_nojump_top.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal.png b/data/games/garage/mods/testnodes/textures/testnodes_normal.png new file mode 100644 index 0000000..a1acfd9 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal1.png b/data/games/garage/mods/testnodes/textures/testnodes_normal1.png new file mode 100644 index 0000000..edaba77 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal1.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal2.png b/data/games/garage/mods/testnodes/textures/testnodes_normal2.png new file mode 100644 index 0000000..0080a9e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal2.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal3.png b/data/games/garage/mods/testnodes/textures/testnodes_normal3.png new file mode 100644 index 0000000..0426ab2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal3.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal4.png b/data/games/garage/mods/testnodes/textures/testnodes_normal4.png new file mode 100644 index 0000000..0d1922e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal4.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal5.png b/data/games/garage/mods/testnodes/textures/testnodes_normal5.png new file mode 100644 index 0000000..0b7dcd2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal5.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_normal6.png b/data/games/garage/mods/testnodes/textures/testnodes_normal6.png new file mode 100644 index 0000000..f34a67d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_normal6.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_not_pointable.png b/data/games/garage/mods/testnodes/textures/testnodes_not_pointable.png new file mode 100644 index 0000000..8509a68 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_not_pointable.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_overlay.png b/data/games/garage/mods/testnodes/textures/testnodes_overlay.png new file mode 100644 index 0000000..1c69b5e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_overlay.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_overlayable.png b/data/games/garage/mods/testnodes/textures/testnodes_overlayable.png new file mode 100644 index 0000000..431bc94 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_overlayable.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_palette_4dir.png b/data/games/garage/mods/testnodes/textures/testnodes_palette_4dir.png new file mode 100644 index 0000000..7a26c9b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_palette_4dir.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_palette_facedir.png b/data/games/garage/mods/testnodes/textures/testnodes_palette_facedir.png new file mode 100644 index 0000000..8cf47bb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_palette_facedir.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_palette_full.png b/data/games/garage/mods/testnodes/textures/testnodes_palette_full.png new file mode 100644 index 0000000..e0a5f8b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_palette_full.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_palette_metal.png b/data/games/garage/mods/testnodes/textures/testnodes_palette_metal.png new file mode 100644 index 0000000..8d87445 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_palette_metal.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_palette_wallmounted.png b/data/games/garage/mods/testnodes/textures/testnodes_palette_wallmounted.png new file mode 100644 index 0000000..682f3ac Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_palette_wallmounted.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike.png new file mode 100644 index 0000000..cc46444 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_degrotate.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_degrotate.png new file mode 100644 index 0000000..01c81da Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_degrotate.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_leveled.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_leveled.png new file mode 100644 index 0000000..53504db Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_leveled.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_meshoptions.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_meshoptions.png new file mode 100644 index 0000000..d504d45 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_meshoptions.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted.png new file mode 100644 index 0000000..79cf212 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base.png new file mode 100644 index 0000000..b9ee9e5 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_degrotate.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_degrotate.png new file mode 100644 index 0000000..85311cb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_degrotate.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_leveled.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_leveled.png new file mode 100644 index 0000000..bc602ba Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_leveled.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_meshoptions.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_meshoptions.png new file mode 100644 index 0000000..d100023 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_meshoptions.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png new file mode 100644 index 0000000..97a8915 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_waving.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_waving.png new file mode 100644 index 0000000..527817b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_waving.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_degrotate.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_degrotate.png new file mode 100644 index 0000000..45e75bd Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_degrotate.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_leveled.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_leveled.png new file mode 100644 index 0000000..8954b2c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_leveled.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_meshoptions.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_meshoptions.png new file mode 100644 index 0000000..a782d48 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_meshoptions.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png new file mode 100644 index 0000000..0decffa Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_waving.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_waving.png new file mode 100644 index 0000000..112a054 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_rooted_waving.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_wallmounted.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_wallmounted.png new file mode 100644 index 0000000..c603f14 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_wallmounted.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_plantlike_waving.png b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_waving.png new file mode 100644 index 0000000..b584a8d Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_plantlike_waving.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_pointable.png b/data/games/garage/mods/testnodes/textures/testnodes_pointable.png new file mode 100644 index 0000000..70c8f36 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_pointable.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_false.png b/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_false.png new file mode 100644 index 0000000..a14713a Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_false.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_true.png b/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_true.png new file mode 100644 index 0000000..448cf91 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_post_effect_color_shaded_true.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail2_crossing.png b/data/games/garage/mods/testnodes/textures/testnodes_rail2_crossing.png new file mode 100644 index 0000000..530bbba Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail2_crossing.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail2_curved.png b/data/games/garage/mods/testnodes/textures/testnodes_rail2_curved.png new file mode 100644 index 0000000..4ed1ca0 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail2_curved.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail2_straight.png b/data/games/garage/mods/testnodes/textures/testnodes_rail2_straight.png new file mode 100644 index 0000000..8749330 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail2_straight.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail2_t_junction.png b/data/games/garage/mods/testnodes/textures/testnodes_rail2_t_junction.png new file mode 100644 index 0000000..0517f65 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail2_t_junction.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail_crossing.png b/data/games/garage/mods/testnodes/textures/testnodes_rail_crossing.png new file mode 100644 index 0000000..3916ce1 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail_crossing.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail_curved.png b/data/games/garage/mods/testnodes/textures/testnodes_rail_curved.png new file mode 100644 index 0000000..e444198 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail_curved.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail_straight.png b/data/games/garage/mods/testnodes/textures/testnodes_rail_straight.png new file mode 100644 index 0000000..872d04f Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail_straight.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_rail_t_junction.png b/data/games/garage/mods/testnodes/textures/testnodes_rail_t_junction.png new file mode 100644 index 0000000..7e4af51 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_rail_t_junction.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_sign3d.png b/data/games/garage/mods/testnodes/textures/testnodes_sign3d.png new file mode 100644 index 0000000..e4ad947 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_sign3d.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_signlike.png b/data/games/garage/mods/testnodes/textures/testnodes_signlike.png new file mode 100644 index 0000000..33ffcba Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_signlike.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_slippery.png b/data/games/garage/mods/testnodes/textures/testnodes_slippery.png new file mode 100644 index 0000000..b990468 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_slippery.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_street_crossing.png b/data/games/garage/mods/testnodes/textures/testnodes_street_crossing.png new file mode 100644 index 0000000..d6e35ad Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_street_crossing.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_street_curved.png b/data/games/garage/mods/testnodes/textures/testnodes_street_curved.png new file mode 100644 index 0000000..251b7fb Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_street_curved.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_street_straight.png b/data/games/garage/mods/testnodes/textures/testnodes_street_straight.png new file mode 100644 index 0000000..639e24b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_street_straight.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_street_t_junction.png b/data/games/garage/mods/testnodes/textures/testnodes_street_t_junction.png new file mode 100644 index 0000000..713621e Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_street_t_junction.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_sunlight_filter.png b/data/games/garage/mods/testnodes/textures/testnodes_sunlight_filter.png new file mode 100644 index 0000000..b38ea40 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_sunlight_filter.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_bt.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_bt.tga new file mode 100644 index 0000000..2dc587b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_bt.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_tb.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_tb.tga new file mode 100644 index 0000000..b44a81c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type10_32bpp_tb.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_bt.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_bt.tga new file mode 100644 index 0000000..d2c2ca6 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_bt.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_tb.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_tb.tga new file mode 100644 index 0000000..dfcb988 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type1_24bpp_tb.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_bt.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_bt.tga new file mode 100644 index 0000000..0206216 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_bt.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_tb.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_tb.tga new file mode 100644 index 0000000..2563f08 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_16bpp_tb.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_bt.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_bt.tga new file mode 100644 index 0000000..3350500 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_bt.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_tb.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_tb.tga new file mode 100644 index 0000000..216de06 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type2_32bpp_tb.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_bt.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_bt.tga new file mode 100644 index 0000000..695bb4b Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_bt.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_tb.tga b/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_tb.tga new file mode 100644 index 0000000..c08a093 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_tga_type3_16bpp_tb.tga differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_torchlike_ceiling.png b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_ceiling.png new file mode 100644 index 0000000..5d9862c Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_ceiling.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_torchlike_floor.png b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_floor.png new file mode 100644 index 0000000..adf1e00 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_floor.png differ diff --git a/data/games/garage/mods/testnodes/textures/testnodes_torchlike_wall.png b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_wall.png new file mode 100644 index 0000000..cb442b2 Binary files /dev/null and b/data/games/garage/mods/testnodes/textures/testnodes_torchlike_wall.png differ diff --git a/data/games/garage/mods/testpathfinder/README.md b/data/games/garage/mods/testpathfinder/README.md new file mode 100644 index 0000000..2b9d46e --- /dev/null +++ b/data/games/garage/mods/testpathfinder/README.md @@ -0,0 +1,15 @@ +# Pathfinder Tester + +Usage: + +Use the Pathfinder Tester tool (`testpathfinder:testpathfinder`). +Here's how it works: + +* Place on node: Set destination position +* Punch: Find path +* Sneak+punch: Select pathfinding algorithm + +Information will be shown in chat. If a path was found, all waypoints +will be shown for a few seconds. + +See `init.lua` for config variables. diff --git a/data/games/garage/mods/testpathfinder/init.lua b/data/games/garage/mods/testpathfinder/init.lua new file mode 100644 index 0000000..a0f99f8 --- /dev/null +++ b/data/games/garage/mods/testpathfinder/init.lua @@ -0,0 +1,136 @@ +local S = core.get_translator("testpathfinder") + +-- Config parameters + +-- Maximum direct distance between start and end +local MAX_DIRECT_DISTANCE = 64 +-- Maximum search distance +local MAX_SEARCH_DISTANCE = 32 +-- Maximum permitted jump height +local MAX_JUMP = 1 +-- Maximum permitted drop height +local MAX_DROP = 5 +-- If true, mod won't refuse to run pathfinder even at long distances +local IGNORE_MAX_DISTANCE_SAFEGUARD = false + +-- End of config parameters + +local timer = 0 +local algorithms = { + "A*_noprefetch", + "A*", + "Dijkstra", +} + +local function find_path_for_player(player, itemstack) + local meta = itemstack:get_meta() + if not meta then + return + end + local x = meta:get_int("pos_x") + local y = meta:get_int("pos_y") + local z = meta:get_int("pos_z") + local algo = meta:get_int("algorithm") + if x and y and z then + local pos2 = {x=x, y=y, z=z} + algo = algorithms[algo+1] + local pos1 = vector.round(player:get_pos()) + -- Don't bother calling pathfinder for high distance to avoid freezing + if (not IGNORE_MAX_DISTANCE_SAFEGUARD) and (vector.distance(pos1, pos2) > MAX_DIRECT_DISTANCE) then + core.chat_send_player(player:get_player_name(), S("Destination too far away! Set a destination (via placing) within a distance of @1 and try again!", MAX_DIRECT_DISTANCE)) + return + end + local str = S("Path from @1 to @2:", + core.pos_to_string(pos1), + core.pos_to_string(pos2)) + + core.chat_send_player(player:get_player_name(), str) + local time_start = core.get_us_time() + local path = core.find_path(pos1, pos2, MAX_SEARCH_DISTANCE, MAX_JUMP, MAX_DROP, algo) + local time_end = core.get_us_time() + local time_diff = time_end - time_start + str = "" + if not path then + core.chat_send_player(player:get_player_name(), S("No path!")) + core.chat_send_player(player:get_player_name(), S("Time: @1 ms", time_diff/1000)) + return + end + for s=1, #path do + str = str .. core.pos_to_string(path[s]) .. "\n" + local t + if s == #path then + t = "testpathfinder_waypoint_end.png" + elseif s == 1 then + t = "testpathfinder_waypoint_start.png" + else + local c = math.floor(((#path-s)/#path)*255) + t = string.format("testpathfinder_waypoint.png^[multiply:#%02x%02x00", 0xFF-c, c) + end + core.add_particle({ + pos = path[s], + expirationtime = 5 + 0.2 * s, + playername = player:get_player_name(), + glow = core.LIGHT_MAX, + texture = t, + size = 3, + }) + end + core.chat_send_player(player:get_player_name(), str) + core.chat_send_player(player:get_player_name(), S("Path length: @1", #path)) + core.chat_send_player(player:get_player_name(), S("Time: @1 ms", time_diff/1000)) + end +end + +local function set_destination(itemstack, user, pointed_thing) + if not (user and user:is_player()) then + return + end + local name = user:get_player_name() + local obj + local meta = itemstack:get_meta() + if pointed_thing.type == "node" then + local pos = pointed_thing.above + meta:set_int("pos_x", pos.x) + meta:set_int("pos_y", pos.y) + meta:set_int("pos_z", pos.z) + core.chat_send_player(user:get_player_name(), S("Destination set to @1", core.pos_to_string(pos))) + return itemstack + end +end + +local function find_path_or_set_algorithm(itemstack, user, pointed_thing) + if not (user and user:is_player()) then + return + end + local ctrl = user:get_player_control() + -- No sneak: Find path + if not ctrl.sneak then + find_path_for_player(user, itemstack) + else + -- Sneak: Set algorithm + local meta = itemstack:get_meta() + local algo = meta:get_int("algorithm") + algo = (algo + 1) % #algorithms + meta:set_int("algorithm", algo) + core.chat_send_player(user:get_player_name(), S("Algorithm: @1", algorithms[algo+1])) + return itemstack + end +end + +-- Punch: Find path +-- Sneak+punch: Select pathfinding algorithm +-- Place: Select destination node +core.register_tool("testpathfinder:testpathfinder", { + description = S("Pathfinder Tester") .."\n".. + S("Finds path between 2 points") .."\n".. + S("Place on node: Select destination") .."\n".. + S("Punch: Find path from here") .."\n".. + S("Sneak+Punch: Change algorithm"), + inventory_image = "testpathfinder_testpathfinder.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = find_path_or_set_algorithm, + on_secondary_use = set_destination, + on_place = set_destination, +}) + + diff --git a/data/games/garage/mods/testpathfinder/mod.conf b/data/games/garage/mods/testpathfinder/mod.conf new file mode 100644 index 0000000..6b2c0e3 --- /dev/null +++ b/data/games/garage/mods/testpathfinder/mod.conf @@ -0,0 +1,2 @@ +name = testpathfinder +description = Tool to test Luanti's pathfinder function diff --git a/data/games/garage/mods/testpathfinder/textures/testpathfinder_testpathfinder.png b/data/games/garage/mods/testpathfinder/textures/testpathfinder_testpathfinder.png new file mode 100644 index 0000000..37eef05 Binary files /dev/null and b/data/games/garage/mods/testpathfinder/textures/testpathfinder_testpathfinder.png differ diff --git a/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint.png b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint.png new file mode 100644 index 0000000..661dcf9 Binary files /dev/null and b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint.png differ diff --git a/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_end.png b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_end.png new file mode 100644 index 0000000..41a1cc5 Binary files /dev/null and b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_end.png differ diff --git a/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_start.png b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_start.png new file mode 100644 index 0000000..a22e31c Binary files /dev/null and b/data/games/garage/mods/testpathfinder/textures/testpathfinder_waypoint_start.png differ diff --git a/data/games/garage/mods/testtools/README.md b/data/games/garage/mods/testtools/README.md new file mode 100644 index 0000000..03d826d --- /dev/null +++ b/data/games/garage/mods/testtools/README.md @@ -0,0 +1,154 @@ +# Test Tools readme + +Test Tools is a mod for developers that adds a bunch of tools to directly manipulate nodes and entities. This is great for quickly testing out stuff. + +Here's the list of tools: + +## Remover +Removes nodes and non-player entities that you punch. + +## Node Setter +Replace a node with another one. + +First, punch a node you want to remember. +Then rightclick any other node to replace it with the node you remembered. + +If you rightclick while pointing nothing, you can manually enter the node and param2. + +## Param2 Tool +Change the value param2 of nodes. + +* Punch: Add 1 to param2 +* Sneak+Punch: Add 8 to param2 +* Place: Subtract 1 from param2 +* Sneak+Place: Subtract 8 from param2 + +Note: Use the debug screen (F5) to see the param2 of the pointed node. + +## Falling Node Tool +Turns nodes into falling nodes. + +Usage: + +* Punch node: Make it fall +* Place: Try to teleport up to 2 units upwards, then make it fall + +## Node Meta Editor +Edit and view metadata of nodes. + +Usage: + +* Punch: Open node metadata editor + +## Item Meta Editor +Edit and view metadata of items. + +Usage: + +* Place/Punch: Opens item metadata editor of the item in the next + inventory slot from the wielded item + +## Entity Rotator +Changes the entity rotation (with `set_rotation`). + +Usage: + +* Punch entity: Rotate yaw +* Punch entity while holding down “Sneak” key: Rotate pitch +* Punch entity while holding down “Special” key (aka “Aux”): Rotate roll + +Each usage rotates the entity by 22.5°. + +## Entity Spawner +Spawns entities. + +Usage: + +* Punch to select entity or spawn one directly +* Place to place selected entity + +## Object Property Editor +Edits properties of objects. + +Usage: + +* Punch object to open a formspec that allows you to view and edit properties +* Punch air to edit properties of your own player object + +To edit a property, select it in the list, enter a new value (in Lua syntax) +and hit “Submit”. + +## Object Attacher +Allows you to attach an object to another one. + +Basic usage: +* First select the parent object, then the child object that should be attached +* Selecting an object is done by punching it +* Sneak+punch to detach selected object +* If you punch air, you select yourself + +Configuration: +* Place: Increase attachment Y position +* Sneak+place: decrease attachment Y position +* Aux+place: Increase attachment X rotation +* Aux+Sneak+Rightclick: Decrease attachment X rotation + +Hint: To detach all objects nearby you (including on yourself), use the +`/detach` server command. + +## Object Mover +Move an object by a given distance. + +Usage: +* Punch object into the direction you want to move it +* Sneak+punch: Move object towards you +* Place: Increase move distance +* Sneak+place: Decrease move distance + +## Children Getter +Shows list of objects that are attached to an object (aka "children") in chat. + +Usage: +* Punch object: Show children of punched object +* Punch air: Show your own children + +## Entity Visual Scaler +Change visual size of entities + +Usage: + +* Punch entity to increase visual size +* Sneak+punch entity to decrease visual size + +## Branding Iron +Give an object a temporary name. + +Usage: +* Punch object: Brand the object +* Punch air: Brand yourself +* The name is valid until the object unloads. +* Devices that accept the returned name also accept "player:" for players. + +Use `testtools.get_branded_object(name)` to get an ObjRef. + +## Note Meta Privatizer +Sets the 'formspec' and 'infotext' metadata fields of a node +to private. This means that clients can no longer access these +fields. +This only works for chests [`chest:chest`] at the moment. + +Usage: +* Punch: Set metadata of pointed node to private + +## Light Tool +Show light level of node. + +Usage: +* Punch: Show light info of node in front of the punched node's side +* Place: Show light info of the node that you touched + +## Particle Spawner +Spawn a random animated particle. + +Usage: +* Punch: Spawn particle diff --git a/data/games/garage/mods/testtools/init.lua b/data/games/garage/mods/testtools/init.lua new file mode 100644 index 0000000..6586d87 --- /dev/null +++ b/data/games/garage/mods/testtools/init.lua @@ -0,0 +1,1067 @@ +local S = core.get_translator("testtools") +local F = core.formspec_escape + +testtools = {} + +dofile(core.get_modpath("testtools") .. "/light.lua") +dofile(core.get_modpath("testtools") .. "/privatizer.lua") +dofile(core.get_modpath("testtools") .. "/particles.lua") +dofile(core.get_modpath("testtools") .. "/node_box_visualizer.lua") + +local pointabilities_nodes = { + nodes = { + ["group:blocking_pointable_test"] = true, + ["group:not_pointable_test"] = true, + }, +} + +local pointabilities_objects = { + objects = { + ["group:blocking_pointable_test"] = true, + ["group:not_pointable_test"] = true, + }, +} + +core.register_tool("testtools:param2tool", { + description = S("Param2 Tool") .."\n".. + S("Modify param2 value of nodes") .."\n".. + S("Punch: +1") .."\n".. + S("Sneak+Punch: +8") .."\n".. + S("Place: -1") .."\n".. + S("Sneak+Place: -8"), + inventory_image = "testtools_param2tool.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, + on_use = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type ~= "node" or (not pos) then + return + end + local add = 1 + if user then + local ctrl = user:get_player_control() + if ctrl.sneak then + add = 8 + end + end + local node = core.get_node(pos) + node.param2 = node.param2 + add + core.swap_node(pos, node) + end, + on_place = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type ~= "node" or (not pos) then + return + end + local add = -1 + if user then + local ctrl = user:get_player_control() + if ctrl.sneak then + add = -8 + end + end + local node = core.get_node(pos) + node.param2 = node.param2 + add + core.swap_node(pos, node) + end, +}) + +core.register_tool("testtools:node_setter", { + description = S("Node Setter") .."\n".. + S("Replace pointed node with something else") .."\n".. + S("Punch: Select pointed node") .."\n".. + S("Place on node: Replace node with selected node") .."\n".. + S("Place in air: Manually select a node"), + inventory_image = "testtools_node_setter.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, + on_use = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type == "nothing" then + local meta = itemstack:get_meta() + meta:set_string("node", "air") + meta:set_int("node_param2", 0) + if user and user:is_player() then + core.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", "air", 0)) + end + return itemstack + elseif pointed_thing.type ~= "node" or (not pos) then + return + end + local node = core.get_node(pos) + local meta = itemstack:get_meta() + meta:set_string("node", node.name) + meta:set_int("node_param2", node.param2) + if user and user:is_player() then + core.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", node.name, node.param2)) + end + return itemstack + end, + on_secondary_use = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + local nodename = meta:get_string("node") or "" + local param2 = meta:get_int("node_param2") or 0 + + core.show_formspec(user:get_player_name(), "testtools:node_setter", + "size[4,4]".. + "field[0.5,1;3,1;nodename;"..F(S("Node name (itemstring):"))..";"..F(nodename).."]".. + "field[0.5,2;3,1;param2;"..F(S("param2:"))..";"..F(tostring(param2)).."]".. + "button_exit[0.5,3;3,1;submit;"..F(S("Submit")).."]" + ) + end, + on_place = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + local meta = itemstack:get_meta() + local nodename = meta:get_string("node") + if nodename == "" and user and user:is_player() then + core.chat_send_player(user:get_player_name(), S("Punch a node first!")) + return + end + local param2 = meta:get_int("node_param2") + if not param2 then + param2 = 0 + end + local node = { name = nodename, param2 = param2 } + if not core.registered_nodes[nodename] then + core.chat_send_player(user:get_player_name(), S("Cannot set unknown node: @1", nodename)) + return + end + core.set_node(pos, node) + end, +}) + +core.register_tool("testtools:remover", { + description = S("Remover") .."\n".. + S("Punch: Remove pointed node or object"), + inventory_image = "testtools_remover.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = { + nodes = pointabilities_nodes.nodes, + objects = pointabilities_objects.objects, + }, + on_use = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type == "node" and pos ~= nil then + core.remove_node(pos) + elseif pointed_thing.type == "object" then + local obj = pointed_thing.ref + if not obj:is_player() then + obj:remove() + else + core.chat_send_player(user:get_player_name(), S("Can't remove players!")) + end + end + end, +}) + +core.register_tool("testtools:falling_node_tool", { + description = S("Falling Node Tool") .."\n".. + S("Punch: Make pointed node fall") .."\n".. + S("Place: Move pointed node 2 units upwards, then make it fall"), + inventory_image = "testtools_falling_node_tool.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, + on_place = function(itemstack, user, pointed_thing) + -- Teleport node 1-2 units upwards (if possible) and make it fall + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type ~= "node" or (not pos) then + return + end + local ok = false + local highest + for i=1,2 do + local above = {x=pos.x,y=pos.y+i,z=pos.z} + local n2 = core.get_node(above) + local def2 = core.registered_nodes[n2.name] + if def2 and (not def2.walkable) then + highest = above + else + break + end + end + if highest then + local node = core.get_node(pos) + local metatable = core.get_meta(pos):to_table() + core.remove_node(pos) + core.set_node(highest, node) + local meta_highest = core.get_meta(highest) + meta_highest:from_table(metatable) + ok = core.spawn_falling_node(highest) + else + ok = core.spawn_falling_node(pos) + end + if not ok and user and user:is_player() then + core.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!")) + end + end, + on_use = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing) + if pointed_thing.type ~= "node" or (not pos) then + return + end + local ok = core.spawn_falling_node(pos) + if not ok and user and user:is_player() then + core.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!")) + end + end, +}) + +core.register_tool("testtools:rotator", { + description = S("Entity Rotator") .. "\n" .. + S("Rotate pointed entity") .."\n".. + S("Punch: Yaw") .."\n".. + S("Sneak+Punch: Pitch") .."\n".. + S("Aux1+Punch: Roll"), + inventory_image = "testtools_entity_rotator.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "object" then + return + end + local obj = pointed_thing.ref + if obj:is_player() then + -- No player rotation + return + else + local axis = "y" + if user and user:is_player() then + local ctrl = user:get_player_control() + if ctrl.sneak then + axis = "x" + elseif ctrl.aux1 then + axis = "z" + end + end + local rot = obj:get_rotation() + rot[axis] = rot[axis] + math.pi/8 + if rot[axis] > math.pi*2 then + rot[axis] = rot[axis] - math.pi*2 + end + obj:set_rotation(rot) + end + end, +}) + +local mover_config = function(itemstack, user, pointed_thing) + if not (user and user:is_player()) then + return + end + local name = user:get_player_name() + local ctrl = user:get_player_control() + local meta = itemstack:get_meta() + local dist = 1.0 + if meta:contains("distance") then + dist = meta:get_int("distance") + end + if ctrl.sneak then + dist = dist - 1 + else + dist = dist + 1 + end + meta:set_int("distance", dist) + core.chat_send_player(user:get_player_name(), S("distance=@1/10", dist*2)) + return itemstack +end + +core.register_tool("testtools:object_mover", { + description = S("Object Mover") .."\n".. + S("Move pointed object towards or away from you") .."\n".. + S("Punch: Move by distance").."\n".. + S("Sneak+Punch: Move by negative distance").."\n".. + S("Place: Increase distance").."\n".. + S("Sneak+Place: Decrease distance"), + inventory_image = "testtools_object_mover.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_place = mover_config, + on_secondary_use = mover_config, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "object" then + return + end + local obj = pointed_thing.ref + if not (user and user:is_player()) then + return + end + local yaw = user:get_look_horizontal() + local dir = core.yaw_to_dir(yaw) + local pos = obj:get_pos() + local pitch = user:get_look_vertical() + if pitch > 0.25 * math.pi then + dir.y = -1 + dir.x = 0 + dir.z = 0 + elseif pitch < -0.25 * math.pi then + dir.y = 1 + dir.x = 0 + dir.z = 0 + end + local ctrl = user:get_player_control() + if ctrl.sneak then + dir = vector.multiply(dir, -1) + end + local meta = itemstack:get_meta() + if meta:contains("distance") then + local dist = meta:get_int("distance") + dir = vector.multiply(dir, dist*0.2) + end + pos = vector.add(pos, dir) + obj:set_pos(pos) + end, +}) + + + +core.register_tool("testtools:entity_scaler", { + description = S("Entity Visual Scaler") .."\n".. + S("Scale visual size of entities") .."\n".. + S("Punch: Increase size") .."\n".. + S("Sneak+Punch: Decrease scale"), + inventory_image = "testtools_entity_scaler.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "object" then + return + end + local obj = pointed_thing.ref + if obj:is_player() then + -- No player scaling + return + else + local diff = 0.1 + if user and user:is_player() then + local ctrl = user:get_player_control() + if ctrl.sneak then + diff = -0.1 + end + end + local prop = obj:get_properties() + if not prop.visual_size then + prop.visual_size = { x=1, y=1, z=1 } + else + prop.visual_size = { x=prop.visual_size.x+diff, y=prop.visual_size.y+diff, z=prop.visual_size.z+diff } + if prop.visual_size.x <= 0.1 then + prop.visual_size.x = 0.1 + end + if prop.visual_size.y <= 0.1 then + prop.visual_size.y = 0.1 + end + if prop.visual_size.z <= 0.1 then + prop.visual_size.z = 0.1 + end + end + obj:set_properties(prop) + end + end, +}) + + +-- value-weak tables, because we don't want to keep the objrefs of unloaded objects +local branded_objects = setmetatable({}, {__mode = "v"}) +local next_brand_num = 1 + +function testtools.get_branded_object(name) + if name:sub(1, 7) == "player:" then + return core.get_player_by_name(name:sub(8)) + elseif name:sub(1, 4) == "obj:" then + return branded_objects[tonumber(name:sub(5)) or 0] + end + return nil +end + +core.register_tool("testtools:branding_iron", { + description = S("Branding Iron") .."\n".. + S("Give an object a temporary name.") .."\n".. + S("Punch object: Brand the object") .."\n".. + S("Punch air: Brand yourself") .."\n".. + S("The name is valid until the object unloads.") .."\n".. + S("Devices that accept the returned name also accept \"player:\" for players."), + inventory_image = "testtools_branding_iron.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_use = function(_itemstack, user, pointed_thing) + local obj + local msg + if pointed_thing.type == "object" then + obj = pointed_thing.ref + msg = "You can now refer to this object with: \"@1\"" + elseif pointed_thing.type == "nothing" then + obj = user + msg = "You can now refer to yourself with: \"@1\"" + else + return + end + + local brand_num = next_brand_num + next_brand_num = next_brand_num + 1 + branded_objects[brand_num] = obj + + core.chat_send_player(user:get_player_name(), S(msg, "obj:"..brand_num)) + end, +}) + + +local selections = {} +local entity_list +local function get_entity_list() + if entity_list then + return entity_list + end + local ents = core.registered_entities + local list = {} + for k,_ in pairs(ents) do + table.insert(list, k) + end + table.sort(list) + entity_list = list + return entity_list +end +core.register_tool("testtools:entity_spawner", { + description = S("Entity Spawner") .."\n".. + S("Spawns entities") .."\n".. + S("Punch: Select entity to spawn") .."\n".. + S("Place: Spawn selected entity"), + inventory_image = "testtools_entity_spawner.png", + groups = { testtool = 1, disable_repair = 1 }, + on_place = function(itemstack, user, pointed_thing) + local name = user:get_player_name() + if pointed_thing.type == "node" then + if selections[name] then + local pos = pointed_thing.above + core.add_entity(pos, get_entity_list()[selections[name]]) + else + core.chat_send_player(name, S("Select an entity first (with punch key)!")) + end + end + end, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "object" then + return + end + if user and user:is_player() then + local list = table.concat(get_entity_list(), ",") + local name = user:get_player_name() + local sel = selections[name] or "" + core.show_formspec(name, "testtools:entity_list", + "size[9,9]".. + "textlist[0,0;9,8;entity_list;"..list..";"..sel..";false]".. + "button[0,8;4,1;spawn;Spawn entity]" + ) + end + end, +}) + +local function prop_to_string(property) + if type(property) == "string" then + return "\"" .. property .. "\"" + elseif type(property) == "table" then + return tostring(dump(property)):gsub("\n", "") + else + return tostring(property) + end +end + +local property_formspec_data = {} +local property_formspec_index = {} +local selected_objects = {} +local function get_object_properties_form(obj, playername) + if not playername then return "" end + local props = obj:get_properties() + local str = "" + property_formspec_data[playername] = {} + local proplist = {} + for k,_ in pairs(props) do + table.insert(proplist, k) + end + table.sort(proplist) + for p=1, #proplist do + local k = proplist[p] + local v = props[k] + local newline = "" + newline = k .. " = " + newline = newline .. prop_to_string(v) + str = str .. F(newline) + if p < #proplist then + str = str .. "," + end + table.insert(property_formspec_data[playername], k) + end + return str +end + +local editor_formspec_selindex = {} + +local editor_formspec = function(playername, obj, value, sel) + if not value then + value = "" + end + if not sel then + sel = "" + end + local list = get_object_properties_form(obj, playername) + local title + if obj:is_player() then + title = S("Object properties of player “@1”", obj:get_player_name()) + else + local ent = obj:get_luaentity() + title = S("Object properties of @1", ent.name) + end + core.show_formspec(playername, "testtools:object_editor", + "size[9,9]".. + "label[0,0;"..F(title).."]".. + "textlist[0,0.5;9,7.5;object_props;"..list..";"..sel..";false]".. + "field[0.2,8.75;8,1;value;"..F(S("Value"))..";"..F(value).."]".. + "field_close_on_enter[value;false]".. + "button[8,8.5;1,1;submit;"..F(S("Submit")).."]" + ) +end + +core.register_tool("testtools:object_editor", { + description = S("Object Property Editor") .."\n".. + S("Edit properties of objects") .."\n".. + S("Punch object: Edit object") .."\n".. + S("Punch air: Edit yourself"), + inventory_image = "testtools_object_editor.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_use = function(itemstack, user, pointed_thing) + if user and user:is_player() then + local name = user:get_player_name() + + if pointed_thing.type == "object" then + selected_objects[name] = pointed_thing.ref + elseif pointed_thing.type == "nothing" then + -- Use on yourself if pointing nothing + selected_objects[name] = user + else + -- Unsupported pointed thing + return + end + + local sel = editor_formspec_selindex[name] + local val + if selected_objects[name] and selected_objects[name]:get_properties() then + local props = selected_objects[name]:get_properties() + local keys = property_formspec_data[name] + if property_formspec_index[name] and props then + local key = keys[property_formspec_index[name]] + val = prop_to_string(props[key]) + end + end + + editor_formspec(name, selected_objects[name], val, sel) + end + end, +}) + +local ent_parent = {} +local ent_child = {} +local DEFAULT_ATTACH_OFFSET_Y = 11 + +local attacher_config = function(itemstack, user, pointed_thing) + if not (user and user:is_player()) then + return + end + if pointed_thing.type == "object" then + return + end + local name = user:get_player_name() + local ctrl = user:get_player_control() + local meta = itemstack:get_meta() + if ctrl.aux1 then + local rot_x = meta:get_float("rot_x") + if ctrl.sneak then + rot_x = rot_x - math.pi/8 + else + rot_x = rot_x + math.pi/8 + end + if rot_x > 6.2 then + rot_x = 0 + elseif rot_x < 0 then + rot_x = math.pi * (15/8) + end + core.chat_send_player(name, S("rotation=@1", core.pos_to_string({x=rot_x,y=0,z=0}))) + meta:set_float("rot_x", rot_x) + else + local pos_y + if meta:contains("pos_y") then + pos_y = meta:get_int("pos_y") + else + pos_y = DEFAULT_ATTACH_OFFSET_Y + end + if ctrl.sneak then + pos_y = pos_y - 1 + else + pos_y = pos_y + 1 + end + core.chat_send_player(name, S("position=@1", core.pos_to_string({x=0,y=pos_y,z=0}))) + meta:set_int("pos_y", pos_y) + end + return itemstack +end + +core.register_tool("testtools:object_attacher", { + description = S("Object Attacher") .."\n".. + S("Attach object to another") .."\n".. + S("Punch objects to first select parent object, then the child object to attach") .."\n".. + S("Punch air to select yourself") .."\n".. + S("Place: Incease attachment Y offset") .."\n".. + S("Sneak+Place: Decease attachment Y offset") .."\n".. + S("Aux1+Place: Incease attachment rotation") .."\n".. + S("Aux1+Sneak+Place: Decrease attachment rotation"), + inventory_image = "testtools_object_attacher.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_place = attacher_config, + on_secondary_use = attacher_config, + on_use = function(itemstack, user, pointed_thing) + if user and user:is_player() then + local name = user:get_player_name() + local selected_object + if pointed_thing.type == "object" then + selected_object = pointed_thing.ref + elseif pointed_thing.type == "nothing" then + selected_object = user + else + return + end + local ctrl = user:get_player_control() + if ctrl.sneak then + if selected_object:get_attach() then + selected_object:set_detach() + core.chat_send_player(name, S("Object detached!")) + else + core.chat_send_player(name, S("Object is not attached!")) + end + return + end + local parent = ent_parent[name] + local child = ent_child[name] + local ename = S("") + if not parent then + parent = selected_object + ent_parent[name] = parent + elseif not child then + child = selected_object + ent_child[name] = child + end + local entity = selected_object:get_luaentity() + if entity then + ename = entity.name + elseif selected_object:is_player() then + ename = selected_object:get_player_name() + end + if selected_object == parent then + core.chat_send_player(name, S("Parent object selected: @1", ename)) + elseif selected_object == child then + core.chat_send_player(name, S("Child object selected: @1", ename)) + end + if parent and child then + if parent == child then + core.chat_send_player(name, S("Can't attach an object to itself!")) + ent_parent[name] = nil + ent_child[name] = nil + return + end + local meta = itemstack:get_meta() + local y + if meta:contains("pos_y") then + y = meta:get_int("pos_y") + else + y = DEFAULT_ATTACH_OFFSET_Y + end + local rx = meta:get_float("rot_x") or 0 + local offset = {x=0,y=y,z=0} + local angle = {x=rx,y=0,z=0} + child:set_attach(parent, "", offset, angle) + local check_parent = child:get_attach() + if check_parent then + core.chat_send_player(name, S("Object attached! position=@1, rotation=@2", + core.pos_to_string(offset), core.pos_to_string(angle))) + else + core.chat_send_player(name, S("Attachment failed!")) + end + ent_parent[name] = nil + ent_child[name] = nil + end + end + end, +}) + +local function print_object(obj) + if obj:is_player() then + return "player '"..obj:get_player_name().."'" + elseif obj:get_luaentity() then + return "LuaEntity '"..obj:get_luaentity().name.."'" + else + return "object" + end +end + +core.register_tool("testtools:children_getter", { + description = S("Children Getter") .."\n".. + S("Shows list of objects attached to object") .."\n".. + S("Punch object to show its 'children'") .."\n".. + S("Punch air to show your own 'children'"), + inventory_image = "testtools_children_getter.png", + groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, + on_use = function(itemstack, user, pointed_thing) + if user and user:is_player() then + local name = user:get_player_name() + local selected_object + local self_name + if pointed_thing.type == "object" then + selected_object = pointed_thing.ref + elseif pointed_thing.type == "nothing" then + selected_object = user + else + return + end + self_name = print_object(selected_object) + local children = selected_object:get_children() + local ret = "" + for c=1, #children do + ret = ret .. "* " .. print_object(children[c]) + if c < #children then + ret = ret .. "\n" + end + end + if ret == "" then + ret = S("No children attached to @1.", self_name) + else + ret = S("Children of @1:", self_name) .. "\n" .. ret + end + core.chat_send_player(user:get_player_name(), ret) + end + end, +}) + +-- Use loadstring to parse param as a Lua value +local function use_loadstring(param, player) + -- For security reasons, require 'server' priv, just in case + -- someone is actually crazy enough to run this on a public server. + local privs = core.get_player_privs(player:get_player_name()) + if not privs.server then + return false, "You need 'server' privilege to change object properties!" + end + if not param then + return false, "Failed: parameter is nil" + end + --[[ DANGER ZONE ]] + -- Interpret string as Lua value + local func, errormsg = loadstring("return (" .. param .. ")") + if not func then + return false, "Failed: " .. errormsg + end + + -- Apply sandbox here using setfenv + setfenv(func, {}) + + -- Run it + local good, errOrResult = pcall(func) + if not good then + -- A Lua error was thrown + return false, "Failed: " .. errOrResult + end + + -- errOrResult will be the value + return true, errOrResult +end + +-- Item Meta Editor + Node Meta Editor +local node_meta_posses = {} +local meta_latest_keylist = {} + +local function show_meta_formspec(user, metatype, pos_or_item, key, value, keylist) + local textlist + if keylist then + textlist = "textlist[0,0.5;2.5,6.5;keylist;"..keylist.."]" + else + textlist = "" + end + + local form = "size[15,9]".. + "label[0,0;"..F(S("Current keys:")).."]".. + textlist.. + "field[3,0.5;12,1;key;"..F(S("Key"))..";"..F(key).."]".. + "textarea[3,1.5;12,6;value;"..F(S("Value (use empty value to delete key)"))..";"..F(value).."]".. + "button[4,8;3,1;set;"..F(S("Set value")).."]" + + local extra_label + local formname + if metatype == "node" then + formname = "testtools:node_meta_editor" + extra_label = S("pos = @1", core.pos_to_string(pos_or_item)) + else + formname = "testtools:item_meta_editor" + extra_label = S("item = @1", pos_or_item:get_name()) + end + form = form .. "label[0,7.2;"..F(extra_label).."]" + + core.show_formspec(user:get_player_name(), formname, form) +end + +local function get_meta_keylist(meta, playername, escaped) + local keys = {} + local ekeys = {} + local mtable = meta:to_table() + for k,_ in pairs(mtable.fields) do + table.insert(keys, k) + if escaped then + table.insert(ekeys, F(k)) + else + table.insert(ekeys, k) + end + end + if playername then + meta_latest_keylist[playername] = keys + end + return table.concat(ekeys, ",") +end + +core.register_tool("testtools:node_meta_editor", { + description = S("Node Meta Editor") .. "\n" .. + S("Place: Edit node metadata"), + inventory_image = "testtools_node_meta_editor.png", + groups = { testtool = 1, disable_repair = 1 }, + on_place = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + if not user:is_player() then + return itemstack + end + local pos = pointed_thing.under + node_meta_posses[user:get_player_name()] = pos + local meta = core.get_meta(pos) + local inv = meta:get_inventory() + show_meta_formspec(user, "node", pos, "", "", get_meta_keylist(meta, user:get_player_name(), true)) + return itemstack + end, +}) + +local function get_item_next_to_wielded_item(player) + local inv = player:get_inventory() + local wield = player:get_wield_index() + local itemstack = inv:get_stack("main", wield+1) + return itemstack +end +local function set_item_next_to_wielded_item(player, itemstack) + local inv = player:get_inventory() + local wield = player:get_wield_index() + inv:set_stack("main", wield+1, itemstack) +end + +local function use_item_meta_editor(itemstack, user, pointed_thing) + if not user:is_player() then + return itemstack + end + local item_to_edit = get_item_next_to_wielded_item(user) + if item_to_edit:is_empty() then + core.chat_send_player(user:get_player_name(), S("Place an item next to the Item Meta Editor in your inventory first!")) + return itemstack + end + local meta = item_to_edit:get_meta() + show_meta_formspec(user, "item", item_to_edit, "", "", get_meta_keylist(meta, user:get_player_name(), true)) + return itemstack +end + +core.register_tool("testtools:item_meta_editor", { + description = S("Item Meta Editor") .. "\n" .. + S("Punch/Place: Edit item metadata of item in the next inventory slot"), + inventory_image = "testtools_item_meta_editor.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = use_item_meta_editor, + on_secondary_use = use_item_meta_editor, + on_place = use_item_meta_editor, +}) + +core.register_on_player_receive_fields(function(player, formname, fields) + if not (player and player:is_player()) then + return + end + if formname == "testtools:entity_list" then + local name = player:get_player_name() + if fields.entity_list then + local expl = core.explode_textlist_event(fields.entity_list) + if expl.type == "DCL" then + local pos = vector.add(player:get_pos(), {x=0,y=1,z=0}) + selections[name] = expl.index + core.add_entity(pos, get_entity_list()[expl.index]) + return + elseif expl.type == "CHG" then + selections[name] = expl.index + return + end + elseif fields.spawn and selections[name] then + local pos = vector.add(player:get_pos(), {x=0,y=1,z=0}) + core.add_entity(pos, get_entity_list()[selections[name]]) + return + end + elseif formname == "testtools:object_editor" then + local name = player:get_player_name() + if fields.object_props then + local expl = core.explode_textlist_event(fields.object_props) + if expl.type == "DCL" or expl.type == "CHG" then + property_formspec_index[name] = expl.index + + local props = selected_objects[name]:get_properties() + local keys = property_formspec_data[name] + if (not property_formspec_index[name]) or (not props) then + return + end + local key = keys[property_formspec_index[name]] + editor_formspec_selindex[name] = expl.index + editor_formspec(name, selected_objects[name], prop_to_string(props[key]), expl.index) + return + end + end + if fields.key_enter_field == "value" or fields.submit then + local props = selected_objects[name]:get_properties() + local keys = property_formspec_data[name] + if (not property_formspec_index[name]) or (not props) then + return + end + local key = keys[property_formspec_index[name]] + if not key then + return + end + local success, str = use_loadstring(fields.value, player) + if success then + props[key] = str + else + core.chat_send_player(name, str) + return + end + selected_objects[name]:set_properties(props) + local sel = editor_formspec_selindex[name] + editor_formspec(name, selected_objects[name], prop_to_string(props[key]), sel) + return + end + elseif formname == "testtools:node_setter" then + local playername = player:get_player_name() + local witem = player:get_wielded_item() + if witem:get_name() == "testtools:node_setter" then + if fields.nodename and fields.param2 then + local param2 = tonumber(fields.param2) + if not param2 then + return + end + local meta = witem:get_meta() + meta:set_string("node", fields.nodename) + meta:set_int("node_param2", param2) + player:set_wielded_item(witem) + end + end + elseif formname == "testtools:node_meta_editor" or formname == "testtools:item_meta_editor" then + local name = player:get_player_name() + local metatype + local pos_or_item + if formname == "testtools:node_meta_editor" then + metatype = "node" + pos_or_item = node_meta_posses[name] + else + metatype = "item" + pos_or_item = get_item_next_to_wielded_item(player) + end + if fields.keylist then + local evnt = core.explode_textlist_event(fields.keylist) + if evnt.type == "DCL" or evnt.type == "CHG" then + local keylist_table = meta_latest_keylist[name] + if metatype == "node" and not pos_or_item then + return + end + local meta + if metatype == "node" then + meta = core.get_meta(pos_or_item) + else + meta = pos_or_item:get_meta() + end + if not keylist_table then + return + end + if #keylist_table == 0 then + return + end + local key = keylist_table[evnt.index] + local value = meta:get_string(key) + local keylist_escaped = {} + for k,v in pairs(keylist_table) do + keylist_escaped[k] = F(v) + end + local keylist = table.concat(keylist_escaped, ",") + show_meta_formspec(player, metatype, pos_or_item, key, value, keylist) + return + end + elseif fields.key and fields.key ~= "" and fields.value then + if metatype == "node" and not pos_or_item then + return + end + local meta + if metatype == "node" then + meta = core.get_meta(pos_or_item) + elseif metatype == "item" then + if pos_or_item:is_empty() then + return + end + meta = pos_or_item:get_meta() + end + if fields.set then + meta:set_string(fields.key, fields.value) + if metatype == "item" then + set_item_next_to_wielded_item(player, pos_or_item) + end + show_meta_formspec(player, metatype, pos_or_item, fields.key, fields.value, + get_meta_keylist(meta, name, true)) + end + return + end + end +end) + +core.register_on_leaveplayer(function(player) + local name = player:get_player_name() + meta_latest_keylist[name] = nil + node_meta_posses[name] = nil +end) + +-- Pointing Staffs + +core.register_tool("testtools:blocked_pointing_staff", { + description = S("Blocked Pointing Staff").."\n".. + S("Can point the Blocking Pointable Node/Object and ".. + "the Pointable Node/Object is point blocking."), + inventory_image = "testtools_blocked_pointing_staff.png", + pointabilities = { + nodes = { + ["testnodes:blocking_pointable"] = true, + ["group:pointable_test"] = "blocking" + }, + objects = { + ["testentities:blocking_pointable"] = true, + ["group:pointable_test"] = "blocking" + } + } +}) + +core.register_tool("testtools:ultimate_pointing_staff", { + description = S("Ultimate Pointing Staff").."\n".. + S("Can point all pointable test nodes, objects and liquids."), + inventory_image = "testtools_ultimate_pointing_staff.png", + liquids_pointable = true, + pointabilities = { + nodes = { + ["group:blocking_pointable_test"] = true, + ["group:pointable_test"] = true, + ["testnodes:not_pointable"] = true + }, + objects = { + ["group:blocking_pointable_test"] = true, + ["group:pointable_test"] = true, + ["testentities:not_pointable"] = true + } + } +}) diff --git a/data/games/garage/mods/testtools/light.lua b/data/games/garage/mods/testtools/light.lua new file mode 100644 index 0000000..861a358 --- /dev/null +++ b/data/games/garage/mods/testtools/light.lua @@ -0,0 +1,37 @@ + +local S = core.get_translator("testtools") + +local function get_func(is_place) + return function(itemstack, user, pointed_thing) + local pos + if is_place then + pos = pointed_thing.under + else + pos = pointed_thing.above + end + if pointed_thing.type ~= "node" or not pos then + return + end + + local node = core.get_node(pos) + local pstr = core.pos_to_string(pos) + local time = core.get_timeofday() + local sunlight = core.get_natural_light(pos) + local artificial = core.get_artificial_light(node.param1) + local message = ("pos=%s | param1=0x%02x | " .. + "sunlight=%d | artificial=%d | timeofday=%.5f" ) + :format(pstr, node.param1, sunlight, artificial, time) + core.chat_send_player(user:get_player_name(), message) + end +end + +core.register_tool("testtools:lighttool", { + description = S("Light Tool") .. "\n" .. + S("Show light values of node") .. "\n" .. + S("Punch: Light of node above touched node") .. "\n" .. + S("Place: Light of touched node itself"), + inventory_image = "testtools_lighttool.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = get_func(false), + on_place = get_func(true), +}) diff --git a/data/games/garage/mods/testtools/mod.conf b/data/games/garage/mods/testtools/mod.conf new file mode 100644 index 0000000..cde1b26 --- /dev/null +++ b/data/games/garage/mods/testtools/mod.conf @@ -0,0 +1,2 @@ +name = testtools +description = Some tools to directly manipulate nodes and entities. Great for development and testing diff --git a/data/games/garage/mods/testtools/node_box_visualizer.lua b/data/games/garage/mods/testtools/node_box_visualizer.lua new file mode 100644 index 0000000..444f939 --- /dev/null +++ b/data/games/garage/mods/testtools/node_box_visualizer.lua @@ -0,0 +1,79 @@ +local S = core.get_translator("testtools") + +core.register_entity("testtools:visual_box", { + initial_properties = { + visual = "cube", + textures = { + "blank.png", "blank.png", "blank.png", + "blank.png", "blank.png", "blank.png", + }, + use_texture_alpha = true, + physical = false, + pointable = false, + static_save = false, + }, + + on_activate = function(self) + self.timestamp = core.get_us_time() + 5000000 + end, + + on_step = function(self) + if core.get_us_time() >= self.timestamp then + self.object:remove() + end + end, +}) + +local BOX_TYPES = {"node_box", "collision_box", "selection_box"} +local DEFAULT_BOX_TYPE = "selection_box" + +local function visualizer_on_use(itemstack, user, pointed_thing) + if pointed_thing.type ~= "node" then + return + end + + local meta = itemstack:get_meta() + local box_type = meta:get("box_type") or DEFAULT_BOX_TYPE + + local result = core.get_node_boxes(box_type, pointed_thing.under) + local t = "testtools_visual_" .. box_type .. ".png" + + for _, box in ipairs(result) do + local box_min = pointed_thing.under + vector.new(box[1], box[2], box[3]) + local box_max = pointed_thing.under + vector.new(box[4], box[5], box[6]) + local box_center = (box_min + box_max) / 2 + local obj = core.add_entity(box_center, "testtools:visual_box") + if not obj then + break + end + obj:set_properties({ + textures = {t, t, t, t, t, t}, + -- Add a small offset to avoid Z-fighting. + visual_size = vector.add(box_max - box_min, 0.01), + }) + end +end + +local function visualizer_on_place(itemstack, placer, pointed_thing) + local meta = itemstack:get_meta() + local prev_value = meta:get("box_type") or DEFAULT_BOX_TYPE + local prev_index = table.indexof(BOX_TYPES, prev_value) + assert(prev_index ~= -1) + + local new_value = BOX_TYPES[(prev_index % #BOX_TYPES) + 1] + meta:set_string("box_type", new_value) + core.chat_send_player(placer:get_player_name(), S("[Node Box Visualizer] box_type = @1", new_value)) + + return itemstack +end + +core.register_tool("testtools:node_box_visualizer", { + description = S("Node Box Visualizer") .. "\n" .. + S("Punch: Show node/collision/selection boxes of the pointed node") .. "\n" .. + S("Place: Change selected box type (default: selection box)"), + inventory_image = "testtools_node_box_visualizer.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = visualizer_on_use, + on_place = visualizer_on_place, + on_secondary_use = visualizer_on_place, +}) diff --git a/data/games/garage/mods/testtools/particles.lua b/data/games/garage/mods/testtools/particles.lua new file mode 100644 index 0000000..17f4f5c --- /dev/null +++ b/data/games/garage/mods/testtools/particles.lua @@ -0,0 +1,36 @@ +core.register_tool("testtools:particle_spawner", { + description = "Particle Spawner".."\n".. + "Punch: Spawn random test particle", + inventory_image = "testtools_particle_spawner.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = function(itemstack, user, pointed_thing) + local pos = core.get_pointed_thing_position(pointed_thing, true) + if pos == nil then + if user then + pos = user:get_pos() + end + end + pos = vector.add(pos, {x=0, y=0.5, z=0}) + local tex, anim + if math.random(0, 1) == 0 then + tex = "testtools_particle_sheet.png" + anim = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5} + else + tex = "testtools_particle_vertical.png" + anim = {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3} + end + + core.add_particle({ + pos = pos, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0.04, z=0}, + expirationtime = 6, + collisiondetection = true, + texture = tex, + animation = anim, + size = 4, + glow = math.random(0, 5), + }) + end, +}) + diff --git a/data/games/garage/mods/testtools/privatizer.lua b/data/games/garage/mods/testtools/privatizer.lua new file mode 100644 index 0000000..54c3508 --- /dev/null +++ b/data/games/garage/mods/testtools/privatizer.lua @@ -0,0 +1,29 @@ +core.register_tool("testtools:privatizer", { + description = "Node Meta Privatizer".."\n".. + "Punch: Marks 'infotext' and 'formspec' meta fields of chest as private", + inventory_image = "testtools_privatizer.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "node" then + local node = core.get_node(pointed_thing.under) + if core.get_item_group(node.name, "meta_is_privatizable") == 1 then + local p = pointed_thing.under + core.log("action", "[testtools] Privatizer used at "..core.pos_to_string(p)) + core.get_meta(p):mark_as_private({"infotext", "formspec"}) + if user and user:is_player() then + core.chat_send_player(user:get_player_name(), "Node metadata (infotext, formspec) set private!") + end + return + elseif node.name == "chest_of_everything:chest" then + if user and user:is_player() then + core.chat_send_player(user:get_player_name(), "Privatizer can't be used on the Chest of Everything. Use it on a normal chest.") + end + return + end + end + if user and user:is_player() then + core.chat_send_player(user:get_player_name(), "Privatizer can only be used on chest!") + end + end, +}) + diff --git a/data/games/garage/mods/testtools/textures/testtools_blocked_pointing_staff.png b/data/games/garage/mods/testtools/textures/testtools_blocked_pointing_staff.png new file mode 100644 index 0000000..aa1cdb9 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_blocked_pointing_staff.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_branding_iron.png b/data/games/garage/mods/testtools/textures/testtools_branding_iron.png new file mode 100644 index 0000000..9ac2bb7 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_branding_iron.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_children_getter.png b/data/games/garage/mods/testtools/textures/testtools_children_getter.png new file mode 100644 index 0000000..763a1e3 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_children_getter.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_entity_rotator.png b/data/games/garage/mods/testtools/textures/testtools_entity_rotator.png new file mode 100644 index 0000000..17ebb2d Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_entity_rotator.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_entity_scaler.png b/data/games/garage/mods/testtools/textures/testtools_entity_scaler.png new file mode 100644 index 0000000..4909c25 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_entity_scaler.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_entity_spawner.png b/data/games/garage/mods/testtools/textures/testtools_entity_spawner.png new file mode 100644 index 0000000..6199e01 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_entity_spawner.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_falling_node_tool.png b/data/games/garage/mods/testtools/textures/testtools_falling_node_tool.png new file mode 100644 index 0000000..30099a7 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_falling_node_tool.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_item_meta_editor.png b/data/games/garage/mods/testtools/textures/testtools_item_meta_editor.png new file mode 100644 index 0000000..5cebb0a Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_item_meta_editor.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_lighttool.png b/data/games/garage/mods/testtools/textures/testtools_lighttool.png new file mode 100644 index 0000000..470adba Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_lighttool.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_node_box_visualizer.png b/data/games/garage/mods/testtools/textures/testtools_node_box_visualizer.png new file mode 100644 index 0000000..1b16aa7 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_node_box_visualizer.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_node_meta_editor.png b/data/games/garage/mods/testtools/textures/testtools_node_meta_editor.png new file mode 100644 index 0000000..89eafd6 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_node_meta_editor.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_node_setter.png b/data/games/garage/mods/testtools/textures/testtools_node_setter.png new file mode 100644 index 0000000..8599438 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_node_setter.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_object_attacher.png b/data/games/garage/mods/testtools/textures/testtools_object_attacher.png new file mode 100644 index 0000000..4d9bf6f Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_object_attacher.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_object_editor.png b/data/games/garage/mods/testtools/textures/testtools_object_editor.png new file mode 100644 index 0000000..d1ce9ce Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_object_editor.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_object_mover.png b/data/games/garage/mods/testtools/textures/testtools_object_mover.png new file mode 100644 index 0000000..8b14e9f Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_object_mover.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_param2tool.png b/data/games/garage/mods/testtools/textures/testtools_param2tool.png new file mode 100644 index 0000000..dbc6635 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_param2tool.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_particle_sheet.png b/data/games/garage/mods/testtools/textures/testtools_particle_sheet.png new file mode 100644 index 0000000..6d70394 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_particle_sheet.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_particle_spawner.png b/data/games/garage/mods/testtools/textures/testtools_particle_spawner.png new file mode 100644 index 0000000..5bd276b Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_particle_spawner.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_particle_vertical.png b/data/games/garage/mods/testtools/textures/testtools_particle_vertical.png new file mode 100644 index 0000000..0320b75 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_particle_vertical.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_privatizer.png b/data/games/garage/mods/testtools/textures/testtools_privatizer.png new file mode 100644 index 0000000..dd82fba Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_privatizer.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_remover.png b/data/games/garage/mods/testtools/textures/testtools_remover.png new file mode 100644 index 0000000..73f14cd Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_remover.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_ultimate_pointing_staff.png b/data/games/garage/mods/testtools/textures/testtools_ultimate_pointing_staff.png new file mode 100644 index 0000000..e4d80fb Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_ultimate_pointing_staff.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_visual_collision_box.png b/data/games/garage/mods/testtools/textures/testtools_visual_collision_box.png new file mode 100644 index 0000000..c8ab97c Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_visual_collision_box.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_visual_node_box.png b/data/games/garage/mods/testtools/textures/testtools_visual_node_box.png new file mode 100644 index 0000000..dbbb8d9 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_visual_node_box.png differ diff --git a/data/games/garage/mods/testtools/textures/testtools_visual_selection_box.png b/data/games/garage/mods/testtools/textures/testtools_visual_selection_box.png new file mode 100644 index 0000000..1f4bbf3 Binary files /dev/null and b/data/games/garage/mods/testtools/textures/testtools_visual_selection_box.png differ diff --git a/data/games/garage/mods/testtranslations/init.lua b/data/games/garage/mods/testtranslations/init.lua new file mode 100644 index 0000000..a2de998 --- /dev/null +++ b/data/games/garage/mods/testtranslations/init.lua @@ -0,0 +1,26 @@ +local S, NS = core.get_translator("testtranslations") + +local function send_compare(name, text) + core.chat_send_player(name, ("%s | %s | %s"):format( + core.get_translated_string("", text), text, core.get_translated_string("fr", text))) +end + +core.register_chatcommand("testtranslations", { + params = "", + description = "Test translations", + privs = {}, + func = function(name, param) + core.chat_send_player(name, "Please ensure your locale is set to \"fr\"") + core.chat_send_player(name, "Untranslated | Client-side translation | Server-side translation (fr)") + send_compare(name, S("Testing .tr files: untranslated")) + send_compare(name, S("Testing .po files: untranslated")) + send_compare(name, S("Testing .mo files: untranslated")) + send_compare(name, S("Testing fuzzy .po entry: untranslated (expected)")) + send_compare(name, core.translate("translation_po", "Testing .po without context: untranslated")) + send_compare(name, core.translate("translation_mo", "Testing .mo without context: untranslated")) + for i = 0,4 do + send_compare(name, NS("@1: .po singular", "@1: .po plural", i, tostring(i))) + send_compare(name, NS("@1: .mo singular", "@1: .mo plural", i, tostring(i))) + end + end +}) diff --git a/data/games/garage/mods/testtranslations/locale/testtranslations.fr.po b/data/games/garage/mods/testtranslations/locale/testtranslations.fr.po new file mode 100644 index 0000000..2bcc6c7 --- /dev/null +++ b/data/games/garage/mods/testtranslations/locale/testtranslations.fr.po @@ -0,0 +1,9 @@ +# Dummy entry. This is a test to make sure that a parser error is not thrown +# if the following line is msgctxt. +msgctxt "testtranslations" +msgid "Dummy entry" +msgstr "Dummy result" + +# Used for translating the mod title +msgid "Test translations" +msgstr "Test translations (French)" diff --git a/data/games/garage/mods/testtranslations/locale/translation_mo.fr.mo b/data/games/garage/mods/testtranslations/locale/translation_mo.fr.mo new file mode 100644 index 0000000..0e7190d Binary files /dev/null and b/data/games/garage/mods/testtranslations/locale/translation_mo.fr.mo differ diff --git a/data/games/garage/mods/testtranslations/locale/translation_po.fr.po b/data/games/garage/mods/testtranslations/locale/translation_po.fr.po new file mode 100644 index 0000000..5aefc0f --- /dev/null +++ b/data/games/garage/mods/testtranslations/locale/translation_po.fr.po @@ -0,0 +1,22 @@ +# Test Plural-Forms parsing +msgid "" +msgstr "" +"Plural-Forms: nplurals=2; plural= (n-1+1)<=1 ? n||1?0:1 : 1?(!!2):2;" + +msgctxt "testtranslations" +msgid "Testing .po files: untranslated" +msgstr "Testing .po files: translated" + +msgctxt "testtranslations" +msgid "@1: .po singular" +msgid_plural "@1: .po plural" +msgstr[0] "@1: .po 0 and 1 (French singular)" +msgstr[1] "@1: .po >1 (French plural)" + +#, foo bar fuzzy +msgctxt "testtranslations" +msgid "Testing fuzzy .po entry: untranslated (expected)" +msgstr "Testing fuzzy .po entry: translated (wrong)" + +msgid "Testing .po without context: untranslated" +msgstr "Testing .po without context: translated" diff --git a/data/games/garage/mods/testtranslations/locale/translation_tr.fr.tr b/data/games/garage/mods/testtranslations/locale/translation_tr.fr.tr new file mode 100644 index 0000000..b9ac66a --- /dev/null +++ b/data/games/garage/mods/testtranslations/locale/translation_tr.fr.tr @@ -0,0 +1,2 @@ +# textdomain: testtranslations +Testing .tr files: untranslated=Testing .tr files: translated diff --git a/data/games/garage/mods/testtranslations/mod.conf b/data/games/garage/mods/testtranslations/mod.conf new file mode 100644 index 0000000..1fc09cf --- /dev/null +++ b/data/games/garage/mods/testtranslations/mod.conf @@ -0,0 +1,3 @@ +name = testtranslations +title = Test translations +description = Test mod to test translations. diff --git a/data/games/garage/mods/testtranslations/test_locale/readme.txt b/data/games/garage/mods/testtranslations/test_locale/readme.txt new file mode 100644 index 0000000..7a2ed43 --- /dev/null +++ b/data/games/garage/mods/testtranslations/test_locale/readme.txt @@ -0,0 +1,4 @@ +The translation files in this directory intentionally include errors (which +would be reported when someone starts the devtest game in the de locale). This +allows the unittest to check that the translation file reader also handles +files that include errors. diff --git a/data/games/garage/mods/testtranslations/test_locale/translation_mo.de.mo b/data/games/garage/mods/testtranslations/test_locale/translation_mo.de.mo new file mode 100644 index 0000000..ffe05cd Binary files /dev/null and b/data/games/garage/mods/testtranslations/test_locale/translation_mo.de.mo differ diff --git a/data/games/garage/mods/testtranslations/test_locale/translation_po.de.po b/data/games/garage/mods/testtranslations/test_locale/translation_po.de.po new file mode 100644 index 0000000..9a64805 --- /dev/null +++ b/data/games/garage/mods/testtranslations/test_locale/translation_po.de.po @@ -0,0 +1,42 @@ +# This file is used by the C++ unittest for testing the parser +msgid "" +msgstr "\n\n\n" +"Plural-Forms: nplurals=2; plural=n!=1;" +"\n\n\n" + +msgid "foo" + msgstr "bar" + +msgid "Untranslated" +msgstr "" + +#, fuzzy +msgid "Fuzzy entry" +msgstr "Wrong" + +msgid "Multi\\""line\n" +"string" +msgstr "Multi\\\"" "li\\ne\nresult" + +msgctxt "Something" in "between" +msgctxt "String does not end +msgstr "Lost string" +msgid "Wrong order" + +msgid "Singular form" +msgid_plural "Plural form" +msgstr[0] "Singular result" +msgstr[1] "Plural result" + +msgid "Not enough value" +msgid_plural "Not enough values" +msgstr[0] "Result" + +msgid "Partial translation" +msgid_plural "Partial translations" +msgstr[0] "Partially translated" +msgstr[1] "" + +msgctxt "context" +msgid "With context" +msgstr "Has context" diff --git a/data/games/garage/mods/testtranslations/translation_mo.de.po b/data/games/garage/mods/testtranslations/translation_mo.de.po new file mode 100644 index 0000000..c3f22c4 --- /dev/null +++ b/data/games/garage/mods/testtranslations/translation_mo.de.po @@ -0,0 +1,26 @@ +msgid "" +msgstr "Plural-Forms: nplurals=2; plural= n != 1;" + +msgctxt "context" +msgid "With context" +msgstr "Has context" + +msgctxt "context" +msgid "Singular form" +msgid_plural "Plural form" +msgstr[0] "Singular result" +msgstr[1] "Plural result" + +# Replace plural form delimiter in the msgstr +msgid "Corrupt singular" +msgid_plural "Corrupt plural" +msgstr[0] "Corrupt singular result" +msgstr[1] "Corrupt plural result" + +# Replace terminating NUL in the MO file +msgid "Corrupt entry" +msgstr "Corrupted result" + +# Change the address of this entry to something invalid +msgid "Removed entry" +msgstr "Removed result" diff --git a/data/games/garage/mods/testtranslations/translation_mo.fr.po b/data/games/garage/mods/testtranslations/translation_mo.fr.po new file mode 100644 index 0000000..e6cf6d6 --- /dev/null +++ b/data/games/garage/mods/testtranslations/translation_mo.fr.po @@ -0,0 +1,18 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n>1;" + +msgctxt "testtranslations" +msgid "Testing .mo files: untranslated" +msgstr "Testing .mo files: translated" + +msgid "Testing .mo without context: untranslated" +msgstr "Testing .mo without context: translated" + +msgctxt "testtranslations" +msgid "@1: .mo singular" +msgid_plural "@1: .mo plural" +msgstr[0] "@1: .mo 0 and 1 (French singular)" +msgstr[1] "@1: .mo >1 (French plural)" diff --git a/data/games/garage/mods/tiled/init.lua b/data/games/garage/mods/tiled/init.lua new file mode 100644 index 0000000..51e1fda --- /dev/null +++ b/data/games/garage/mods/tiled/init.lua @@ -0,0 +1,50 @@ +local align_help = "Texture spans over a space of 8×8 nodes" +local align_help_n = "Tiles looks the same for every node" + +core.register_node("tiled:tiled", { + description = "Tiled Node (world-aligned)".."\n"..align_help, + tiles = {{ + name = "tiled_tiled.png", + align_style = "world", + scale = 8, + }}, + groups = {cracky=3}, +}) + +core.register_node("tiled:tiled_rooted", { + description = "Tiled 'plantlike_rooted' Node (world-aligned)".."\n".. + "Base node texture spans over a space of 8×8 nodes".."\n".. + "A plantlike thing grows on top", + paramtype = "light", + drawtype = "plantlike_rooted", + tiles = {{ + name = "tiled_tiled.png", + align_style = "world", + scale = 8, + }}, + special_tiles = {"tiled_tiled_node.png"}, + groups = {cracky=3}, +}) + +core.register_node("tiled:tiled_n", { + description = "Tiled Node (node-aligned)".."\n"..align_help_n, + tiles = {{ + name = "tiled_tiled_node.png", + align_style = "node", + }}, + groups = {cracky=3}, +}) + +stairs.register_stair_and_slab("tiled_n", "tiled:tiled_n", + {cracky=3}, + {{name="tiled_tiled_node.png", align_style="node"}}, + "Tiled Stair (node-aligned)".."\n"..align_help_n, + "Tiled Slab (node-aligned)".."\n"..align_help_n) + +stairs.register_stair_and_slab("tiled", "tiled:tiled", + {cracky=3}, + {{name="tiled_tiled.png", align_style="world", scale=8}}, + "Tiled Stair (world-aligned)".."\n"..align_help, + "Tiled Slab (world-aligned)".."\n"..align_help) + + diff --git a/data/games/garage/mods/tiled/mod.conf b/data/games/garage/mods/tiled/mod.conf new file mode 100644 index 0000000..78b19f9 --- /dev/null +++ b/data/games/garage/mods/tiled/mod.conf @@ -0,0 +1,3 @@ +name = tiled +description = Add nodes with a special texture that spans multiple nodes (aka "world-aligned") +depends = stairs diff --git a/data/games/garage/mods/tiled/textures/tiled_tiled.png b/data/games/garage/mods/tiled/textures/tiled_tiled.png new file mode 100644 index 0000000..363a264 Binary files /dev/null and b/data/games/garage/mods/tiled/textures/tiled_tiled.png differ diff --git a/data/games/garage/mods/tiled/textures/tiled_tiled_node.png b/data/games/garage/mods/tiled/textures/tiled_tiled_node.png new file mode 100644 index 0000000..cc69028 Binary files /dev/null and b/data/games/garage/mods/tiled/textures/tiled_tiled_node.png differ diff --git a/data/games/garage/mods/unittests/async_env.lua b/data/games/garage/mods/unittests/async_env.lua new file mode 100644 index 0000000..b00deb3 --- /dev/null +++ b/data/games/garage/mods/unittests/async_env.lua @@ -0,0 +1,209 @@ +-- helper + +core.register_async_dofile(core.get_modpath(core.get_current_modname()) .. + DIR_DELIM .. "inside_async_env.lua") + +local function deepequal(a, b) + if type(a) == "function" then + return type(b) == "function" + elseif type(a) ~= "table" then + return a == b + elseif type(b) ~= "table" then + return false + end + for k, v in pairs(a) do + if not deepequal(v, b[k]) then + return false + end + end + for k, v in pairs(b) do + if not deepequal(a[k], v) then + return false + end + end + return true +end + +-- Object Passing / Serialization + +local test_object = { + name = "stairs:stair_glass", + type = "node", + groups = {oddly_breakable_by_hand = 3, cracky = 3, stair = 1}, + description = "Glass Stair", + sounds = { + dig = {name = "default_glass_footstep", gain = 0.5}, + footstep = {name = "default_glass_footstep", gain = 0.3}, + dug = {name = "default_break_glass", gain = 1} + }, + node_box = { + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + {-0.5, 0, 0, 0.5, 0.5, 0.5} + }, + type = "fixed" + }, + tiles = { + {name = "stairs_glass_split.png", backface_culling = true}, + {name = "default_glass.png", backface_culling = true}, + {name = "stairs_glass_stairside.png^[transformFX", backface_culling = true} + }, + on_place = function(itemstack, placer) + return core.is_player(placer) + end, + sunlight_propagates = true, + is_ground_content = false, + pos = vector.new(-1, -2, -3), +} + +local function test_object_passing() + local tmp = core.serialize_roundtrip(test_object) + assert(deepequal(test_object, tmp)) + + local circular_key = {"foo", "bar"} + circular_key[circular_key] = true + tmp = core.serialize_roundtrip(circular_key) + assert(tmp[1] == "foo") + assert(tmp[2] == "bar") + assert(tmp[tmp] == true) + + local circular_value = {"foo"} + circular_value[2] = circular_value + tmp = core.serialize_roundtrip(circular_value) + assert(tmp[1] == "foo") + assert(tmp[2] == tmp) + + -- Two-segment cycle + local cycle_seg_1, cycle_seg_2 = {}, {} + cycle_seg_1[1] = cycle_seg_2 + cycle_seg_2[1] = cycle_seg_1 + tmp = core.serialize_roundtrip(cycle_seg_1) + assert(tmp[1][1] == tmp) + + -- Duplicated value without a cycle + local acyclic_dup_holder = {} + tmp = ItemStack("") + acyclic_dup_holder[tmp] = tmp + tmp = core.serialize_roundtrip(acyclic_dup_holder) + for k, v in pairs(tmp) do + assert(rawequal(k, v)) + end +end +unittests.register("test_object_passing", test_object_passing) + +local function test_userdata_passing(_, pos) + -- basic userdata passing + local obj = table.copy(test_object.tiles[1]) + obj.test = ItemStack("default:cobble 99") + local tmp = core.serialize_roundtrip(obj) + assert(type(tmp.test) == "userdata") + assert(obj.test:to_string() == tmp.test:to_string()) + + -- object can't be passed, should error + obj = core.raycast(pos, pos) + assert(not pcall(core.serialize_roundtrip, obj)) + + -- VManip + local vm = core.get_voxel_manip(pos, pos) + local expect = vm:get_node_at(pos) + local vm2 = core.serialize_roundtrip(vm) + assert(deepequal(vm2:get_node_at(pos), expect)) +end +unittests.register("test_userdata_passing", test_userdata_passing, {map=true}) + +-- Asynchronous jobs + +local function test_handle_async(cb) + -- Basic test including mod name tracking and unittests.async_test() + -- which is defined inside_async_env.lua + local func = function(x) + return core.get_last_run_mod(), _VERSION, unittests[x]() + end + local expect = {core.get_last_run_mod(), _VERSION, true} + + core.handle_async(func, function(...) + if not deepequal(expect, {...}) then + return cb("Values did not equal") + end + if core.get_last_run_mod() ~= expect[1] then + return cb("Mod name not tracked correctly") + end + + -- Test passing of nil arguments and return values + core.handle_async(function(a, b) + return a, b + end, function(a, b) + if b ~= 123 then + return cb("Argument went missing") + end + cb() + end, nil, 123) + end, "async_test") +end +unittests.register("test_handle_async", test_handle_async, {async=true}) + +local function test_userdata_passing2(cb, _, pos) + -- VManip: check transfer into other env + local vm = core.get_voxel_manip(pos, pos) + local expect = vm:get_node_at(pos) + + core.handle_async(function(vm_, pos_) + return vm_:get_node_at(pos_) + end, function(ret) + if not deepequal(expect, ret) then + return cb("Node data mismatch (one-way)") + end + + -- VManip: test a roundtrip + core.handle_async(function(vm_) + return vm_ + end, function(vm2) + if not deepequal(expect, vm2:get_node_at(pos)) then + return cb("Node data mismatch (roundtrip)") + end + cb() + end, vm) + end, vm, pos) +end +unittests.register("test_userdata_passing2", test_userdata_passing2, {map=true, async=true}) + +local function test_portable_metatable_override() + assert(pcall(core.register_portable_metatable, "__builtin:vector", vector.metatable), + "Metatable name aliasing throws an error when it should be allowed") + + assert(not pcall(core.register_portable_metatable, "__builtin:vector", {}), + "Illegal metatable overriding allowed") +end +unittests.register("test_portable_metatable_override", test_portable_metatable_override) + +local function test_portable_metatable_registration(cb) + local custom_metatable = {} + core.register_portable_metatable("unittests:custom_metatable", custom_metatable) + + core.handle_async(function(x) + -- unittests.custom_metatable is registered in inside_async_env.lua + return getmetatable(x) == unittests.custom_metatable, x + end, function(metatable_preserved_async, table_after_roundtrip) + if not metatable_preserved_async then + return cb("Custom metatable not preserved (main -> async)") + end + if getmetatable(table_after_roundtrip) ~= custom_metatable then + return cb("Custom metable not preserved (after roundtrip)") + end + cb() + end, setmetatable({}, custom_metatable)) +end +unittests.register("test_portable_metatable_registration", test_portable_metatable_registration, {async=true}) + +local function test_vector_preserve(cb) + local vec = vector.new(1, 2, 3) + core.handle_async(function(x) + return x[1] + end, function(ret) + if ret ~= vec then -- fails if metatable was not preserved + return cb("Vector value mismatch") + end + cb() + end, {vec}) +end +unittests.register("test_async_vector", test_vector_preserve, {async=true}) diff --git a/data/games/garage/mods/unittests/color.lua b/data/games/garage/mods/unittests/color.lua new file mode 100644 index 0000000..8615444 --- /dev/null +++ b/data/games/garage/mods/unittests/color.lua @@ -0,0 +1,17 @@ +local function assert_colors_equal(c1, c2) + if type(c1) == "table" and type(c2) == "table" then + assert(c1.r == c2.r and c1.g == c2.g and c1.b == c2.b and c1.a == c2.a) + else + assert(c1 == c2) + end +end + +local function test_color_conversion() + assert_colors_equal(core.colorspec_to_table("#fff"), {r = 255, g = 255, b = 255, a = 255}) + assert_colors_equal(core.colorspec_to_table(0xFF00FF00), {r = 0, g = 255, b = 0, a = 255}) + assert_colors_equal(core.colorspec_to_table("#00000000"), {r = 0, g = 0, b = 0, a = 0}) + assert_colors_equal(core.colorspec_to_table("green"), {r = 0, g = 128, b = 0, a = 255}) + assert_colors_equal(core.colorspec_to_table("gren"), nil) +end + +unittests.register("test_color_conversion", test_color_conversion) diff --git a/data/games/garage/mods/unittests/content_ids.lua b/data/games/garage/mods/unittests/content_ids.lua new file mode 100644 index 0000000..d2f1e0c --- /dev/null +++ b/data/games/garage/mods/unittests/content_ids.lua @@ -0,0 +1,37 @@ +core.register_alias("unittests:test_content_ids_alias1", "air") +core.register_alias("unittests:test_content_ids_alias2", "~") + +local function test_content_ids() + assert(core.get_content_id("air") == core.CONTENT_AIR) + assert(core.get_content_id("unittests:test_content_ids_alias1") == core.CONTENT_AIR) + assert(core.get_content_id("unknown") == core.CONTENT_UNKNOWN) + assert(core.get_content_id("ignore") == core.CONTENT_IGNORE) + + assert(core.get_name_from_content_id(core.CONTENT_AIR) == "air") + assert(core.get_name_from_content_id(core.CONTENT_UNKNOWN) == "unknown") + assert(core.get_name_from_content_id(core.CONTENT_IGNORE) == "ignore") + + assert(pcall(core.get_content_id, "~") == false) + assert(pcall(core.get_content_id, "unittests:test_content_ids_alias2") == false) + assert(pcall(core.get_content_id) == false) + assert(core.get_name_from_content_id(0xFFFF) == "unknown") + assert(pcall(core.get_name_from_content_id) == false) +end + +-- Run while mod is loading. +test_content_ids() + +-- Run after mods have loaded. +unittests.register("test_content_ids", test_content_ids) + +-- Run in async environment. +local function test_content_ids_async(cb) + local function func(test_func) + local ok, err = pcall(test_func) + if not ok then + return err + end + end + core.handle_async(func, cb, test_content_ids) +end +unittests.register("test_content_ids_async", test_content_ids_async, {async=true}) diff --git a/data/games/garage/mods/unittests/crafting.lua b/data/games/garage/mods/unittests/crafting.lua new file mode 100644 index 0000000..38b1202 --- /dev/null +++ b/data/games/garage/mods/unittests/crafting.lua @@ -0,0 +1,112 @@ +dofile(core.get_modpath(core.get_current_modname()) .. "/crafting_prepare.lua") + +-- Test core.clear_craft function +local function test_clear_craft() + -- Clearing by output + core.register_craft({ + output = "foo", + recipe = {{"bar"}} + }) + core.register_craft({ + output = "foo 4", + recipe = {{"foo", "bar"}} + }) + assert(#core.get_all_craft_recipes("foo") == 2) + core.clear_craft({output="foo"}) + assert(core.get_all_craft_recipes("foo") == nil) + -- Clearing by input + core.register_craft({ + output = "foo 4", + recipe = {{"foo", "bar"}} + }) + assert(#core.get_all_craft_recipes("foo") == 1) + core.clear_craft({recipe={{"foo", "bar"}}}) + assert(core.get_all_craft_recipes("foo") == nil) +end +unittests.register("test_clear_craft", test_clear_craft) + +-- Test core.get_craft_result function +local function test_get_craft_result() + -- normal + local input = { + method = "normal", + width = 2, + items = {"", "unittests:coal_lump", "", "unittests:stick"} + } + core.log("info", "[unittests] torch crafting input: "..dump(input)) + local output, decremented_input = core.get_craft_result(input) + core.log("info", "[unittests] torch crafting output: "..dump(output)) + core.log("info", "[unittests] torch crafting decremented input: "..dump(decremented_input)) + assert(output.item) + core.log("info", "[unittests] torch crafting output.item:to_table(): "..dump(output.item:to_table())) + assert(output.item:get_name() == "unittests:torch") + assert(output.item:get_count() == 4) + + -- fuel + input = { + method = "fuel", + width = 1, + items = {"unittests:coal_lump"} + } + core.log("info", "[unittests] coal fuel input: "..dump(input)) + output, decremented_input = core.get_craft_result(input) + core.log("info", "[unittests] coal fuel output: "..dump(output)) + core.log("info", "[unittests] coal fuel decremented input: "..dump(decremented_input)) + assert(output.time) + assert(output.time > 0) + + -- cooking + input = { + method = "cooking", + width = 1, + items = {"unittests:iron_lump"} + } + core.log("info", "[unittests] iron lump cooking input: "..dump(output)) + output, decremented_input = core.get_craft_result(input) + core.log("info", "[unittests] iron lump cooking output: "..dump(output)) + core.log("info", "[unittests] iron lump cooking decremented input: "..dump(decremented_input)) + assert(output.time) + assert(output.time > 0) + assert(output.item) + core.log("info", "[unittests] iron lump cooking output.item:to_table(): "..dump(output.item:to_table())) + assert(output.item:get_name() == "unittests:steel_ingot") + assert(output.item:get_count() == 1) + + -- tool repair (repairable) + input = { + method = "normal", + width = 2, + -- Using a wear of 60000 + items = {"unittests:repairable_tool 1 60000", "unittests:repairable_tool 1 60000"} + } + core.log("info", "[unittests] repairable tool crafting input: "..dump(input)) + output, decremented_input = core.get_craft_result(input) + core.log("info", "[unittests] repairable tool crafting output: "..dump(output)) + core.log("info", "[unittests] repairable tool crafting decremented input: "..dump(decremented_input)) + assert(output.item) + core.log("info", "[unittests] repairable tool crafting output.item:to_table(): "..dump(output.item:to_table())) + assert(output.item:get_name() == "unittests:repairable_tool") + -- Test the wear value. + -- See src/craftdef.cpp in Luanti source code for the formula. The formula to calculate + -- the value 51187 is: + -- 65536 - ((65536-60000)+(65536-60000)) + floor(additonal_wear * 65536 + 0.5) = 51187 + -- where additional_wear = 0.05 + assert(output.item:get_wear() == 51187) + assert(output.item:get_count() == 1) + + -- failing tool repair (unrepairable) + input = { + method = "normal", + width = 2, + items = {"unittests:unrepairable_tool 1 60000", "unittests:unrepairable_tool 1 60000"} + } + core.log("info", "[unittests] unrepairable tool crafting input: "..dump(input)) + output, decremented_input = core.get_craft_result(input) + core.log("info", "[unittests] unrepairable tool crafting output: "..dump(output)) + core.log("info", "[unittests] unrepairable tool crafting decremented input: "..dump(decremented_input)) + assert(output.item) + core.log("info", "[unittests] unrepairable tool crafting output.item:to_table(): "..dump(output.item:to_table())) + -- unrepairable tool must not yield any output + assert(output.item:is_empty()) +end +unittests.register("test_get_craft_result", test_get_craft_result) diff --git a/data/games/garage/mods/unittests/crafting_prepare.lua b/data/games/garage/mods/unittests/crafting_prepare.lua new file mode 100644 index 0000000..63ad0a5 --- /dev/null +++ b/data/games/garage/mods/unittests/crafting_prepare.lua @@ -0,0 +1,94 @@ +-- Registering some dummy items and recipes for the crafting tests + +core.register_craftitem("unittests:torch", { + description = "Crafting Test Item: Torch", + inventory_image = "unittests_torch.png", + + groups = { dummy = 1 }, +}) +core.register_craftitem("unittests:coal_lump", { + description = "Crafting Test Item: Coal Lump", + inventory_image = "unittests_coal_lump.png", + + groups = { dummy = 1 }, +}) +core.register_craftitem("unittests:stick", { + description = "Crafting Test Item: Stick", + inventory_image = "unittests_stick.png", + + groups = { dummy = 1 }, +}) +core.register_craftitem("unittests:iron_lump", { + description = "Crafting Test Item: Iron Lump", + inventory_image = "unittests_iron_lump.png", + + groups = { dummy = 1 }, +}) +core.register_craftitem("unittests:steel_ingot", { + description = "Crafting Test Item: Steel Ingot", + inventory_image = "unittests_steel_ingot.png", + + groups = { dummy = 1 }, +}) + +-- Use aliases in recipes for more complete testing + +core.register_alias("unittests:steel_ingot_alias", "unittests:steel_ingot") +core.register_alias("unittests:coal_lump_alias", "unittests:coal_lump") +core.register_alias("unittests:iron_lump_alias", "unittests:iron_lump") + +-- Recipes for tests: Normal crafting, cooking and fuel + +core.register_craft({ + output = 'unittests:torch 4', + recipe = { + {'unittests:coal_lump_alias'}, + {'unittests:stick'}, + } +}) + +core.register_craft({ + type = "cooking", + output = "unittests:steel_ingot_alias", + recipe = "unittests:iron_lump_alias", +}) + +core.register_craft({ + type = "fuel", + recipe = "unittests:coal_lump_alias", + burntime = 40, +}) + +-- Test tool repair +core.register_craft({ + type = "toolrepair", + additional_wear = -0.05, +}) + +-- Test the disable_repair=1 group +core.register_tool("unittests:unrepairable_tool", { + description = "Crafting Test Item: Unrepairable Tool", + inventory_image = "unittests_unrepairable_tool.png", + tool_capabilities = { + groupcaps = { + cracky = { + times = {3, 2, 1}, + } + } + }, + groups = { disable_repair = 1, dummy = 1 } +}) + +core.register_tool("unittests:repairable_tool", { + description = "Crafting Test Item: Repairable Tool", + inventory_image = "unittests_repairable_tool.png", + tool_capabilities = { + groupcaps = { + cracky = { + times = {3, 2, 1}, + } + } + }, + + groups = { dummy = 1 }, +}) diff --git a/data/games/garage/mods/unittests/entity.lua b/data/games/garage/mods/unittests/entity.lua new file mode 100644 index 0000000..af91a2a --- /dev/null +++ b/data/games/garage/mods/unittests/entity.lua @@ -0,0 +1,236 @@ +local log = {} + +local function insert_log(...) + log[#log+1] = string.format(...) +end + +local function objref_str(self, ref) + if ref and ref:is_player() then + return "player" + end + return self.object == ref and "self" or tostring(ref) +end + +core.register_entity("unittests:callbacks", { + initial_properties = { + hp_max = 5, + visual = "upright_sprite", + textures = { "unittests_callback.png" }, + static_save = false, + }, + + on_activate = function(self, staticdata, dtime_s) + self.object:set_armor_groups({test = 100}) + assert(self.object:get_hp() == self.initial_properties.hp_max) + insert_log("on_activate(%d)", #staticdata) + end, + on_deactivate = function(self, removal) + insert_log("on_deactivate(%s)", tostring(removal)) + end, + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + insert_log("on_punch(%s, %.1f, %d)", objref_str(self, puncher), + time_from_last_punch, damage) + end, + on_death = function(self, killer) + assert(self.object:get_hp() == 0) + insert_log("on_death(%s)", objref_str(self, killer)) + end, + on_rightclick = function(self, clicker) + insert_log("on_rightclick(%s)", objref_str(self, clicker)) + end, + on_attach_child = function(self, child) + insert_log("on_attach_child(%s)", objref_str(self, child)) + assert(child:get_attach() == self.object) + local ok = false + for _, obj in ipairs(self.object:get_children()) do + if obj == child then + ok = true + end + end + assert(ok, "Child not found in get_children") + end, + on_detach_child = function(self, child) + insert_log("on_detach_child(%s)", objref_str(self, child)) + assert(child:get_attach() == nil) + local ok = true + for _, obj in ipairs(self.object:get_children()) do + if obj == child then + ok = false + end + end + assert(ok, "Former child found in get_children") + end, + on_detach = function(self, parent) + insert_log("on_detach(%s)", objref_str(self, parent)) + assert(self.object:get_attach() == nil) + local ok = true + for _, obj in ipairs(parent:get_children()) do + if obj == self.object then + ok = false + end + end + assert(ok, "Former child found in get_children") + end, + get_staticdata = function(self) + assert(false) + end, +}) + +-- + +local function check_log(expect) + if #expect ~= #log then + error("Log mismatch: " .. core.write_json(log)) + end + for i, s in ipairs(expect) do + if log[i] ~= s then + error("Log mismatch at " .. i .. ": " .. core.write_json(log)) + end + end + log = {} -- clear it for next time +end + +local function test_entity_lifecycle(_, pos) + log = {} + + -- with binary in staticdata + local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def") + assert(obj and obj:is_valid()) + check_log({"on_activate(7)"}) + + obj:set_hp(0) + check_log({"on_death(nil)", "on_deactivate(true)"}) + + assert(not obj:is_valid()) +end +unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true}) + +local function test_entity_interact(_, pos) + log = {} + + local obj = core.add_entity(pos, "unittests:callbacks") + check_log({"on_activate(0)"}) + + -- rightclick + obj:right_click(obj) + check_log({"on_rightclick(self)"}) + + -- useless punch + obj:punch(obj, 0.5, {}) + check_log({"on_punch(self, 0.5, 0)"}) + + -- fatal punch + obj:punch(obj, 1.9, { + full_punch_interval = 1.0, + damage_groups = { test = 10 }, + }) + check_log({ + -- does 10 damage even though we only have 5 hp + "on_punch(self, 1.9, 10)", + "on_death(self)", + "on_deactivate(true)" + }) +end +unittests.register("test_entity_interact", test_entity_interact, {map=true}) + +local function test_entity_attach(player, pos) + log = {} + + local obj = core.add_entity(pos, "unittests:callbacks") + check_log({"on_activate(0)"}) + + -- attach player to entity + player:set_attach(obj) + check_log({"on_attach_child(player)"}) + assert(player:get_attach() == obj) + player:set_detach() + check_log({"on_detach_child(player)"}) + assert(player:get_attach() == nil) + + -- attach entity to player + obj:set_attach(player) + check_log({}) + assert(obj:get_attach() == player) + obj:set_detach() + check_log({"on_detach(player)"}) + assert(obj:get_attach() == nil) + + obj:remove() +end +unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true}) + +--------- + +core.register_entity("unittests:dummy", { + initial_properties = { + hp_max = 1, + visual = "upright_sprite", + textures = { "no_texture.png" }, + static_save = false, + }, +}) + +local function test_entity_raycast(_, pos) + local obj1 = core.add_entity(pos, "unittests:dummy") + local obj2 = core.add_entity(pos:offset(1, 0, 0), "unittests:dummy") + local raycast = core.raycast(pos:offset(-1, 0, 0), pos:offset(2, 0, 0), true, false) + for pt in raycast do + if pt.type == "object" then + assert(pt.ref == obj1) + obj1:remove() + obj2:remove() + obj1 = nil -- object should be hit exactly one + end + end + assert(obj1 == nil) +end +unittests.register("test_entity_raycast", test_entity_raycast, {map=true}) + +local function test_object_iterator(pos, make_iterator) + local obj1 = core.add_entity(pos, "unittests:dummy") + local obj2 = core.add_entity(pos, "unittests:dummy") + assert(obj1 and obj2) + local found = false + -- As soon as we find one of the objects, we remove both, invalidating the other. + for obj in make_iterator() do + assert(obj:is_valid()) + if obj == obj1 or obj == obj2 then + obj1:remove() + obj2:remove() + found = true + end + end + assert(found) +end + +unittests.register("test_objects_inside_radius", function(_, pos) + test_object_iterator(pos, function() + return core.objects_inside_radius(pos, 1) + end) +end, {map=true}) + +unittests.register("test_objects_in_area", function(_, pos) + test_object_iterator(pos, function() + return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1)) + end) +end, {map=true}) + +-- Tests that bone rotation euler angles are preserved (see #14992) +local function test_get_bone_rot(_, pos) + local obj = core.add_entity(pos, "unittests:dummy") + for _ = 1, 100 do + local function assert_similar(euler_angles) + local _, rot = obj:get_bone_position("bonename") + assert(euler_angles:distance(rot) < 1e-3) + local override = obj:get_bone_override("bonename") + assert(euler_angles:distance(override.rotation.vec:apply(math.deg)) < 1e-3) + end + local deg = 1e3 * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_position("bonename", vector.zero(), deg) + assert_similar(deg) + local rad = 3 * math.pi * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_override("bonename", {rotation = {vec = rad}}) + assert_similar(rad:apply(math.deg)) + end +end +unittests.register("test_get_bone_rot", test_get_bone_rot, {map=true}) diff --git a/data/games/garage/mods/unittests/get_version.lua b/data/games/garage/mods/unittests/get_version.lua new file mode 100644 index 0000000..7ef7825 --- /dev/null +++ b/data/games/garage/mods/unittests/get_version.lua @@ -0,0 +1,16 @@ + +unittests.register("test_get_version", function() + local version = core.get_version() + assert(type(version) == "table") + assert(type(version.project) == "string") + assert(type(version.string) == "string") + assert(type(version.proto_min) == "number") + assert(type(version.proto_max) == "number") + assert(version.proto_max >= version.proto_min) + assert(type(version.is_dev) == "boolean") + if version.is_dev then + assert(type(version.hash) == "string") + else + assert(version.hash == nil) + end +end) diff --git a/data/games/garage/mods/unittests/init.lua b/data/games/garage/mods/unittests/init.lua new file mode 100644 index 0000000..7243343 --- /dev/null +++ b/data/games/garage/mods/unittests/init.lua @@ -0,0 +1,235 @@ +unittests = {} + +unittests.list = {} + +-- name: Name of the test +-- func: +-- for sync: function(player, pos), should error on failure +-- for async: function(callback, player, pos) +-- MUST call callback() or callback("error msg") in case of error once test is finished +-- this means you cannot use assert() in the test implementation +-- opts: { +-- player = false, -- Does test require a player? +-- map = false, -- Does test require map access? +-- async = false, -- Does the test run asynchronously? (read notes above!) +-- } +function unittests.register(name, func, opts) + local def = table.copy(opts or {}) + def.name = name + def.func = func + table.insert(unittests.list, def) +end + +function unittests.on_finished(all_passed) + -- free to override +end + +-- Calls invoke with a callback as argument +-- Suspends coroutine until that callback is called +-- Return values are passed through +local function await(invoke) + local co = coroutine.running() + assert(co) + local called_early = true + invoke(function(...) + if called_early == true then + called_early = {...} + else + coroutine.resume(co, ...) + co = nil + end + end) + if called_early ~= true then + -- callback was already called before yielding + return unpack(called_early) + end + called_early = nil + return coroutine.yield() +end + +function unittests.run_one(idx, counters, out_callback, player, pos) + local def = unittests.list[idx] + if not def.player then + player = nil + elseif player == nil then + out_callback(false) + return false + end + if not def.map then + pos = nil + elseif pos == nil then + out_callback(false) + return false + end + + local tbegin = core.get_us_time() + local function done(status, err) + local tend = core.get_us_time() + local ms_taken = (tend - tbegin) / 1000 + + if not status then + core.log("error", err) + end + print(string.format("[%s] %s - %dms", + status and "PASS" or "FAIL", def.name, ms_taken)) + counters.time = counters.time + ms_taken + counters.total = counters.total + 1 + if status then + counters.passed = counters.passed + 1 + end + end + + if def.async then + core.log("info", "[unittest] running " .. def.name .. " (async)") + def.func(function(err) + done(err == nil, err) + out_callback(true) + end, player, pos) + else + core.log("info", "[unittest] running " .. def.name) + local status, err = pcall(def.func, player, pos) + done(status, err) + out_callback(true) + end + + return true +end + +local function wait_for_player(callback) + if #core.get_connected_players() > 0 then + return callback(core.get_connected_players()[1]) + end + local first = true + core.register_on_joinplayer(function(player) + if first then + callback(player) + first = false + end + end) +end + +local function wait_for_map(pos, callback) + local function check() + if core.get_node(pos).name ~= "ignore" then + callback() + else + core.after(0, check) + end + end + check() +end + +-- This runs in a coroutine so it uses await() +function unittests.run_all() + local counters = { time = 0, total = 0, passed = 0 } + + -- Run standalone tests first + for idx = 1, #unittests.list do + local def = unittests.list[idx] + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, nil, nil) + end) + end + + -- Wait for a player to join, run tests that require a player + local player = await(wait_for_player) + for idx = 1, #unittests.list do + local def = unittests.list[idx] + if not def.done then + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, player, nil) + end) + end + end + + -- Wait for the world to generate/load, run tests that require map access + local pos = player:get_pos():round():offset(0, 5, 0) + core.forceload_block(pos, true, -1) + await(function(cb) + wait_for_map(pos, cb) + end) + for idx = 1, #unittests.list do + local def = unittests.list[idx] + if not def.done then + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, player, pos) + end) + end + end + + -- Print stats + assert(#unittests.list == counters.total) + print(string.rep("+", 80)) + print(string.format("Devtest Unit Test Results: %s", + counters.total == counters.passed and "PASSED" or "FAILED")) + print(string.format(" %d / %d failed tests.", + counters.total - counters.passed, counters.total)) + print(string.format(" Testing took %dms total.", counters.time)) + print(string.rep("+", 80)) + unittests.on_finished(counters.total == counters.passed) + return counters.total == counters.passed +end + +-------------- + +local modpath = core.get_modpath("unittests") +dofile(modpath .. "/misc.lua") +dofile(modpath .. "/player.lua") +dofile(modpath .. "/crafting.lua") +dofile(modpath .. "/itemdescription.lua") +dofile(modpath .. "/async_env.lua") +dofile(modpath .. "/entity.lua") +dofile(modpath .. "/get_version.lua") +dofile(modpath .. "/itemstack_equals.lua") +dofile(modpath .. "/content_ids.lua") +dofile(modpath .. "/metadata.lua") +dofile(modpath .. "/raycast.lua") +dofile(modpath .. "/inventory.lua") +dofile(modpath .. "/load_time.lua") +dofile(modpath .. "/on_shutdown.lua") +dofile(modpath .. "/color.lua") + +-------------- + +local function send_results(name, ok) + core.chat_send_player(name, + core.colorize(ok and "green" or "red", + (ok and "All devtest unit tests passed." or + "There were devtest unit test failures.") .. + " Check the console for detailed output.")) +end + +if core.settings:get_bool("devtest_unittests_autostart", false) then + local test_results = nil + core.after(0, function() + -- CI adds a mod which sets `unittests.on_finished` + -- to write status information to the filesystem + local old_on_finished = unittests.on_finished + unittests.on_finished = function(ok) + for _, player in ipairs(core.get_connected_players()) do + send_results(player:get_player_name(), ok) + end + test_results = ok + old_on_finished(ok) + end + coroutine.wrap(unittests.run_all)() + end) + core.register_on_joinplayer(function(player) + if test_results == nil then + return -- tests haven't completed yet + end + send_results(player:get_player_name(), test_results) + end) +else + core.register_chatcommand("unittests", { + privs = {basic_privs=true}, + description = "Runs devtest unittests (may modify player or map state)", + func = function(name, param) + unittests.on_finished = function(ok) + send_results(name, ok) + end + coroutine.wrap(unittests.run_all)() + return true, "" + end, + }) +end diff --git a/data/games/garage/mods/unittests/inside_async_env.lua b/data/games/garage/mods/unittests/inside_async_env.lua new file mode 100644 index 0000000..fb61060 --- /dev/null +++ b/data/games/garage/mods/unittests/inside_async_env.lua @@ -0,0 +1,35 @@ +unittests = {} + +core.log("info", "Hello World") + +unittests.custom_metatable = {} +core.register_portable_metatable("unittests:custom_metatable", unittests.custom_metatable) + +local function do_tests() + assert(core == minetest) + -- stuff that should not be here + assert(not core.get_player_by_name) + assert(not core.set_node) + assert(not core.object_refs) + -- stuff that should be here + assert(ItemStack) + local meta = ItemStack():get_meta() + assert(type(meta) == "userdata") + assert(type(meta.set_tool_capabilities) == "function") + assert(core.registered_items[""]) + assert(next(core.registered_nodes) ~= nil) + assert(core.registered_craftitems["unittests:stick"]) + -- alias handling + assert(core.registered_items["unittests:steel_ingot_alias"].name == + "unittests:steel_ingot") + -- fallback to item defaults + assert(core.registered_items["unittests:description_test"].on_place == true) +end + +function unittests.async_test() + local ok, err = pcall(do_tests) + if not ok then + core.log("error", err) + end + return ok +end diff --git a/data/games/garage/mods/unittests/inside_mapgen_env.lua b/data/games/garage/mods/unittests/inside_mapgen_env.lua new file mode 100644 index 0000000..f6f8513 --- /dev/null +++ b/data/games/garage/mods/unittests/inside_mapgen_env.lua @@ -0,0 +1,34 @@ +core.log("info", "Hello World") + +local function do_tests() + assert(core == minetest) + -- stuff that should not be here + assert(not core.get_player_by_name) + assert(not core.object_refs) + -- stuff that should be here + assert(core.register_on_generated) + assert(core.get_node) + assert(core.spawn_tree) + assert(ItemStack) + local meta = ItemStack():get_meta() + assert(type(meta) == "userdata") + assert(type(meta.set_tool_capabilities) == "function") + assert(core.registered_items[""]) + assert(core.save_gen_notify) + -- alias handling + assert(core.registered_items["unittests:steel_ingot_alias"].name == + "unittests:steel_ingot") + -- fallback to item defaults + assert(core.registered_items["unittests:description_test"].on_place == true) +end + +-- first thread to get here runs the tests +if core.ipc_cas("unittests:mg_once", nil, true) then + -- this is checked from the main env + core.ipc_set("unittests:mg", { pcall(do_tests) }) +end + +core.register_on_generated(function(vm, pos1, pos2, blockseed) + local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1 + assert(pos2:subtract(pos1) == vector.new(n, n, n)) +end) diff --git a/data/games/garage/mods/unittests/inventory.lua b/data/games/garage/mods/unittests/inventory.lua new file mode 100644 index 0000000..cffcba4 --- /dev/null +++ b/data/games/garage/mods/unittests/inventory.lua @@ -0,0 +1,73 @@ + +local item_with_meta = ItemStack({name = "air", meta = {test = "abc"}}) + +local test_list = { + ItemStack("air"), + ItemStack(""), + ItemStack(item_with_meta), +} + +local function compare_lists(a, b) + if not a or not b or #a ~= #b then + return false + end + for i=1, #a do + if not ItemStack(a[i]):equals(ItemStack(b[i])) then + return false + end + end + return true +end + +local function test_inventory() + local inv = core.create_detached_inventory("test") + + inv:set_lists({test = {""}}) + assert(inv:get_list("test")) + + assert(inv:get_size("test") == 1) + assert(inv:set_size("test", 3)) + assert(not inv:set_size("test", -1)) + + assert(inv:get_width("test") == 0) + assert(inv:set_width("test", 3)) + assert(not inv:set_width("test", -1)) + + inv:set_stack("test", 1, "air") + inv:set_stack("test", 3, item_with_meta) + assert(not inv:is_empty("test")) + assert(compare_lists(inv:get_list("test"), test_list)) + + assert(inv:add_item("test", "air") == ItemStack()) + assert(inv:add_item("test", item_with_meta) == ItemStack()) + assert(inv:get_stack("test", 1) == ItemStack("air 2")) + + assert(inv:room_for_item("test", "air 99")) + inv:set_stack("test", 2, "air 99") + assert(not inv:room_for_item("test", "air 99")) + inv:set_stack("test", 2, "") + + assert(inv:contains_item("test", "air")) + assert(not inv:contains_item("test", "air 99")) + assert(inv:contains_item("test", item_with_meta, true)) + + -- Items should be removed in reverse and combine with first stack removed + assert(inv:remove_item("test", "air") == item_with_meta) + item_with_meta:set_count(2) + assert(inv:remove_item("test", "air 2") == item_with_meta) + assert(inv:remove_item("test", "air") == ItemStack("air")) + assert(inv:is_empty("test")) + + -- Failure of set_list(s) should not change inventory + local before = inv:get_list("test") + pcall(inv.set_lists, inv, {test = true}) + pcall(inv.set_list, inv, "test", true) + local after = inv:get_list("test") + assert(compare_lists(before, after)) + + local location = inv:get_location() + assert(core.remove_detached_inventory("test")) + assert(not core.get_inventory(location)) +end + +unittests.register("test_inventory", test_inventory) diff --git a/data/games/garage/mods/unittests/itemdescription.lua b/data/games/garage/mods/unittests/itemdescription.lua new file mode 100644 index 0000000..730248d --- /dev/null +++ b/data/games/garage/mods/unittests/itemdescription.lua @@ -0,0 +1,43 @@ +local full_description = "Description Test Item\nFor testing item decription" +core.register_tool("unittests:description_test", { + description = full_description, + inventory_image = "unittests_description_test.png", + groups = { dummy = 1 }, +}) + +core.register_chatcommand("item_description", { + param = "", + description = "Show the short and full description of the wielded item.", + func = function(name) + local player = core.get_player_by_name(name) + local item = player:get_wielded_item() + return true, string.format("short_description: %s\ndescription: %s", + item:get_short_description(), item:get_description()) + end +}) + +local function test_short_desc() + local function get_short_description(item) + return ItemStack(item):get_short_description() + end + + local stack = ItemStack("unittests:description_test") + assert(stack:get_short_description() == "Description Test Item") + assert(get_short_description("unittests:description_test") == "Description Test Item") + assert(core.registered_items["unittests:description_test"].short_description == nil) + assert(stack:get_description() == full_description) + assert(stack:get_description() == core.registered_items["unittests:description_test"].description) + + stack:get_meta():set_string("description", "Hello World") + assert(stack:get_short_description() == "Hello World") + assert(stack:get_description() == "Hello World") + assert(get_short_description(stack) == "Hello World") + assert(get_short_description("unittests:description_test") == "Description Test Item") + + stack:get_meta():set_string("short_description", "Foo Bar") + assert(stack:get_short_description() == "Foo Bar") + assert(stack:get_description() == "Hello World") + + return true +end +unittests.register("test_short_desc", test_short_desc) diff --git a/data/games/garage/mods/unittests/itemstack_equals.lua b/data/games/garage/mods/unittests/itemstack_equals.lua new file mode 100644 index 0000000..561e612 --- /dev/null +++ b/data/games/garage/mods/unittests/itemstack_equals.lua @@ -0,0 +1,74 @@ +local function test_itemstack_equals_non_stack() + local i1 = ItemStack("basenodes:stone") + local i2 = { foo = "bar" } + + assert(not i1:equals(i2)) + assert(i1 ~= i2) + assert(i2 ~= i1) +end + +unittests.register("test_itemstack_equals_non_stack", test_itemstack_equals_non_stack) + +local function test_itemstack_equals_name() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:desert_stone") + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_name", test_itemstack_equals_name) + +local function test_itemstack_equals_count() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:stone 2") + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_count", test_itemstack_equals_count) + +local function test_itemstack_equals_wear() + local i1 = ItemStack("basetools:axe_stone") + local i2 = ItemStack("basetools:axe_stone") + + i2:add_wear(1) + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_wear", test_itemstack_equals_wear) + +local function test_itemstack_equals_metadata() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:stone") + local i3 = ItemStack("basenodes:stone") + + local m1 = i1:get_meta() + local m2 = i2:get_meta() + local m3 = i3:get_meta() + + local keys = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"} + local values = {} + + for i, key in pairs(keys) do + m1:set_int(key, i) + m3:set_int(key, i) + values[key] = i + end + + m3:set_int("a", 999) + + for key, i in pairs(values) do + m2:set_int(key, i) + end + + assert(i1:equals(i2)) + assert(i1 == i2) + assert(not i1:equals(i3)) + assert(i1 ~= i3) +end + +unittests.register("test_itemstack_equals_metadata", test_itemstack_equals_metadata) diff --git a/data/games/garage/mods/unittests/load_time.lua b/data/games/garage/mods/unittests/load_time.lua new file mode 100644 index 0000000..12f1a38 --- /dev/null +++ b/data/games/garage/mods/unittests/load_time.lua @@ -0,0 +1,13 @@ +-- Test item (un)registration and overriding +do + local itemname = "unittests:test_override_item" + core.register_craftitem(":" .. itemname, {description = "foo"}) + assert(assert(core.registered_items[itemname]).description == "foo") + core.override_item(itemname, {description = "bar"}) + assert(assert(core.registered_items[itemname]).description == "bar") + core.override_item(itemname, {}, {"description"}) + -- description has the empty string as a default + assert(assert(core.registered_items[itemname]).description == "") + core.unregister_item("unittests:test_override_item") + assert(core.registered_items["unittests:test_override_item"] == nil) +end diff --git a/data/games/garage/mods/unittests/metadata.lua b/data/games/garage/mods/unittests/metadata.lua new file mode 100644 index 0000000..bdd51c3 --- /dev/null +++ b/data/games/garage/mods/unittests/metadata.lua @@ -0,0 +1,121 @@ +-- Tests of generic and specific metadata functionality + +local compare_meta = ItemStack("unittests:iron_lump"):get_meta() +compare_meta:from_table({ + fields = { + a = "1", + b = "2", + c = "3", + d = "4", + e = "e", + }, +}) + +local function test_metadata(meta) + meta:from_table({fields = {a = 2, b = "2"}}) + meta:set_string("a", 1) + meta:set_string("c", "3") + meta:set_int("d", 4) + meta:set_string("e", "e") + + meta:set_string("", "!") + meta:set_string("", "") + + assert(meta:equals(compare_meta)) + + local tab = meta:to_table() + assert(tab.fields.a == "1") + assert(tab.fields.b == "2") + assert(tab.fields.c == "3") + assert(tab.fields.d == "4") + assert(tab.fields.e == "e") + + local keys = meta:get_keys() + assert(table.indexof(keys, "a") > 0) + assert(table.indexof(keys, "b") > 0) + assert(table.indexof(keys, "c") > 0) + assert(table.indexof(keys, "d") > 0) + assert(table.indexof(keys, "e") > 0) + assert(#keys == 5) + + assert(not meta:contains("")) + assert(meta:contains("a")) + assert(meta:contains("b")) + assert(meta:contains("c")) + assert(meta:contains("d")) + assert(meta:contains("e")) + + assert(meta:get("") == nil) + assert(meta:get_string("") == "") + assert(meta:get_int("") == 0) + assert(meta:get_float("") == 0.0) + assert(meta:get("a") == "1") + assert(meta:get_string("a") == "1") + assert(meta:get_int("a") == 1) + assert(meta:get_float("a") == 1.0) + assert(meta:get_int("e") == 0) + assert(meta:get_float("e") == 0.0) + + meta:set_float("f", 1.1) + meta:set_string("g", "${f}") + meta:set_string("h", "${g}") + meta:set_string("i", "${h}") + assert(meta:get_float("h") > 1) + assert(meta:get_string("i") == "${f}") + + meta:set_float("j", 1.23456789) + assert(meta:get_float("j") == 1.23456789) + meta:set_float("j", -1 / 0) + assert(meta:get_float("j") == -1 / 0) + meta:set_float("j", 0 / 0) + assert(core.is_nan(meta:get_float("j"))) + + meta:from_table() + assert(next(meta:to_table().fields) == nil) + assert(#meta:get_keys() == 0) + + assert(not meta:equals(compare_meta)) +end + +local function test_metadata_compat(meta) + -- key/value removal using set_string (undocumented, deprecated way) + meta:set_string("key", "value") + assert(meta:get_string("key") == "value") + meta:set_string("key", nil) -- ignore warning + assert(meta:to_table().fields["key"] == nil) + + -- undocumented but supported consequence of Lua's + -- automatic string <--> number cast + meta:set_string("key", 2) + assert(meta:get_string("key") == "2") + + -- from_table with non-string keys (supported) + local values = meta:to_table() + values.fields["new"] = 420 + meta:from_table(values) + assert(meta:get_int("new") == 420) + values.fields["new"] = nil + meta:from_table(values) + assert(meta:get("new") == nil) +end + + +local storage_a = core.get_mod_storage() +local storage_b = core.get_mod_storage() +local function test_mod_storage() + assert(rawequal(storage_a, storage_b)) + test_metadata(storage_a) +end +unittests.register("test_mod_storage", test_mod_storage) + +local function test_item_metadata() + local meta = ItemStack("unittest:coal_lump"):get_meta() + test_metadata(meta) + test_metadata_compat(meta) +end +unittests.register("test_item_metadata", test_item_metadata) + +local function test_node_metadata(player, pos) + test_metadata(core.get_meta(pos)) +end +unittests.register("test_node_metadata", test_node_metadata, {map=true}) diff --git a/data/games/garage/mods/unittests/misc.lua b/data/games/garage/mods/unittests/misc.lua new file mode 100644 index 0000000..d01eed1 --- /dev/null +++ b/data/games/garage/mods/unittests/misc.lua @@ -0,0 +1,332 @@ +core.register_mapgen_script(core.get_modpath(core.get_current_modname()) .. + DIR_DELIM .. "inside_mapgen_env.lua") + +local function test_pseudo_random() + -- We have comprehensive unit tests in C++, this is just to make sure the API code isn't messing up + local gen1 = PseudoRandom(13) + assert(gen1:next() == 22290) + assert(gen1:next() == 13854) + + local gen2 = PseudoRandom(gen1:get_state()) + for n = 0, 16 do + assert(gen1:next() == gen2:next()) + end + + local pr3 = PseudoRandom(-101) + assert(pr3:next(0, 100) == 35) + -- unusual case that is normally disallowed: + assert(pr3:next(10000, 42767) == 12485) +end +unittests.register("test_pseudo_random", test_pseudo_random) + +local function test_pcg_random() + -- We have comprehensive unit tests in C++, this is just to make sure the API code isn't messing up + local gen1 = PcgRandom(55) + + for n = 0, 16 do + gen1:next() + end + + local gen2 = PcgRandom(26) + gen2:set_state(gen1:get_state()) + + for n = 16, 32 do + assert(gen1:next() == gen2:next()) + end +end +unittests.register("test_pcg_random", test_pcg_random) + +local function test_dynamic_media(cb, player) + if core.get_player_information(player:get_player_name()).protocol_version < 40 then + core.log("warning", "test_dynamic_media: Client too old, skipping test.") + return cb() + end + + -- Check that the client acknowledges media transfers + local path = core.get_worldpath() .. "/test_media.obj" + local f = io.open(path, "w") + f:write("# contents don't matter\n") + f:close() + + local call_ok = false + local ok = core.dynamic_add_media({ + filepath = path, + to_player = player:get_player_name(), + }, function(name) + if not call_ok then + return cb("impossible condition") + end + cb() + end) + if not ok then + return cb("dynamic_add_media() returned error") + end + call_ok = true + + -- if the callback isn't called this test will just hang :shrug: +end +unittests.register("test_dynamic_media", test_dynamic_media, {async=true, player=true}) + +local function test_v3f_metatable(player) + assert(vector.check(player:get_pos())) +end +unittests.register("test_v3f_metatable", test_v3f_metatable, {player=true}) + +local function test_v3s16_metatable(player, pos) + local node = core.get_node(pos) + local found_pos = core.find_node_near(pos, 0, node.name, true) + assert(vector.check(found_pos)) +end +unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true}) + +local function test_clear_meta(_, pos) + local ref = core.get_meta(pos) + + for way = 1, 3 do + ref:set_string("foo", "bar") + assert(ref:contains("foo")) + + if way == 1 then + ref:from_table({}) + elseif way == 2 then + ref:from_table(nil) + else + ref:set_string("foo", "") + end + + assert(#core.find_nodes_with_meta(pos, pos) == 0, "clearing failed " .. way) + end +end +unittests.register("test_clear_meta", test_clear_meta, {map=true}) + +local on_punch_called, on_place_called +core.register_on_placenode(function() + on_place_called = true +end) +core.register_on_punchnode(function() + on_punch_called = true +end) +local function test_node_callbacks(_, pos) + on_place_called = false + on_punch_called = false + + core.place_node(pos, {name="basenodes:dirt"}) + assert(on_place_called, "on_place not called") + core.punch_node(pos) + assert(on_punch_called, "on_punch not called") + core.remove_node(pos) +end +unittests.register("test_node_callbacks", test_node_callbacks, {map=true}) + +local function test_hashing() + local input = "hello\000world" + assert(core.sha1(input) == "f85b420f1e43ebf88649dfcab302b898d889606c") + assert(core.sha256(input) == "b206899bc103669c8e7b36de29d73f95b46795b508aa87d612b2ce84bfb29df2") +end +unittests.register("test_hashing", test_hashing) + +local function test_compress() + -- This text should be compressible, to make sure the results are... normal + local text = "The\000 icey canoe couldn't move very well on the\128 lake. The\000 ice was too stiff and the icey canoe's paddles simply wouldn't punch through." + local methods = { + "deflate", + "zstd", + -- "noodle", -- for warning alarm test + } + local zstd_magic = string.char(0x28, 0xB5, 0x2F, 0xFD) + for _, method in ipairs(methods) do + local compressed = core.compress(text, method) + assert(core.decompress(compressed, method) == text, "input/output mismatch for compression method " .. method) + local has_zstd_magic = compressed:sub(1, 4) == zstd_magic + if method == "zstd" then + assert(has_zstd_magic, "zstd magic number not in zstd method") + else + assert(not has_zstd_magic, "zstd magic number in method " .. method .. " (which is not zstd)") + end + end +end +unittests.register("test_compress", test_compress) + +local function test_urlencode() + -- checks that API code handles null bytes + assert(core.urlencode("foo\000bar!") == "foo%00bar%21") +end +unittests.register("test_urlencode", test_urlencode) + +local function test_parse_json() + local raw = "{\"how\\u0000weird\":\n\"yes\\u0000really\",\"n\":-1234567891011,\"z\":null}" + do + local data = core.parse_json(raw) + assert(data["how\000weird"] == "yes\000really") + assert(data.n == -1234567891011) + assert(data.z == nil) + end + do + local null = {} + local data = core.parse_json(raw, null) + assert(data.z == null) + end + do + local data, err = core.parse_json('"ceci n\'est pas un json', nil, true) + assert(data == nil) + assert(type(err) == "string") + end +end +unittests.register("test_parse_json", test_parse_json) + +local function test_write_json() + -- deeply nested structures should be preserved + local leaf = 42 + local data = leaf + for i = 1, 1000 do + data = {data} + end + local roundtripped = core.parse_json(core.write_json(data)) + for i = 1, 1000 do + roundtripped = roundtripped[1] + end + assert(roundtripped == 42) +end +unittests.register("test_write_json", test_write_json) + +local function test_game_info() + local info = core.get_game_info() + local game_conf = Settings(info.path .. "/game.conf") + assert(info.id == "devtest") + assert(info.title == game_conf:get("title")) +end +unittests.register("test_game_info", test_game_info) + +local function test_mapgen_edges(cb) + -- Test that the map can extend to the expected edges and no further. + local min_edge, max_edge = core.get_mapgen_edges() + local min_finished = {} + local max_finished = {} + local function finish() + if #min_finished ~= 1 then + return cb("Expected 1 block to emerge around mapgen minimum edge") + end + if min_finished[1] ~= (min_edge / core.MAP_BLOCKSIZE):floor() then + return cb("Expected block within minimum edge to emerge") + end + if #max_finished ~= 1 then + return cb("Expected 1 block to emerge around mapgen maximum edge") + end + if max_finished[1] ~= (max_edge / core.MAP_BLOCKSIZE):floor() then + return cb("Expected block within maximum edge to emerge") + end + return cb() + end + local emerges_left = 2 + local function emerge_block(blockpos, action, blocks_left, finished) + if action ~= core.EMERGE_CANCELLED then + table.insert(finished, blockpos) + end + if blocks_left == 0 then + emerges_left = emerges_left - 1 + if emerges_left == 0 then + return finish() + end + end + end + core.emerge_area(min_edge:subtract(1), min_edge, emerge_block, min_finished) + core.emerge_area(max_edge, max_edge:add(1), emerge_block, max_finished) +end +unittests.register("test_mapgen_edges", test_mapgen_edges, {map=true, async=true}) + +local finish_test_on_mapblocks_changed +core.register_on_mapblocks_changed(function(modified_blocks, modified_block_count) + if finish_test_on_mapblocks_changed then + finish_test_on_mapblocks_changed(modified_blocks, modified_block_count) + finish_test_on_mapblocks_changed = nil + end +end) +local function test_on_mapblocks_changed(cb, player, pos) + local bp1 = (pos / core.MAP_BLOCKSIZE):floor() + local bp2 = bp1:add(1) + for _, bp in ipairs({bp1, bp2}) do + -- Make a modification in the block. + local p = bp * core.MAP_BLOCKSIZE + core.load_area(p) + local meta = core.get_meta(p) + meta:set_int("test_on_mapblocks_changed", meta:get_int("test_on_mapblocks_changed") + 1) + end + finish_test_on_mapblocks_changed = function(modified_blocks, modified_block_count) + if modified_block_count < 2 then + return cb("Expected at least two mapblocks to be recorded as modified") + end + if not modified_blocks[core.hash_node_position(bp1)] or + not modified_blocks[core.hash_node_position(bp2)] then + return cb("The expected mapblocks were not recorded as modified") + end + cb() + end +end +unittests.register("test_on_mapblocks_changed", test_on_mapblocks_changed, {map=true, async=true}) + +local function test_gennotify_api() + local DECO_ID = 123 + local UD_ID = "unittests:dummy" + + -- the engine doesn't check if the id is actually valid, maybe it should + core.set_gen_notify({decoration=true}, {DECO_ID}) + + core.set_gen_notify({custom=true}, nil, {UD_ID}) + + local flags, deco, custom = core.get_gen_notify() + local function ff(flag) + return (" " .. flags .. " "):match("[ ,]" .. flag .. "[ ,]") ~= nil + end + assert(ff("decoration"), "'decoration' flag missing") + assert(ff("custom"), "'custom' flag missing") + assert(table.indexof(deco, DECO_ID) > 0) + assert(table.indexof(custom, UD_ID) > 0) + + core.set_gen_notify({decoration=false, custom=false}) + + flags, deco, custom = core.get_gen_notify() + assert(not ff("decoration") and not ff("custom")) + assert(#deco == 0, "deco ids not empty") + assert(#custom == 0, "custom ids not empty") +end +unittests.register("test_gennotify_api", test_gennotify_api) + +-- <=> inside_mapgen_env.lua +local function test_mapgen_env(cb) + -- emerge threads start delayed so this can take a second + local res = core.ipc_get("unittests:mg") + if res == nil then + return core.after(0, test_mapgen_env, cb) + end + -- handle error status + if res[1] then + cb() + else + cb(res[2]) + end +end +unittests.register("test_mapgen_env", test_mapgen_env, {async=true}) + +local function test_ipc_vector_preserve(cb) + -- the IPC also uses register_portable_metatable + core.ipc_set("unittests:v", vector.new(4, 0, 4)) + local v = core.ipc_get("unittests:v") + assert(type(v) == "table") + assert(vector.check(v)) +end +unittests.register("test_ipc_vector_preserve", test_ipc_vector_preserve) + +local function test_ipc_poll(cb) + core.ipc_set("unittests:flag", nil) + assert(core.ipc_poll("unittests:flag", 1) == false) + + -- Note that unlike the async result callback - which has to wait for the + -- next server step - the IPC is instant + local t0 = core.get_us_time() + core.handle_async(function() + core.ipc_set("unittests:flag", true) + end, function() end) + assert(core.ipc_poll("unittests:flag", 1000) == true, "Wait failed (or slow machine?)") + print("delta: " .. (core.get_us_time() - t0) .. "us") +end +unittests.register("test_ipc_poll", test_ipc_poll) diff --git a/data/games/garage/mods/unittests/mod.conf b/data/games/garage/mods/unittests/mod.conf new file mode 100644 index 0000000..ccff737 --- /dev/null +++ b/data/games/garage/mods/unittests/mod.conf @@ -0,0 +1,4 @@ +name = unittests +description = Adds automated unit tests for the engine +# Also test that it is possible to depend on first_mod +depends = first_mod, basenodes diff --git a/data/games/garage/mods/unittests/on_shutdown.lua b/data/games/garage/mods/unittests/on_shutdown.lua new file mode 100644 index 0000000..6d5d886 --- /dev/null +++ b/data/games/garage/mods/unittests/on_shutdown.lua @@ -0,0 +1,22 @@ +-- Test whether players still exist on shutdown +local players = {} + +core.register_on_joinplayer(function(player) + players[player:get_player_name()] = true +end) + +core.register_on_leaveplayer(function(player) + local name = player:get_player_name(); + assert(players[name], "Unrecorded player join.") + players[name] = nil +end) + +core.register_on_shutdown(function() + for _, player in pairs(core.get_connected_players()) do + local name = player:get_player_name() + assert(players[name], "Unrecorded player join or left too early.") + players[name] = nil + end + + assert(not next(players), "Invalid connected players on shutdown.") +end) diff --git a/data/games/garage/mods/unittests/player.lua b/data/games/garage/mods/unittests/player.lua new file mode 100644 index 0000000..f8945f3 --- /dev/null +++ b/data/games/garage/mods/unittests/player.lua @@ -0,0 +1,206 @@ +-- +-- HP Change Reasons +-- +local expect = nil +core.register_on_player_hpchange(function(player, hp_change, reason) + if expect == nil then + return + end + + for key, value in pairs(reason) do + assert(expect[key] == value) + end + for key, value in pairs(expect) do + assert(reason[key] == value) + end + + expect = nil +end) + +local function run_hpchangereason_tests(player) + local old_hp = player:get_hp() + + player:set_hp(20) + expect = { type = "set_hp", from = "mod" } + player:set_hp(3) + assert(expect == nil) + + expect = { a = 234, type = "set_hp", from = "mod" } + player:set_hp(7, { a= 234 }) + assert(expect == nil) + + expect = { df = 3458973454, type = "fall", from = "mod" } + player:set_hp(10, { type = "fall", df = 3458973454 }) + assert(expect == nil) + + player:set_hp(old_hp) +end +unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true}) + +-- +-- HP differences +-- + +local expected_diff = nil +local hpchange_counter = 0 +local die_counter = 0 +core.register_on_player_hpchange(function(player, hp_change, reason) + if expected_diff then + assert(hp_change == expected_diff) + hpchange_counter = hpchange_counter + 1 + end +end) +core.register_on_dieplayer(function() + die_counter = die_counter + 1 +end) + +local function hp_diference_test(player, hp_max) + assert(hp_max >= 22) + + local old_hp = player:get_hp() + local old_hp_max = player:get_properties().hp_max + + hpchange_counter = 0 + die_counter = 0 + + expected_diff = nil + player:set_properties({hp_max = hp_max}) + player:set_hp(22) + assert(player:get_hp() == 22) + assert(hpchange_counter == 0) + assert(die_counter == 0) + + -- HP difference is not clamped + expected_diff = -25 + player:set_hp(-3) + -- actual final HP value is clamped to >= 0 + assert(player:get_hp() == 0) + assert(hpchange_counter == 1) + assert(die_counter == 1) + + expected_diff = 22 + player:set_hp(22) + assert(player:get_hp() == 22) + assert(hpchange_counter == 2) + assert(die_counter == 1) + + -- Integer overflow is prevented + -- so result is S32_MIN, not S32_MIN - 22 + expected_diff = -2147483648 + player:set_hp(-2147483648) + -- actual final HP value is clamped to >= 0 + assert(player:get_hp() == 0) + assert(hpchange_counter == 3) + assert(die_counter == 2) + + -- Damage is ignored if player is already dead (hp == 0) + expected_diff = "never equal" + player:set_hp(-11) + assert(player:get_hp() == 0) + -- no on_player_hpchange or on_dieplayer call expected + assert(hpchange_counter == 3) + assert(die_counter == 2) + + expected_diff = 11 + player:set_hp(11) + assert(player:get_hp() == 11) + assert(hpchange_counter == 4) + assert(die_counter == 2) + + -- HP difference is not clamped + expected_diff = 1000000 - 11 + player:set_hp(1000000) + -- actual final HP value is clamped to <= hp_max + assert(player:get_hp() == hp_max) + assert(hpchange_counter == 5) + assert(die_counter == 2) + + -- "Healing" is not ignored when hp == hp_max + expected_diff = 80000 - hp_max + player:set_hp(80000) + assert(player:get_hp() == hp_max) + -- on_player_hpchange_call expected + assert(hpchange_counter == 6) + assert(die_counter == 2) + + expected_diff = nil + player:set_properties({hp_max = old_hp_max}) + player:set_hp(old_hp) + core.close_formspec(player:get_player_name(), "") -- hide death screen +end +local function run_hp_difference_tests(player) + hp_diference_test(player, 22) + hp_diference_test(player, 30) + hp_diference_test(player, 65535) -- U16_MAX +end +unittests.register("test_hp_difference", run_hp_difference_tests, {player=true}) + +-- +-- Player meta +-- +local function run_player_meta_tests(player) + local meta = player:get_meta() + meta:set_string("foo", "bar") + assert(meta:contains("foo")) + assert(meta:get_string("foo") == "bar") + assert(meta:get("foo") == "bar") + + local meta2 = player:get_meta() + assert(meta2:get_string("foo") == "bar") + assert(meta2:get("foo") == "bar") + assert(meta:equals(meta2)) + + meta:set_string("bob", "dillan") + assert(meta:get_string("foo") == "bar") + assert(meta:get_string("bob") == "dillan") + assert(meta:get("bob") == "dillan") + assert(meta2:get_string("foo") == "bar") + assert(meta2:get_string("bob") == "dillan") + assert(meta2:get("bob") == "dillan") + assert(meta:equals(meta2)) + + meta:set_string("foo", "") + assert(not meta:contains("foo")) + assert(meta:get("foo") == nil) + assert(meta:get_string("foo") == "") + assert(meta:equals(meta2)) +end +unittests.register("test_player_meta", run_player_meta_tests, {player=true}) + +-- +-- Player add pos +-- +local function run_player_add_pos_tests(player) + local pos = player:get_pos() + player:add_pos(vector.new(0, 1000, 0)) + local newpos = player:get_pos() + player:add_pos(vector.new(0, -1000, 0)) + local backpos = player:get_pos() + local newdist = vector.distance(pos, newpos) + assert(math.abs(newdist - 1000) <= 1) + assert(vector.distance(pos, backpos) <= 1) +end +unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true}) + +-- +-- Hotbar selection clamp +-- +local function run_player_hotbar_clamp_tests(player) + local inv = player:get_inventory() + local old_inv_size = inv:get_size("main") + local old_inv_list = inv:get_list("main") -- Avoid accidentally removing item + local old_bar_size = player:hud_get_hotbar_itemcount() + + inv:set_size("main", 5) + + player:hud_set_hotbar_itemcount(2) + assert(player:hud_get_hotbar_itemcount() == 2) + + player:hud_set_hotbar_itemcount(6) + assert(player:hud_get_hotbar_itemcount() == 5) + + inv:set_size("main", old_inv_size) + inv:set_list("main", old_inv_list) + player:hud_set_hotbar_itemcount(old_bar_size) +end +unittests.register("test_player_hotbar_clamp", run_player_hotbar_clamp_tests, {player=true}) diff --git a/data/games/garage/mods/unittests/raycast.lua b/data/games/garage/mods/unittests/raycast.lua new file mode 100644 index 0000000..08d6a11 --- /dev/null +++ b/data/games/garage/mods/unittests/raycast.lua @@ -0,0 +1,36 @@ +local function raycast_with_pointabilities(start_pos, end_pos, pointabilities) + local ray = core.raycast(start_pos, end_pos, nil, nil, pointabilities) + for hit in ray do + if hit.type == "node" then + return hit.under + end + end + return nil +end + +local function test_raycast_pointabilities(player, pos1) + local pos2 = pos1:offset(0, 0, 1) + local pos3 = pos1:offset(0, 0, 2) + + local oldnode1 = core.get_node(pos1) + local oldnode2 = core.get_node(pos2) + local oldnode3 = core.get_node(pos3) + core.swap_node(pos1, {name = "air"}) + core.swap_node(pos2, {name = "testnodes:not_pointable"}) + core.swap_node(pos3, {name = "testnodes:pointable"}) + + local p = nil + assert(raycast_with_pointabilities(pos1, pos3, p) == pos3) + + p = core.registered_items["testtools:blocked_pointing_staff"].pointabilities + assert(raycast_with_pointabilities(pos1, pos3, p) == nil) + + p = core.registered_items["testtools:ultimate_pointing_staff"].pointabilities + assert(raycast_with_pointabilities(pos1, pos3, p) == pos2) + + core.swap_node(pos1, oldnode1) + core.swap_node(pos2, oldnode2) + core.swap_node(pos3, oldnode3) +end + +unittests.register("test_raycast_pointabilities", test_raycast_pointabilities, {map=true}) diff --git a/data/games/garage/mods/unittests/textures/default_dirt.png b/data/games/garage/mods/unittests/textures/default_dirt.png new file mode 100644 index 0000000..5867030 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/default_dirt.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_callback.png b/data/games/garage/mods/unittests/textures/unittests_callback.png new file mode 100644 index 0000000..3c80fdc Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_callback.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_coal_lump.png b/data/games/garage/mods/unittests/textures/unittests_coal_lump.png new file mode 100644 index 0000000..f460d90 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_coal_lump.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_description_test.png b/data/games/garage/mods/unittests/textures/unittests_description_test.png new file mode 100644 index 0000000..e3c7275 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_description_test.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_iron_lump.png b/data/games/garage/mods/unittests/textures/unittests_iron_lump.png new file mode 100644 index 0000000..22f43e9 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_iron_lump.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_repairable_tool.png b/data/games/garage/mods/unittests/textures/unittests_repairable_tool.png new file mode 100644 index 0000000..46fbbaa Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_repairable_tool.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_steel_ingot.png b/data/games/garage/mods/unittests/textures/unittests_steel_ingot.png new file mode 100644 index 0000000..6977696 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_steel_ingot.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_stick.png b/data/games/garage/mods/unittests/textures/unittests_stick.png new file mode 100644 index 0000000..ffdce70 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_stick.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_torch.png b/data/games/garage/mods/unittests/textures/unittests_torch.png new file mode 100644 index 0000000..ba5eebe Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_torch.png differ diff --git a/data/games/garage/mods/unittests/textures/unittests_unrepairable_tool.png b/data/games/garage/mods/unittests/textures/unittests_unrepairable_tool.png new file mode 100644 index 0000000..c676213 Binary files /dev/null and b/data/games/garage/mods/unittests/textures/unittests_unrepairable_tool.png differ diff --git a/data/games/garage/mods/util_commands/init.lua b/data/games/garage/mods/util_commands/init.lua new file mode 100644 index 0000000..8341901 --- /dev/null +++ b/data/games/garage/mods/util_commands/init.lua @@ -0,0 +1,243 @@ +core.register_chatcommand("hotbar", { + params = "", + description = "Set hotbar size", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local size = tonumber(param) + if not size then + return false, "Missing or incorrect size parameter!" + end + local ok = player:hud_set_hotbar_itemcount(size) + if ok then + return true + else + return false, "Invalid item count!" + end + end, +}) + +core.register_chatcommand("hp", { + params = "", + description = "Set your health", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local hp = tonumber(param) + if not hp or core.is_nan(hp) or hp < 0 or hp > 65535 then + return false, "Missing or incorrect hp parameter!" + end + player:set_hp(hp) + return true + end, +}) + +local s_infplace = core.settings:get("devtest_infplace") +if s_infplace == "true" then + infplace = true +elseif s_infplace == "false" then + infplace = false +else + infplace = core.is_creative_enabled("") +end + +core.register_chatcommand("infplace", { + params = "", + description = "Toggle infinite node placement", + func = function(name, param) + infplace = not infplace + if infplace then + core.chat_send_all("Infinite node placement enabled!") + core.log("action", "Infinite node placement enabled") + else + core.chat_send_all("Infinite node placement disabled!") + core.log("action", "Infinite node placement disabled") + end + return true + end, +}) + +core.register_chatcommand("detach", { + params = "[]", + description = "Detach all objects nearby", + func = function(name, param) + local radius = tonumber(param) + if type(radius) ~= "number" then + radius = 8 + end + if radius < 1 then + radius = 1 + end + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local objs = core.get_objects_inside_radius(player:get_pos(), radius) + local num = 0 + for o=1, #objs do + if objs[o]:get_attach() then + objs[o]:set_detach() + num = num + 1 + end + end + return true, string.format("%d object(s) detached.", num) + end, +}) + +core.register_chatcommand("use_tool", { + params = "(dig ) | (hit ) []", + description = "Apply tool wear a number of times, as if it were used for digging", + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, "No player." + end + local mode, group, level, uses = string.match(param, "([a-z]+) ([a-z0-9]+) (-?%d+) (%d+)") + if not mode then + mode, group, level = string.match(param, "([a-z]+) ([a-z0-9]+) (-?%d+)") + uses = 1 + end + if not mode or not group or not level then + return false + end + if mode ~= "dig" and mode ~= "hit" then + return false + end + local tool = player:get_wielded_item() + local caps = tool:get_tool_capabilities() + if not caps or tool:get_count() == 0 then + return false, "No tool in hand." + end + local actual_uses = 0 + for u=1, uses do + local wear = tool:get_wear() + local dp + if mode == "dig" then + dp = core.get_dig_params({[group]=3, level=level}, caps, wear) + else + dp = core.get_hit_params({[group]=100}, caps, level, wear) + end + tool:add_wear(dp.wear) + actual_uses = actual_uses + 1 + if tool:get_count() == 0 then + break + end + end + player:set_wielded_item(tool) + if tool:get_count() == 0 then + return true, string.format("Tool used %d time(s). ".. + "The tool broke after %d use(s).", uses, actual_uses) + else + local wear = tool:get_wear() + return true, string.format("Tool used %d time(s). ".. + "Final wear=%d", uses, wear) + end + end, +}) + + +-- Unlimited node placement +core.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack) + if placer and placer:is_player() then + return infplace + end +end) + +-- Don't pick up if the item is already in the inventory +local old_handle_node_drops = core.handle_node_drops +function core.handle_node_drops(pos, drops, digger) + if not digger or not digger:is_player() or not infplace then + return old_handle_node_drops(pos, drops, digger) + end + local inv = digger:get_inventory() + if inv then + for _, item in ipairs(drops) do + if not inv:contains_item("main", item, true) then + inv:add_item("main", item) + end + end + end +end + +core.register_chatcommand("set_displayed_itemcount", { + params = "(-s \"\" [-c ]) | -a ", + description = "Set the displayed itemcount of the wielded item", + func = function(name, param) + local player = core.get_player_by_name(name) + local item = player:get_wielded_item() + local meta = item:get_meta() + local flag1 = param:sub(1, 2) + if flag1 == "-s" then + if param:sub(3, 4) ~= " \"" then + return false, "Error: Space and string with \"s expected after -s." + end + local se = param:find("\"", 5, true) + if not se then + return false, "Error: String with two \"s expected after -s." + end + local s = param:sub(5, se - 1) + if param:sub(se + 1, se + 4) == " -c " then + s = core.colorize(param:sub(se + 5), s) + end + meta:set_string("count_meta", s) + elseif flag1 == "-a" then + local num = tonumber(param:sub(4)) + if not num then + return false, "Error: Invalid number: "..param:sub(4) + end + meta:set_int("count_alignment", num) + else + return false + end + player:set_wielded_item(item) + return true, "Displayed itemcount set." + end, +}) + +core.register_chatcommand("dump_item", { + params = "", + description = "Prints a dump of the wielded item in table form", + func = function(name, param) + local player = core.get_player_by_name(name) + local item = player:get_wielded_item() + local str = dump(item:to_table()) + print(str) + return true, str + end, +}) + +core.register_chatcommand("dump_itemdef", { + params = "", + description = "Prints a dump of the wielded item's definition in table form", + func = function(name, param) + local player = core.get_player_by_name(name) + local str = dump(player:get_wielded_item():get_definition()) + print(str) + return true, str + end, +}) + +core.register_chatcommand("dump_wear_bar", { + params = "", + description = "Prints a dump of the wielded item's wear bar parameters in table form", + func = function(name, param) + local player = core.get_player_by_name(name) + local item = player:get_wielded_item() + local str = dump(item:get_wear_bar_params()) + print(str) + return true, str + end, +}) + +core.register_chatcommand("set_saturation", { + params = "", + description = "Set the saturation for current player.", + func = function(player_name, param) + local saturation = tonumber(param) + core.get_player_by_name(player_name):set_lighting({saturation = saturation }) + end +}) diff --git a/data/games/garage/mods/util_commands/mod.conf b/data/games/garage/mods/util_commands/mod.conf new file mode 100644 index 0000000..fea6dd3 --- /dev/null +++ b/data/games/garage/mods/util_commands/mod.conf @@ -0,0 +1,2 @@ +name = util_commands +description = Random server commands to make testing easier and more convenient diff --git a/data/games/garage/screenshot.png b/data/games/garage/screenshot.png new file mode 100644 index 0000000..7324883 Binary files /dev/null and b/data/games/garage/screenshot.png differ diff --git a/data/games/garage/settingtypes.txt b/data/games/garage/settingtypes.txt new file mode 100644 index 0000000..c436564 --- /dev/null +++ b/data/games/garage/settingtypes.txt @@ -0,0 +1,32 @@ +# If enabled, nodes won't be used up when placed. +# Note: This behavior can also be toggled in-game with the /infplace command. +# +# - true: enabled +# - false: disabled +# - auto: only enabled when Creative Mode is enabled (default) +devtest_infplace (Infinite node placement) enum auto true,false,auto + +# If enabled, new players receive some initial items when joining for the first time. +give_initial_stuff (Give initial stuff) bool true + +# If enabled, automated tests of the Lua API such as player health, crafting and PseudoRandom will be performed on startup. +devtest_unittests_autostart (Perform unit tests) bool false + +# If enabled, the game will use all mapgen aliases for the v6 mapgen. +# If disabled, it will only use a minimal set of mapgen aliases. +# If enabled, there should be biome-specific tree, leaves and ground nodes. If disabled, stuff should use fallback nodes (like stone instead of desert stone). +# +# Many mapgen aliases have fallback values when no value is provided. Having this setting disabled can be useful to test whether those fallback values are functional. +devtest_v6_mapgen_aliases (Use all v6 mapgen aliases) bool false + +# If enabled, the game will use dungeon stairs by enabling the corresponding mapgen aliases. +# +# Disabling this setting can be useful to test whether dungeons still work when stairs are not defined. +devtest_dungeon_stairs (Generate dungeon stairs) bool false + +# If enabled, the mapgen alias 'mapgen_mossycobble' will be used. This should enable random mossy cobblestone in dungeons. +# If disabled, it won't be used. The engine should fall back to cobble instead. +devtest_dungeon_mossycobble (Generate mossy cobblestone) bool false + +# If enabled, some very basic biomes will be registered. +devtest_register_biomes (Register biomes) bool true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..87dfe41 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + minetest: + image: lscr.io/linuxserver/minetest:latest + container_name: minetest + environment: + - PUID=1000 + - PGID=1000 + - TZ=Etc/UTC + - "CLI_ARGS=--gameid garage" + volumes: + - ./data:/defaults + - ./conf:/config/.minetest/main-config + ports: + - 30001:30000/udp + restart: unless-stopped \ No newline at end of file