From 3494eb07a042893603964c6b83503c5c5f0da94d Mon Sep 17 00:00:00 2001 From: IQuant Date: Sun, 3 Nov 2024 16:06:41 +0300 Subject: [PATCH] WIP rust api --- .gitignore | 2 +- ewext/Cargo.lock | 97 ++++++++++++++++++++ ewext/Cargo.toml | 1 + ewext/noita_api_macro/Cargo.lock | 105 ++++++++++++++++++++++ ewext/noita_api_macro/Cargo.toml | 14 +++ ewext/noita_api_macro/src/components.json | 1 + ewext/noita_api_macro/src/lib.rs | 96 ++++++++++++++++++++ ewext/src/noita.rs | 3 + scripts/parse_components.py | 28 +++++- 9 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 ewext/noita_api_macro/Cargo.lock create mode 100644 ewext/noita_api_macro/Cargo.toml create mode 100644 ewext/noita_api_macro/src/components.json create mode 100644 ewext/noita_api_macro/src/lib.rs diff --git a/.gitignore b/.gitignore index 27b9b0c3..f5bee3c9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ save_state *png~ /quant.ew/files/system/player/tmp/ /quant.ew/ewext.dll -/quant.ew/ewext0.dll \ No newline at end of file +/quant.ew/ewext0.dll diff --git a/ewext/Cargo.lock b/ewext/Cargo.lock index b471edbd..6c76b2e8 100644 --- a/ewext/Cargo.lock +++ b/ewext/Cargo.lock @@ -45,6 +45,7 @@ dependencies = [ "backtrace", "iced-x86", "libloading", + "noita_api_macro", ] [[package]] @@ -53,6 +54,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "iced-x86" version = "1.21.0" @@ -62,6 +69,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.5.0" @@ -99,6 +112,17 @@ dependencies = [ "adler2", ] +[[package]] +name = "noita_api_macro" +version = "0.1.0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", +] + [[package]] name = "object" version = "0.36.5" @@ -108,12 +132,85 @@ dependencies = [ "memchr", ] +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/ewext/Cargo.toml b/ewext/Cargo.toml index 7249b43f..93fc5769 100644 --- a/ewext/Cargo.toml +++ b/ewext/Cargo.toml @@ -15,3 +15,4 @@ strip = true libloading = "0.8.5" backtrace = "0.3.74" iced-x86 = "1.21.0" +noita_api_macro = {path = "noita_api_macro"} diff --git a/ewext/noita_api_macro/Cargo.lock b/ewext/noita_api_macro/Cargo.lock new file mode 100644 index 00000000..4d12544c --- /dev/null +++ b/ewext/noita_api_macro/Cargo.lock @@ -0,0 +1,105 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "noita_api_macro" +version = "0.1.0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/ewext/noita_api_macro/Cargo.toml b/ewext/noita_api_macro/Cargo.toml new file mode 100644 index 00000000..d584faf6 --- /dev/null +++ b/ewext/noita_api_macro/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "noita_api_macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +heck = "0.5.0" +proc-macro2 = "1.0.89" +quote = "1.0.37" +serde = { version = "1.0.214", features = ["derive"] } +serde_json = "1.0.132" diff --git a/ewext/noita_api_macro/src/components.json b/ewext/noita_api_macro/src/components.json new file mode 100644 index 00000000..e14ac4cc --- /dev/null +++ b/ewext/noita_api_macro/src/components.json @@ -0,0 +1 @@ +[{"name": "AIAttackComponent", "fields": [{"field": "use_probability", "typ": "int", "desc": "The probability for using this attack if it's otherwise possible"}, {"field": "min_distance", "typ": "float", "desc": "The minimum distance from enemy at which we can perform this attack."}, {"field": "max_distance", "typ": "float", "desc": "The maximum distance from enemy at which we can perform this attack."}, {"field": "angular_range_deg", "typ": "float", "desc": "When looking for threats/prey this is our field of view around the X axis. 90 means we scan the whole 180 degrees around the X axis, to the left and right."}, {"field": "state_duration_frames", "typ": "int", "desc": "How long do we stay in the attack state, before other states are allowed?"}, {"field": "frames_between", "typ": "int", "desc": "The minimum number of frames we wait between these attacks"}, {"field": "frames_between_global", "typ": "int", "desc": "The minimum number of frames we wait after this attack before doing any other ranged attack"}, {"field": "animation_name", "typ": "std::string", "desc": "The animation to play when performing this attack"}, {"field": "attack_landing_ranged_enabled", "typ": "bool", "desc": "If 1, we try to land before doing the attack, if there's ground near nearby under us"}, {"field": "attack_ranged_action_frame", "typ": "int", "desc": "The frame of the 'attack_ranged' animation during which the ranged attack actually occurs"}, {"field": "attack_ranged_offset_x", "typ": "float", "desc": "'attack_ranged_entity_file' is created here when performing a ranged attack"}, {"field": "attack_ranged_offset_y", "typ": "float", "desc": "'attack_ranged_entity_file' is created here when performing a ranged attack"}, {"field": "attack_ranged_root_offset_x", "typ": "float", "desc": ""}, {"field": "attack_ranged_root_offset_y", "typ": "float", "desc": ""}, {"field": "attack_ranged_use_message", "typ": "bool", "desc": "If 1, we do ranged attacks by sending a Message_UseItem"}, {"field": "attack_ranged_predict", "typ": "bool", "desc": "If 1, we attempt to predict target movement and shoot accordingly"}, {"field": "attack_ranged_entity_file", "typ": "std::string", "desc": "File to projectile entity that is created when performing a ranged attack"}, {"field": "attack_ranged_entity_count_min", "typ": "int", "desc": "Minimum number of projectiles shot when performing a ranged attack"}, {"field": "attack_ranged_entity_count_max", "typ": "int", "desc": "Maximum number of projectiles shot when performing a ranged attack"}, {"field": "attack_ranged_use_laser_sight", "typ": "bool", "desc": "If 1, we draw a laser sight to our target. Requires entity to have a sprite with tag 'laser_sight'"}, {"field": "attack_ranged_aim_rotation_enabled", "typ": "bool", "desc": "If 1, we use a laser sight"}, {"field": "attack_ranged_aim_rotation_speed", "typ": "float", "desc": "How fast can we rotate our aim to track targets"}, {"field": "attack_ranged_aim_rotation_shooting_ok_angle_deg", "typ": "float", "desc": "If our aim is closer than this to the target we shoot"}, {"field": "mRangedAttackCurrentAimAngle", "typ": "float", "desc": "which direction does our gun currently point at, physically saying?"}, {"field": "mNextFrameUsable", "typ": "int", "desc": ""}]}, {"name": "AIComponent", "fields": [{"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}, {"field": "data", "typ": "AIData*", "desc": ""}]}, {"name": "AbilityComponent", "fields": [{"field": "cooldown_frames", "typ": "int", "desc": ""}, {"field": "entity_file", "typ": "std::string", "desc": "the projectile entity file"}, {"field": "sprite_file", "typ": "std::string", "desc": ""}, {"field": "entity_count", "typ": "int", "desc": ""}, {"field": "never_reload", "typ": "bool", "desc": ""}, {"field": "reload_time_frames", "typ": "int", "desc": ""}, {"field": "mana", "typ": "float", "desc": ""}, {"field": "mana_max", "typ": "float", "desc": ""}, {"field": "mana_charge_speed", "typ": "float", "desc": ""}, {"field": "rotate_in_hand", "typ": "bool", "desc": ""}, {"field": "rotate_in_hand_amount", "typ": "float", "desc": "[0-1], how much does the item rotate related to the actual aiming angle"}, {"field": "rotate_hand_amount", "typ": "float", "desc": "[0-1], how much does hand sprite rotate related to the actual aiming angle"}, {"field": "fast_projectile", "typ": "bool", "desc": "if 1, then the velocity of the bullet is increased quite a bit. Lightning requires this"}, {"field": "swim_propel_amount", "typ": "float", "desc": ""}, {"field": "max_charged_actions", "typ": "int", "desc": ""}, {"field": "charge_wait_frames", "typ": "int", "desc": ""}, {"field": "item_recoil_recovery_speed", "typ": "float", "desc": "How quickly does the item return to resting state after getting recoil"}, {"field": "item_recoil_max", "typ": "float", "desc": "Maximum distance moved by recoil"}, {"field": "item_recoil_offset_coeff", "typ": "float", "desc": "Item distance moved by recoil = mItemRecoil * item_recoil_offset_coeff"}, {"field": "item_recoil_rotation_coeff", "typ": "float", "desc": "Item rotation by recoil = mItemRecoil * item_recoil_rotation_coeff"}, {"field": "base_item_file", "typ": "std::string", "desc": "when dropping / throwing the item, this is the base_item that we add the ability component to"}, {"field": "use_entity_file_as_projectile_info_proxy", "typ": "bool", "desc": ""}, {"field": "click_to_use", "typ": "bool", "desc": ""}, {"field": "stat_times_player_has_shot", "typ": "int", "desc": "used to track how many times player has shot this 'ability'"}, {"field": "stat_times_player_has_edited", "typ": "int", "desc": "used to track how many times this has been edited"}, {"field": "shooting_reduces_amount_in_inventory", "typ": "bool", "desc": ""}, {"field": "throw_as_item", "typ": "bool", "desc": ""}, {"field": "simulate_throw_as_item", "typ": "bool", "desc": "If 1, the item will be work as normal ability, but throwing animation is played by the user"}, {"field": "max_amount_in_inventory", "typ": "int", "desc": ""}, {"field": "amount_in_inventory", "typ": "int", "desc": ""}, {"field": "drop_as_item_on_death", "typ": "bool", "desc": ""}, {"field": "ui_name", "typ": "std::string", "desc": "way to name the weapons"}, {"field": "use_gun_script", "typ": "bool", "desc": "If 1, the default ability behaviour is replaced with one that uses the lua gun system."}, {"field": "is_petris_gun", "typ": "bool", "desc": "if 1, TODO( PETRI)"}, {"field": "gun_level", "typ": "int", "desc": "the level of the wand, set in gun_procedural.lua"}, {"field": "add_these_child_actions", "typ": "std::string", "desc": "e.g. 'bullet,bullet,damage' ... actions are parsed into a string. These are added as actual entities when the item is initialized"}, {"field": "current_slot_durability", "typ": "int", "desc": "After this many slots the last slot of the gun is removed. -1 means not initialized/infinite."}, {"field": "slot_consumption_function", "typ": "std::string", "desc": "Name of the lua function in 'gun.lua' that is called to calculate durability of the last slot in the gun"}, {"field": "mNextFrameUsable", "typ": "int", "desc": "hax, don't touch!"}, {"field": "mCastDelayStartFrame", "typ": "int", "desc": "hax, don't touch!"}, {"field": "mReloadFramesLeft", "typ": "int", "desc": "hax, don't touch!"}, {"field": "mReloadNextFrameUsable", "typ": "int", "desc": "hax, don't touch!"}, {"field": "mChargeCount", "typ": "int", "desc": "hax, don't touch!"}, {"field": "mIsInitialized", "typ": "bool", "desc": ""}, {"field": "gun_config", "typ": "ConfigGun", "desc": "Constants for gun script"}, {"field": "gunaction_config", "typ": "ConfigGunActionInfo", "desc": "Constants for gun script"}, {"field": "mAmmoLeft", "typ": "int", "desc": ""}, {"field": "mNextChargeFrame", "typ": "int", "desc": ""}, {"field": "mItemRecoil", "typ": "float", "desc": ""}]}, {"name": "AdvancedFishAIComponent", "fields": [{"field": "move_check_range_min", "typ": "float", "desc": ""}, {"field": "move_check_range_max", "typ": "float", "desc": ""}, {"field": "flock", "typ": "bool", "desc": ""}, {"field": "avoid_predators", "typ": "bool", "desc": ""}, {"field": "mHasTargetDirection", "typ": "bool", "desc": ""}, {"field": "mTargetPos", "typ": "vec2", "desc": ""}, {"field": "mTargetVec", "typ": "vec2", "desc": ""}, {"field": "mLastFramesMovementAreaMin", "typ": "vec2", "desc": ""}, {"field": "mLastFramesMovementAreaMax", "typ": "vec2", "desc": ""}, {"field": "mNumFailedTargetSearches", "typ": "uint32", "desc": ""}, {"field": "mNextFrameCheckAreWeStuck", "typ": "int", "desc": ""}, {"field": "mNextFrameCheckFlockWants", "typ": "int", "desc": ""}, {"field": "mNextFramePredatorAvoidance", "typ": "int", "desc": ""}, {"field": "mScared", "typ": "float", "desc": ""}, {"field": "mWantsToBeInFlock", "typ": "bool", "desc": ""}]}, {"name": "AltarComponent", "fields": [{"field": "recognized_entity_tags", "typ": "std::string", "desc": ""}, {"field": "uses_remaining", "typ": "int", "desc": ""}, {"field": "good_fx_material", "typ": "int", "desc": "String name of material for particles emitted on successful sacrifice"}, {"field": "neutral_fx_material", "typ": "int", "desc": "String name of material for particles emitted on successful sacrifice"}, {"field": "evil_fx_material", "typ": "int", "desc": "String name of material for particles emitted on successful sacrifice"}, {"field": "m_recognized_entity_tags", "typ": "EntityTags", "desc": ""}, {"field": "m_recognized_entity_tags_count", "typ": "uint32", "desc": ""}, {"field": "m_current_entity_tags", "typ": "EntityTags", "desc": ""}]}, {"name": "AnimalAIComponent", "fields": [{"field": "ai_state", "typ": "int", "desc": "Current state of ai, defines what the animal is doing"}, {"field": "ai_state_timer", "typ": "int", "desc": "If not 0, then we wait till this frame to pop current state from our state stack"}, {"field": "keep_state_alive_when_enabled", "typ": "bool", "desc": "if 1, will ensure state timer keeps current state alive for a while when Component is Enabled"}, {"field": "preferred_job", "typ": "std::string", "desc": "We always do this job, unless interrupted (i.e. by taking fire damage)"}, {"field": "escape_if_damaged_probability", "typ": "int", "desc": "the chance of escaping if someone damages us. only works if 'can_fly = 0 '"}, {"field": "attack_if_damaged_probability", "typ": "int", "desc": "the chance of counter-attacking if someone damages us, and we didn't escape"}, {"field": "eye_offset_x", "typ": "int", "desc": "We cast rays from our position + eye_offset to check if we can see something"}, {"field": "eye_offset_y", "typ": "int", "desc": "We cast rays from our position + eye_offset to check if we can see something"}, {"field": "attack_only_if_attacked", "typ": "bool", "desc": "If 1, we never attack anyone unless attacked before by someone"}, {"field": "dont_counter_attack_own_herd", "typ": "bool", "desc": "If 1, we don't attack members of our herd even if they accidentally attack us"}, {"field": "creature_detection_range_x", "typ": "float", "desc": "When looking for threats/prey this is the max distance from us on the X axis we scan"}, {"field": "creature_detection_range_y", "typ": "float", "desc": "When looking for threats/prey this is the max distance from us on the Y axis we scan"}, {"field": "creature_detection_angular_range_deg", "typ": "float", "desc": "When looking for threats/prey this is our field of view around the X axis. 90 means we scan the whole 180 degrees around the X axis, to the left and right"}, {"field": "creature_detection_check_every_x_frames", "typ": "int", "desc": "Checks for threats/prey take place at least this many frames apart from each other"}, {"field": "max_distance_to_cam_to_start_hunting", "typ": "float", "desc": "JobDefault idles before we've been once at least this close to the camera"}, {"field": "pathfinding_max_depth_no_target", "typ": "int", "desc": "The maximum depth (in nodes) path search use when we have not found prey yet"}, {"field": "pathfinding_max_depth_has_target", "typ": "int", "desc": "The maximum depth (in nodes) path search use when we have found prey"}, {"field": "aggressiveness_min", "typ": "float", "desc": "what's the initial random aggressiveness of this creature"}, {"field": "aggressiveness_max", "typ": "float", "desc": "what's the initial random aggressiveness of this creature"}, {"field": "tries_to_ranged_attack_friends", "typ": "bool", "desc": "if 1, the AI tries to attack whoever it considers a friend based on herd_ids, CHARMED and BERSERK status etc. useful e.g. for healers."}, {"field": "attack_melee_enabled", "typ": "bool", "desc": "If 1, and melee attack has been configured, we can perform melee attacks"}, {"field": "attack_dash_enabled", "typ": "bool", "desc": "If 1, and dash attack has been configured, we can perform dash attacks (a long-distance melee attack where we dash towards the enemy)"}, {"field": "attack_landing_ranged_enabled", "typ": "bool", "desc": "If 1, and ranged attack has been configured, we can perform ranged attacks"}, {"field": "attack_ranged_enabled", "typ": "bool", "desc": "If 1, and ranged attack has been configured, we can perform ranged attacks"}, {"field": "attack_knockback_multiplier", "typ": "float", "desc": "If not 0, melee and dash attacks cause knockback to target"}, {"field": "is_static_turret", "typ": "bool", "desc": "If 1, we can only attack in one fixed direction"}, {"field": "attack_melee_max_distance", "typ": "int", "desc": "Maximum distance at which we can perform a melee attack"}, {"field": "attack_melee_action_frame", "typ": "int", "desc": "The animation frame during which the melee attack damage is inflicted and visual effects are created"}, {"field": "attack_melee_frames_between", "typ": "int", "desc": "The minimum number of frames we wait between melee attacks"}, {"field": "attack_melee_damage_min", "typ": "float", "desc": "Melee attack damage inclusive minimum amount. The damage is randomized between melee attack_damage_min and attack_melee_damage_max"}, {"field": "attack_melee_damage_max", "typ": "float", "desc": "Melee attack damage inclusive maximum amount. The damage is randomized between melee attack_damage_min and attack_melee_damage_max"}, {"field": "attack_melee_impulse_vector_x", "typ": "float", "desc": "The x component of the impulse that is applied to damaged entities"}, {"field": "attack_melee_impulse_vector_y", "typ": "float", "desc": "The y component of the impulse that is applied to damaged entities"}, {"field": "attack_melee_impulse_multiplier", "typ": "float", "desc": "A multiplier applied to attack_melee_impulse"}, {"field": "attack_melee_offset_x", "typ": "float", "desc": "Melee attack particle effects are created here"}, {"field": "attack_melee_offset_y", "typ": "float", "desc": "Melee attack particle effects are created here"}, {"field": "attack_melee_finish_enabled", "typ": "bool", "desc": "If 1, we perform a finishing move when our attack would kill the target using the 'attack_finish' animation"}, {"field": "attack_melee_finish_action_frame", "typ": "int", "desc": "The animation frame during which the melee attack finishing move damage is inflicted and visual effects are created"}, {"field": "attack_dash_distance", "typ": "float", "desc": "The maximum distance from enemy at which we can perform a dash attack. If a normal melee attack is possible we always do that instead"}, {"field": "attack_dash_frames_between", "typ": "int", "desc": "The minimum number of frames we wait between dash attacks"}, {"field": "attack_dash_damage", "typ": "float", "desc": "The amount of damage inflicted by the dash attack"}, {"field": "attack_dash_speed", "typ": "float", "desc": "The speed at which we dash"}, {"field": "attack_dash_lob", "typ": "float", "desc": "The smaller this value is the more curved our dash attack trajectory is"}, {"field": "attack_ranged_min_distance", "typ": "float", "desc": "The minimum distance from enemy at which we can perform a ranged attack."}, {"field": "attack_ranged_max_distance", "typ": "float", "desc": "The maximum distance from enemy at which we can perform a ranged attack."}, {"field": "attack_ranged_action_frame", "typ": "int", "desc": "The frame of the 'attack_ranged' animation during which the ranged attack actually occurs"}, {"field": "attack_ranged_offset_x", "typ": "float", "desc": "'attack_ranged_entity_file' is created here when performing a ranged attack"}, {"field": "attack_ranged_offset_y", "typ": "float", "desc": "'attack_ranged_entity_file' is created here when performing a ranged attack"}, {"field": "attack_ranged_use_message", "typ": "bool", "desc": "If 1, we do ranged attacks by sending a Message_UseItem"}, {"field": "attack_ranged_predict", "typ": "bool", "desc": "If 1, we attempt to predict target movement and shoot accordingly"}, {"field": "attack_ranged_entity_file", "typ": "std::string", "desc": "File to projectile entity that is created when performing a ranged attack"}, {"field": "attack_ranged_entity_count_min", "typ": "int", "desc": "Minimum number of projectiles shot when performing a ranged attack"}, {"field": "attack_ranged_entity_count_max", "typ": "int", "desc": "Maximum number of projectiles shot when performing a ranged attack"}, {"field": "attack_ranged_use_laser_sight", "typ": "bool", "desc": "If 1, we draw a laser sight to our target. Requires entity to have a sprite with tag 'laser_sight'"}, {"field": "attack_ranged_laser_sight_beam_kind", "typ": "bool", "desc": "0 = red, 1 = blue "}, {"field": "attack_ranged_aim_rotation_enabled", "typ": "bool", "desc": ""}, {"field": "attack_ranged_aim_rotation_speed", "typ": "float", "desc": ""}, {"field": "attack_ranged_aim_rotation_shooting_ok_angle_deg", "typ": "float", "desc": ""}, {"field": "attack_ranged_state_duration_frames", "typ": "int", "desc": "How long do we stay in the attack state, before other states are allowed?"}, {"field": "hide_from_prey", "typ": "bool", "desc": "If 1, we attempt to hide from our target after a succesful attack"}, {"field": "hide_from_prey_target_distance", "typ": "float", "desc": "The minimum distance from our target where we should move when hiding"}, {"field": "hide_from_prey_time", "typ": "int", "desc": "The number of frames we spend hiding and staying hiding"}, {"field": "food_eating_create_particles", "typ": "bool", "desc": "If 1, we replace eaten cells with particles made of this material"}, {"field": "eating_area_radius_x", "typ": "int", "desc": "1/2 width of the area from which we eat food"}, {"field": "eating_area_radius_y", "typ": "int", "desc": "1/2 height of the area from which we eat food"}, {"field": "mouth_offset_x", "typ": "int", "desc": "The center of the area from which we eat food"}, {"field": "mouth_offset_y", "typ": "int", "desc": "The center of the area from which we eat food"}, {"field": "defecates_and_pees", "typ": "bool", "desc": "If 1, we occasionally take a leak or a dump"}, {"field": "butt_offset_x", "typ": "int", "desc": "Bodily wastes are created here"}, {"field": "butt_offset_y", "typ": "int", "desc": "Bodily wastes are created here"}, {"field": "pee_velocity_x", "typ": "float", "desc": "The velocity at which our piss gets shot"}, {"field": "pee_velocity_y", "typ": "float", "desc": "The velocity at which our piss gets shot"}, {"field": "needs_food", "typ": "bool", "desc": "If 1, we stop to eat if we encounter 'food_material' cells"}, {"field": "sense_creatures", "typ": "bool", "desc": "If 1, we occasionally search our surroundings for prey and threats"}, {"field": "sense_creatures_through_walls", "typ": "bool", "desc": "If 1, will see creatures even if the wall raycast fails"}, {"field": "can_fly", "typ": "bool", "desc": "If 1, we can fly. Please set 'PathFindingComponent.can_fly' to 1 as well if this is 1"}, {"field": "can_walk", "typ": "bool", "desc": "If 1, we can walk. Please set 'PathFindingComponent.can_walk' to 1 as well if this is 1"}, {"field": "path_distance_to_target_node_to_turn_around", "typ": "int", "desc": "If we're further than this from target path finding node on the X-axis we turn to face it"}, {"field": "path_cleanup_explosion_radius", "typ": "float", "desc": "If we get stuck on ground we create an explosion this big to clear our surroundings a bit"}, {"field": "max_distance_to_move_from_home", "typ": "float", "desc": ""}, {"field": "attack_melee_finish_config_explosion", "typ": "ConfigExplosion", "desc": "If we have explosion, it's the setup for it"}, {"field": "attack_ranged_frames_between", "typ": "LensValue", "desc": "The minimum number of frames we wait between ranged attacks"}, {"field": "food_material", "typ": "int", "desc": "The cell material we eat if encountering said material and 'needs_food' is 1"}, {"field": "food_particle_effect_material", "typ": "int", "desc": "We create particles made of this material when eating if 'food_eating_create_particles' is 1"}, {"field": "mAggression", "typ": "LensValue", "desc": "the greater this value the more likely we're to attack creatures from other herds"}, {"field": "mAiStateStack", "typ": "AI_STATE_STACK", "desc": "a stack of actions and times they take, we can push new actions to the front and pop them from there"}, {"field": "mAiStateLastSwitchFrame", "typ": "int", "desc": "when was the last time we switched a state"}, {"field": "mAiStatePrev", "typ": "int", "desc": "previous AI state"}, {"field": "mCreatureDetectionNextCheck", "typ": "int", "desc": "threat/prey check, next time we check for threat/prey"}, {"field": "mGreatestThreat", "typ": "EntityID", "desc": "the entity we consider to be our greatest threat"}, {"field": "mGreatestPrey", "typ": "EntityID", "desc": "the entity we consider to be our most important prey"}, {"field": "mSelectedMultiAttack", "typ": "int", "desc": "which AIAttackComponent attack are we using?"}, {"field": "mHasFoundPrey", "typ": "bool", "desc": "1, if we have ever found prey"}, {"field": "mHasBeenAttackedByPlayer", "typ": "bool", "desc": "1, if we have been ever attacked"}, {"field": "mHasStartedAttacking", "typ": "bool", "desc": "1, if we have ever started attacking anyone"}, {"field": "mNearbyFoodCount", "typ": "int", "desc": "amount of 'food_material' near us"}, {"field": "mEatNextFrame", "typ": "int", "desc": "next frame we can eat"}, {"field": "mEatTime", "typ": "int", "desc": "time we've been constantly eating"}, {"field": "mFrameNextGiveUp", "typ": "int", "desc": "next frame we consider ourselves to be stuck"}, {"field": "mLastFramesMovementAreaMin", "typ": "vec2", "desc": "AABB min of the area where we've been since the last time we got stuck"}, {"field": "mLastFramesMovementAreaMax", "typ": "vec2", "desc": "AABB max of the area where we've been since the last time we got stuck"}, {"field": "mFoodMaterialId", "typ": "int", "desc": "cached id of 'food_material'"}, {"field": "mFoodParticleEffectMaterialId", "typ": "int", "desc": "cached id of 'food_particle_effect_material'"}, {"field": "mNextJumpLob", "typ": "float", "desc": "we use this for next jump"}, {"field": "mNextJumpTarget", "typ": "vec2", "desc": "we use this for next jump"}, {"field": "mNextJumpHasVelocity", "typ": "bool", "desc": "we use this for next jump"}, {"field": "mLastFrameJumped", "typ": "int", "desc": "previous frame we launched into a jump"}, {"field": "mFramesWithoutTarget", "typ": "int", "desc": ""}, {"field": "mLastFrameCanDamageOwnHerd", "typ": "int", "desc": ""}, {"field": "mHomePosition", "typ": "vec2", "desc": "where our home is located"}, {"field": "mLastFrameAttackWasDone", "typ": "int", "desc": "when was the last time we did an attack (not necessarily did damage to anyone though)"}, {"field": "mNextFrameCanCallFriend", "typ": "int", "desc": ""}, {"field": "mNextFrameRespondFriend", "typ": "int", "desc": ""}, {"field": "mHasNoticedPlayer", "typ": "bool", "desc": "if 1, we have noticed player or player projectile"}, {"field": "mRangedAttackCurrentAimAngle", "typ": "float", "desc": "which direction does our gun currently point at, physically saying?"}, {"field": "mRangedAttackNextFrame", "typ": "int", "desc": "next frame we can perform a ranged attack"}, {"field": "mMeleeAttackNextFrame", "typ": "int", "desc": "next frame we can perform a melee attack"}, {"field": "mNextMeleeAttackDamage", "typ": "float", "desc": "the amount of damage our next melee attack will cause. used by finishing move logic"}, {"field": "mMeleeAttacking", "typ": "bool", "desc": "1, if we're doing a melee attack"}, {"field": "mMeleeAttackDashNextFrame", "typ": "int", "desc": "the next frame we can perform a melee attack"}, {"field": "mCurrentJob", "typ": "RtsUnitGoal", "desc": "info about our current job. sorta legacy and could be simplified because the RTS logic is not used anywhere but doesn't have much overhead either."}]}, {"name": "ArcComponent", "fields": [{"field": "lifetime", "typ": "int", "desc": "remaining number of frames the arc exists"}, {"field": "type", "typ": "ARC_TYPE::Enum", "desc": "which implementation the arc should use"}, {"field": "material", "typ": "int", "desc": "string name for the material the arc is made of"}, {"field": "mArcTarget", "typ": "EntityID", "desc": "if 'mArcTarget' points to an existing entity a lighting arc will be created between this entity and 'mArcTarget'"}]}, {"name": "AreaDamageComponent", "fields": [{"field": "circle_radius", "typ": "float", "desc": "if > 0, will only damage entities inside the aabb rectangle which are closer than 'circle_radius' to the aabb center."}, {"field": "damage_per_frame", "typ": "float", "desc": ""}, {"field": "update_every_n_frame", "typ": "int", "desc": ""}, {"field": "entity_responsible", "typ": "EntityID", "desc": "if NULL, will try to figure out who to blame"}, {"field": "death_cause", "typ": "std::string", "desc": ""}, {"field": "entities_with_tag", "typ": "std::string", "desc": "damage entities with this tag"}, {"field": "aabb_min", "typ": "vec2", "desc": ""}, {"field": "aabb_max", "typ": "vec2", "desc": ""}, {"field": "damage_type", "typ": "DAMAGE_TYPES::Enum", "desc": "the damage type"}]}, {"name": "AttachToEntityComponent", "fields": [{"field": "only_position", "typ": "bool", "desc": "if 1, we only inherit position. it is calculated as follows: target_position + target_offset * target_scale"}, {"field": "target_hotspot_tag", "typ": "std::string", "desc": "if set, we apply the offset of target HotSpot with this tag"}, {"field": "target_sprite_id", "typ": "int", "desc": "if >= 0, the Nth sprite transform in target entity is inherited"}, {"field": "rotate_based_on_x_scale", "typ": "bool", "desc": "if 1, the rotation is set to 0 deg if scale >= 0 else to 180 deg"}, {"field": "destroy_component_when_target_is_gone", "typ": "bool", "desc": "should probably be on by default"}, {"field": "Transform", "typ": "types::xform", "desc": ""}, {"field": "target", "typ": "EntityID", "desc": "EntityID of the entity we're attached to. This will fail after save/load, unfortunately"}, {"field": "mUpdateFrame", "typ": "int", "desc": ""}]}, {"name": "AudioComponent", "fields": [{"field": "file", "typ": "std::string", "desc": ""}, {"field": "event_root", "typ": "std::string", "desc": ""}, {"field": "audio_physics_material", "typ": "std::string", "desc": ""}, {"field": "set_latest_event_position", "typ": "bool", "desc": ""}, {"field": "remove_latest_event_on_destroyed", "typ": "bool", "desc": ""}, {"field": "send_message_on_event_dead", "typ": "bool", "desc": ""}, {"field": "play_only_if_visible", "typ": "bool", "desc": "plays sounds only if entity position is on screen and not covered by fog of war"}, {"field": "m_audio_physics_material", "typ": "int", "desc": ""}, {"field": "m_latest_source", "typ": "AudioSourceHandle", "desc": ""}]}, {"name": "AudioListenerComponent", "fields": [{"field": "z", "typ": "float", "desc": ""}]}, {"name": "AudioLoopComponent", "fields": [{"field": "file", "typ": "std::string", "desc": ""}, {"field": "event_name", "typ": "std::string", "desc": ""}, {"field": "auto_play", "typ": "bool", "desc": ""}, {"field": "auto_play_if_enabled", "typ": "bool", "desc": ""}, {"field": "play_on_component_enable", "typ": "bool", "desc": ""}, {"field": "calculate_material_lowpass", "typ": "bool", "desc": ""}, {"field": "set_speed_parameter", "typ": "bool", "desc": ""}, {"field": "set_speed_parameter_only_based_on_x_movement", "typ": "bool", "desc": ""}, {"field": "set_speed_parameter_only_based_on_y_movement", "typ": "bool", "desc": ""}, {"field": "volume_autofade_speed", "typ": "float", "desc": ""}, {"field": "m_volume", "typ": "float", "desc": ""}, {"field": "m_intensity", "typ": "float", "desc": ""}, {"field": "m_intensity2", "typ": "float", "desc": ""}, {"field": "m_source", "typ": "AudioSourceHandle", "desc": ""}, {"field": "m_frame_created", "typ": "int", "desc": ""}]}, {"name": "BiomeTrackerComponent", "fields": [{"field": "limit_to_every_n_frame", "typ": "int", "desc": "if > 1, we will only check the biome every n frames"}, {"field": "unsafe_current_biome", "typ": "Biome*", "desc": "DO NOT ACCESS, since this can be in valid"}, {"field": "current_biome_name", "typ": "std::string", "desc": "used to track in which biome we are at"}]}, {"name": "BlackHoleComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}, {"field": "particle_attractor_force", "typ": "float", "desc": ""}, {"field": "damage_probability", "typ": "float", "desc": ""}, {"field": "damage_amount", "typ": "float", "desc": ""}, {"field": "m_particle_attractor_id", "typ": "int16", "desc": ""}]}, {"name": "BookComponent", "fields": [{"field": "TEMP_TEMPY", "typ": "float", "desc": ""}, {"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}]}, {"name": "BossDragonComponent", "fields": [{"field": "speed", "typ": "float", "desc": ""}, {"field": "speed_hunt", "typ": "float", "desc": ""}, {"field": "acceleration", "typ": "float", "desc": ""}, {"field": "direction_adjust_speed", "typ": "float", "desc": ""}, {"field": "direction_adjust_speed_hunt", "typ": "float", "desc": ""}, {"field": "gravity", "typ": "float", "desc": ""}, {"field": "tail_gravity", "typ": "float", "desc": ""}, {"field": "part_distance", "typ": "float", "desc": ""}, {"field": "ground_check_offset", "typ": "int", "desc": ""}, {"field": "eat_ground_radius", "typ": "float", "desc": ""}, {"field": "eat_ground", "typ": "bool", "desc": "does the worm destroy the ground it moves through or not?"}, {"field": "hitbox_radius", "typ": "float", "desc": ""}, {"field": "bite_damage", "typ": "float", "desc": "how much damage does this do when it hits an entity"}, {"field": "target_kill_radius", "typ": "float", "desc": ""}, {"field": "target_kill_ragdoll_force", "typ": "float", "desc": ""}, {"field": "hunt_box_radius", "typ": "float", "desc": ""}, {"field": "random_target_box_radius", "typ": "float", "desc": ""}, {"field": "new_hunt_target_check_every", "typ": "int", "desc": ""}, {"field": "new_random_target_check_every", "typ": "int", "desc": ""}, {"field": "jump_cam_shake", "typ": "float", "desc": ""}, {"field": "jump_cam_shake_distance", "typ": "float", "desc": ""}, {"field": "eat_anim_wait_mult", "typ": "float", "desc": ""}, {"field": "projectile_1", "typ": "std::string", "desc": ""}, {"field": "projectile_1_count", "typ": "int", "desc": ""}, {"field": "projectile_2", "typ": "std::string", "desc": ""}, {"field": "projectile_2_count", "typ": "int", "desc": ""}, {"field": "ragdoll_filename", "typ": "std::string", "desc": ""}, {"field": "mTargetEntityId", "typ": "int", "desc": ""}, {"field": "mTargetVec", "typ": "vec2", "desc": ""}, {"field": "mGravVelocity", "typ": "float", "desc": ""}, {"field": "mSpeed", "typ": "float", "desc": ""}, {"field": "mRandomTarget", "typ": "vec2", "desc": ""}, {"field": "mLastLivingTargetPos", "typ": "vec2", "desc": ""}, {"field": "mNextTargetCheckFrame", "typ": "int", "desc": ""}, {"field": "mNextHuntTargetCheckFrame", "typ": "int", "desc": ""}, {"field": "mOnGroundPrev", "typ": "bool", "desc": ""}, {"field": "mMaterialIdPrev", "typ": "int", "desc": ""}, {"field": "mPhase", "typ": "int", "desc": ""}, {"field": "mNextPhaseSwitchTime", "typ": "int", "desc": ""}, {"field": "mPartDistance", "typ": "float", "desc": ""}, {"field": "mIsInitialized", "typ": "bool", "desc": ""}]}, {"name": "BossHealthBarComponent", "fields": [{"field": "gui", "typ": "bool", "desc": ""}, {"field": "gui_special_final_boss", "typ": "bool", "desc": ""}, {"field": "in_world", "typ": "bool", "desc": ""}, {"field": "gui_max_distance_visible", "typ": "float", "desc": ""}, {"field": "mOldSpritesDestroyed", "typ": "bool", "desc": ""}]}, {"name": "CameraBoundComponent", "fields": [{"field": "enabled", "typ": "bool", "desc": "If enabled, kills this component if it's outside the camera distance"}, {"field": "distance", "typ": "float", "desc": "Distance in pixels from the center of camera, if outside this distance the entity is destroyed"}, {"field": "distance_border", "typ": "float", "desc": "Offset towards camera in pixels from 'distance' where the entity is respawned if it was frozen"}, {"field": "max_count", "typ": "int", "desc": "If more than 'max_count' entities of this type exist the one furthest from camera is destroyed"}, {"field": "freeze_on_distance_kill", "typ": "bool", "desc": "If true and the entity went too far - this entity will be stored so we can later respawn it where it was destroyed because it got too far from the camera?"}, {"field": "freeze_on_max_count_kill", "typ": "bool", "desc": "If true and the entity was one too many of its kind - this entity will be stored so we can later respawn it where it was destroyed because it got too far from the camera?"}]}, {"name": "CardinalMovementComponent", "fields": [{"field": "horizontal_movement", "typ": "bool", "desc": "allow horizontal movement"}, {"field": "vertical_movement", "typ": "bool", "desc": "allow vertical movement"}, {"field": "intercardinal_movement", "typ": "bool", "desc": "allow intercardinal movement"}, {"field": "mPrevPos", "typ": "vec2", "desc": ""}]}, {"name": "CellEaterComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}, {"field": "eat_probability", "typ": "int", "desc": ""}, {"field": "only_stain", "typ": "bool", "desc": ""}, {"field": "eat_dynamic_physics_bodies", "typ": "bool", "desc": ""}, {"field": "limited_materials", "typ": "bool", "desc": "if true, will only eat the materials defined in material_list"}, {"field": "ignored_material_tag", "typ": "std::string", "desc": "if set, will not eat any materials with this tag. please note that this lowers the performance of cell eating by some amount."}, {"field": "ignored_material", "typ": "int", "desc": "String name of a material that shouldn't be eaten by the component"}, {"field": "materials", "typ": "VEC_OF_MATERIALS", "desc": "is a list of accepted materials sorted"}]}, {"name": "CharacterCollisionComponent", "fields": [{"field": "getting_crushed_threshold", "typ": "int", "desc": ""}, {"field": "moving_up_before_getting_crushed_threshold", "typ": "int", "desc": ""}, {"field": "getting_crushed_counter", "typ": "int", "desc": "1.12.2018 - Is this still used?"}, {"field": "stuck_in_ground_counter", "typ": "int", "desc": "used this mostly for player to figure out if it's stuck in ground"}, {"field": "mCollidedHorizontally", "typ": "bool", "desc": ""}]}, {"name": "CharacterDataComponent", "fields": [{"field": "platforming_type", "typ": "int", "desc": "0 = oldest, 1 = newer, 2 = safest"}, {"field": "mass", "typ": "float", "desc": "1.0 = approx. mass of player"}, {"field": "buoyancy_check_offset_y", "typ": "int", "desc": ""}, {"field": "liquid_velocity_coeff", "typ": "float", "desc": "how much do liquids move this character. e.g. when standing in a flowing river"}, {"field": "gravity", "typ": "float", "desc": ""}, {"field": "fly_recharge_spd", "typ": "float", "desc": ""}, {"field": "fly_recharge_spd_ground", "typ": "float", "desc": ""}, {"field": "flying_needs_recharge", "typ": "bool", "desc": "const variable... player has this as true"}, {"field": "flying_in_air_wait_frames", "typ": "int", "desc": "to fix the tap tap tap flying cheese, we wait this many frames before recharging in air"}, {"field": "flying_recharge_removal_frames", "typ": "int", "desc": "another fix to the tap tap - this is how many frames from pressing down up we'll remove fly charge"}, {"field": "climb_over_y", "typ": "int", "desc": ""}, {"field": "check_collision_max_size_x", "typ": "int", "desc": ""}, {"field": "check_collision_max_size_y", "typ": "int", "desc": ""}, {"field": "is_on_ground", "typ": "bool", "desc": ""}, {"field": "is_on_slippery_ground", "typ": "bool", "desc": ""}, {"field": "ground_stickyness", "typ": "float", "desc": ""}, {"field": "effect_hit_ground", "typ": "bool", "desc": ""}, {"field": "eff_hg_damage_min", "typ": "int", "desc": "if we want to damage ground when hitting it... this is the place"}, {"field": "eff_hg_damage_max", "typ": "int", "desc": "if we want to damage ground when hitting it... this is the place"}, {"field": "eff_hg_position_x", "typ": "float", "desc": ""}, {"field": "eff_hg_position_y", "typ": "float", "desc": ""}, {"field": "eff_hg_size_x", "typ": "float", "desc": ""}, {"field": "eff_hg_size_y", "typ": "float", "desc": ""}, {"field": "eff_hg_velocity_min_x", "typ": "float", "desc": ""}, {"field": "eff_hg_velocity_max_x", "typ": "float", "desc": ""}, {"field": "eff_hg_velocity_min_y", "typ": "float", "desc": ""}, {"field": "eff_hg_velocity_max_y", "typ": "float", "desc": ""}, {"field": "eff_hg_offset_y", "typ": "float", "desc": ""}, {"field": "eff_hg_update_box2d", "typ": "bool", "desc": "if true, will move physics bodies that it hits"}, {"field": "eff_hg_b2force_multiplier", "typ": "float", "desc": "multiplies the velocity with this..."}, {"field": "destroy_ground", "typ": "float", "desc": "how much damage do we do the ground when land on it"}, {"field": "send_transform_update_message", "typ": "bool", "desc": "if 1, will send Message_TransformUpdated to updated entities and their children when the component is processed by PlayerCollisionSystem or CharacterCollisionSystem"}, {"field": "dont_update_velocity_and_xform", "typ": "bool", "desc": "might be useful if you want to use CharacterCollisionSystem to only update on_ground status"}, {"field": "mFlyingTimeLeft", "typ": "float", "desc": "How much flying energy do we have left? - NOTE( Petri ): 1.3.2023 - This used to be a private variable. It was changed to fix the save/load infinite flying bug."}, {"field": "collision_aabb_min_x", "typ": "LensValue", "desc": ""}, {"field": "collision_aabb_max_x", "typ": "LensValue", "desc": ""}, {"field": "collision_aabb_min_y", "typ": "LensValue", "desc": ""}, {"field": "collision_aabb_max_y", "typ": "LensValue", "desc": ""}, {"field": "fly_time_max", "typ": "LensValue", "desc": "how much flying energy + "}, {"field": "mFramesOnGround", "typ": "int", "desc": ""}, {"field": "mLastFrameOnGround", "typ": "int", "desc": ""}, {"field": "mVelocity", "typ": "vec2", "desc": ""}, {"field": "mCollidedHorizontally", "typ": "bool", "desc": "moved this here from CharacterCollisionComponent - since that is multithreaded and we needed a non multithreaded version"}]}, {"name": "CharacterPlatformingComponent", "fields": [{"field": "jump_velocity_x", "typ": "float", "desc": ""}, {"field": "jump_velocity_y", "typ": "float", "desc": ""}, {"field": "jump_keydown_buffer", "typ": "int", "desc": ""}, {"field": "fly_speed_mult", "typ": "float", "desc": "AI stuff"}, {"field": "fly_speed_change_spd", "typ": "float", "desc": "player"}, {"field": "fly_model_player", "typ": "bool", "desc": "if true, uses player fly model"}, {"field": "fly_smooth_y", "typ": "bool", "desc": "if true, smooths out the AI fly model"}, {"field": "accel_x", "typ": "float", "desc": ""}, {"field": "accel_x_air", "typ": "float", "desc": ""}, {"field": "pixel_gravity", "typ": "float", "desc": ""}, {"field": "swim_idle_buoyancy_coeff", "typ": "float", "desc": ""}, {"field": "swim_down_buoyancy_coeff", "typ": "float", "desc": ""}, {"field": "swim_up_buoyancy_coeff", "typ": "float", "desc": ""}, {"field": "swim_drag", "typ": "float", "desc": "when in water velocity *= swim_drag"}, {"field": "swim_extra_horizontal_drag", "typ": "float", "desc": "when in water velocity.x *= swim_extra_horizontal_drag"}, {"field": "mouse_look", "typ": "bool", "desc": ""}, {"field": "mouse_look_buffer", "typ": "float", "desc": ""}, {"field": "keyboard_look", "typ": "bool", "desc": "if true, turns based on if left or right has been pressed down"}, {"field": "turning_buffer", "typ": "float", "desc": ""}, {"field": "animation_to_play", "typ": "std::string", "desc": ""}, {"field": "animation_to_play_next", "typ": "std::string", "desc": ""}, {"field": "run_animation_velocity_switching_threshold", "typ": "float", "desc": ""}, {"field": "run_animation_velocity_switching_enabled", "typ": "bool", "desc": ""}, {"field": "turn_animation_frames_between", "typ": "int", "desc": ""}, {"field": "precision_jumping_max_duration_frames", "typ": "int", "desc": "maximum duration of precision jump or knockback. -1 = infinite"}, {"field": "audio_liquid_splash_intensity", "typ": "float", "desc": ""}, {"field": "velocity_min_x", "typ": "LensValue", "desc": ""}, {"field": "velocity_max_x", "typ": "LensValue", "desc": ""}, {"field": "velocity_min_y", "typ": "LensValue", "desc": ""}, {"field": "velocity_max_y", "typ": "LensValue", "desc": ""}, {"field": "run_velocity", "typ": "LensValue", "desc": ""}, {"field": "fly_velocity_x", "typ": "LensValue", "desc": ""}, {"field": "fly_speed_max_up", "typ": "LensValue", "desc": ""}, {"field": "fly_speed_max_down", "typ": "LensValue", "desc": ""}, {"field": "mExAnimationPos", "typ": "vec2", "desc": ""}, {"field": "mFramesInAirCounter", "typ": "int", "desc": ""}, {"field": "mIsPrecisionJumping", "typ": "bool", "desc": ""}, {"field": "mPrecisionJumpingTime", "typ": "int", "desc": ""}, {"field": "mPrecisionJumpingSpeedX", "typ": "float", "desc": ""}, {"field": "mPrecisionJumpingTimeLeft", "typ": "int", "desc": ""}, {"field": "mFlyThrottle", "typ": "float", "desc": ""}, {"field": "mSmoothedFlyingTargetY", "typ": "float", "desc": ""}, {"field": "mJetpackEmitting", "typ": "int", "desc": "-1 = undefined, 0 = not emitting, 1 = emitting"}, {"field": "mNextTurnAnimationFrame", "typ": "int", "desc": ""}, {"field": "mFramesNotSwimming", "typ": "int", "desc": "0 = currently swimming"}, {"field": "mFramesSwimming", "typ": "int", "desc": "0 = not currently swimming"}, {"field": "mShouldCrouch", "typ": "bool", "desc": ""}, {"field": "mShouldCrouchPrev", "typ": "bool", "desc": ""}, {"field": "mLastPostureSwitchFrame", "typ": "int", "desc": ""}, {"field": "mLookOverrideLastFrame", "typ": "int", "desc": ""}, {"field": "mLookOverrideDirection", "typ": "int", "desc": ""}]}, {"name": "CharacterStatsComponent", "fields": [{"field": "stats", "typ": "CharacterStatsModifier", "desc": ""}]}, {"name": "CollisionTriggerComponent", "fields": [{"field": "width", "typ": "float", "desc": ""}, {"field": "height", "typ": "float", "desc": ""}, {"field": "radius", "typ": "float", "desc": ""}, {"field": "required_tag", "typ": "std::string", "desc": ""}, {"field": "remove_component_when_triggered", "typ": "bool", "desc": ""}, {"field": "destroy_this_entity_when_triggered", "typ": "bool", "desc": ""}, {"field": "timer_for_destruction", "typ": "int", "desc": ""}, {"field": "self_trigger", "typ": "bool", "desc": "if true, the shooter can trigger it"}, {"field": "skip_self_frames", "typ": "int", "desc": "skips checks against self during these frames"}, {"field": "mTimer", "typ": "int", "desc": ""}]}, {"name": "ConsumableTeleportComponent", "fields": [{"field": "create_other_end", "typ": "bool", "desc": ""}, {"field": "is_at_home", "typ": "bool", "desc": ""}, {"field": "collision_radius", "typ": "float", "desc": ""}, {"field": "target_id", "typ": "uint32", "desc": ""}, {"field": "id", "typ": "uint32", "desc": ""}, {"field": "mNextUsableFrame", "typ": "int", "desc": ""}, {"field": "mHasOtherEnd", "typ": "bool", "desc": ""}, {"field": "target_location", "typ": "vec2", "desc": ""}]}, {"name": "ControllerGoombaAIComponent", "fields": [{"field": "auto_turn_around_enabled", "typ": "bool", "desc": "disable this if you don't want creature to 'look around', while standing still"}, {"field": "wait_to_turn_around", "typ": "int", "desc": ""}, {"field": "wall_hit_wait", "typ": "int", "desc": ""}, {"field": "check_wall_detection", "typ": "bool", "desc": ""}, {"field": "wall_detection_aabb_min_x", "typ": "float", "desc": ""}, {"field": "wall_detection_aabb_max_x", "typ": "float", "desc": ""}, {"field": "wall_detection_aabb_min_y", "typ": "float", "desc": ""}, {"field": "wall_detection_aabb_max_y", "typ": "float", "desc": ""}, {"field": "check_floor_detection", "typ": "bool", "desc": ""}, {"field": "floor_detection_aabb_min_x", "typ": "float", "desc": ""}, {"field": "floor_detection_aabb_max_x", "typ": "float", "desc": ""}, {"field": "floor_detection_aabb_min_y", "typ": "float", "desc": ""}, {"field": "floor_detection_aabb_max_y", "typ": "float", "desc": ""}, {"field": "mChangingDirectionCounter", "typ": "int", "desc": ""}]}, {"name": "ControlsComponent", "fields": [{"field": "polymorph_hax", "typ": "bool", "desc": ""}, {"field": "polymorph_next_attack_frame", "typ": "int", "desc": ""}, {"field": "enabled", "typ": "bool", "desc": ""}, {"field": "gamepad_indirect_aiming_enabled", "typ": "bool", "desc": ""}, {"field": "gamepad_fire_on_thumbstick_extend", "typ": "bool", "desc": ""}, {"field": "gamepad_fire_on_thumbstick_extend_threshold", "typ": "float", "desc": ""}, {"field": "mButtonDownFire", "typ": "bool", "desc": ""}, {"field": "mButtonFrameFire", "typ": "int", "desc": ""}, {"field": "mButtonLastFrameFire", "typ": "int", "desc": ""}, {"field": "mButtonDownFire2", "typ": "bool", "desc": ""}, {"field": "mButtonFrameFire2", "typ": "int", "desc": ""}, {"field": "mButtonDownAction", "typ": "bool", "desc": ""}, {"field": "mButtonFrameAction", "typ": "int", "desc": ""}, {"field": "mButtonDownThrow", "typ": "bool", "desc": ""}, {"field": "mButtonFrameThrow", "typ": "int", "desc": ""}, {"field": "mButtonDownInteract", "typ": "bool", "desc": ""}, {"field": "mButtonFrameInteract", "typ": "int", "desc": ""}, {"field": "mButtonDownLeft", "typ": "bool", "desc": ""}, {"field": "mButtonFrameLeft", "typ": "int", "desc": ""}, {"field": "mButtonDownRight", "typ": "bool", "desc": ""}, {"field": "mButtonFrameRight", "typ": "int", "desc": ""}, {"field": "mButtonDownUp", "typ": "bool", "desc": ""}, {"field": "mButtonFrameUp", "typ": "int", "desc": ""}, {"field": "mButtonDownDown", "typ": "bool", "desc": ""}, {"field": "mButtonFrameDown", "typ": "int", "desc": ""}, {"field": "mButtonDownJump", "typ": "bool", "desc": ""}, {"field": "mButtonFrameJump", "typ": "int", "desc": ""}, {"field": "mButtonDownRun", "typ": "bool", "desc": ""}, {"field": "mButtonFrameRun", "typ": "int", "desc": ""}, {"field": "mButtonDownFly", "typ": "bool", "desc": ""}, {"field": "mButtonFrameFly", "typ": "int", "desc": ""}, {"field": "mButtonDownDig", "typ": "bool", "desc": ""}, {"field": "mButtonFrameDig", "typ": "int", "desc": ""}, {"field": "mButtonDownChangeItemR", "typ": "bool", "desc": ""}, {"field": "mButtonFrameChangeItemR", "typ": "int", "desc": ""}, {"field": "mButtonCountChangeItemR", "typ": "int", "desc": "note these have special count property"}, {"field": "mButtonDownChangeItemL", "typ": "bool", "desc": ""}, {"field": "mButtonFrameChangeItemL", "typ": "int", "desc": ""}, {"field": "mButtonCountChangeItemL", "typ": "int", "desc": "note these have special count property"}, {"field": "mButtonDownInventory", "typ": "bool", "desc": ""}, {"field": "mButtonFrameInventory", "typ": "int", "desc": ""}, {"field": "mButtonDownHolsterItem", "typ": "bool", "desc": ""}, {"field": "mButtonFrameHolsterItem", "typ": "int", "desc": ""}, {"field": "mButtonDownDropItem", "typ": "bool", "desc": ""}, {"field": "mButtonFrameDropItem", "typ": "int", "desc": ""}, {"field": "mButtonDownKick", "typ": "bool", "desc": ""}, {"field": "mButtonFrameKick", "typ": "int", "desc": ""}, {"field": "mButtonDownEat", "typ": "bool", "desc": ""}, {"field": "mButtonFrameEat", "typ": "int", "desc": ""}, {"field": "mButtonDownLeftClick", "typ": "bool", "desc": "NOTE! Ignores gamepad, if mouse is pressed this will be true."}, {"field": "mButtonFrameLeftClick", "typ": "int", "desc": "NOTE! Ignores gamepad, if mouse is pressed this will be true."}, {"field": "mButtonDownRightClick", "typ": "bool", "desc": "NOTE! Ignores gamepad, if mouse is pressed this will be true."}, {"field": "mButtonFrameRightClick", "typ": "int", "desc": "NOTE! Ignores gamepad, if mouse is pressed this will be true."}, {"field": "mButtonDownTransformLeft", "typ": "bool", "desc": "NOT IN USE!"}, {"field": "mButtonFrameTransformLeft", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mButtonDownTransformRight", "typ": "bool", "desc": "NOT IN USE!"}, {"field": "mButtonFrameTransformRight", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mButtonDownTransformUp", "typ": "bool", "desc": "NOT IN USE!"}, {"field": "mButtonFrameTransformUp", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mButtonCountTransformUp", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mButtonDownTransformDown", "typ": "bool", "desc": "NOT IN USE!"}, {"field": "mButtonFrameTransformDown", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mButtonCountTransformDown", "typ": "int", "desc": "NOT IN USE!"}, {"field": "mFlyingTargetY", "typ": "float", "desc": ""}, {"field": "mAimingVector", "typ": "vec2", "desc": ""}, {"field": "mAimingVectorNormalized", "typ": "vec2", "desc": "Aiming vector normalized to unit sphere."}, {"field": "mAimingVectorNonZeroLatest", "typ": "vec2", "desc": ""}, {"field": "mGamepadAimingVectorRaw", "typ": "vec2", "desc": ""}, {"field": "mJumpVelocity", "typ": "vec2", "desc": "used mostly by AI only?"}, {"field": "mMousePosition", "typ": "vec2", "desc": ""}, {"field": "mMousePositionRaw", "typ": "vec2", "desc": ""}, {"field": "mMousePositionRawPrev", "typ": "vec2", "desc": ""}, {"field": "mMouseDelta", "typ": "vec2", "desc": ""}, {"field": "mGamepadIndirectAiming", "typ": "vec2", "desc": ""}, {"field": "mGamePadCursorInWorld", "typ": "vec2", "desc": "where the aiming cursor is in the world, updated by platformshooterplayer_system "}, {"field": "mButtonDownDelayLineFire", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineFire2", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineRight", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineLeft", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineUp", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineDown", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineKick", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineThrow", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineJump", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "mButtonDownDelayLineFly", "typ": "uint32_t", "desc": "Used to delay input for some game effects"}, {"field": "input_latency_frames", "typ": "LensValue", "desc": "Adds latency to some inputs. Used by some game effects. Max 31."}]}, {"name": "CrawlerAnimalComponent", "fields": [{"field": "ray_length", "typ": "float", "desc": ""}, {"field": "ray_count", "typ": "int", "desc": ""}, {"field": "gravity", "typ": "float", "desc": ""}, {"field": "terminal_velocity", "typ": "float", "desc": ""}, {"field": "speed", "typ": "float", "desc": ""}, {"field": "give_up_area_radius", "typ": "int", "desc": ""}, {"field": "give_up_time", "typ": "int", "desc": ""}, {"field": "attack_from_ceiling_check_ray_length", "typ": "float", "desc": ""}, {"field": "attack_from_ceiling_check_every_n_frames", "typ": "int", "desc": ""}, {"field": "collision_damage", "typ": "float", "desc": ""}, {"field": "collision_damage_radius", "typ": "float", "desc": ""}, {"field": "collision_damage_frames_between", "typ": "int", "desc": ""}, {"field": "animate", "typ": "bool", "desc": ""}, {"field": "mDir", "typ": "bool", "desc": ""}, {"field": "mFrameNextGiveUp", "typ": "int", "desc": ""}, {"field": "mFrameNextDamage", "typ": "int", "desc": ""}, {"field": "mFrameNextAttackFromCeilingCheck", "typ": "int", "desc": ""}, {"field": "mMin", "typ": "vec2", "desc": ""}, {"field": "mMax", "typ": "vec2", "desc": ""}, {"field": "mPrevNonSnappedPosition", "typ": "vec2", "desc": ""}, {"field": "mPrevCellPosition", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition2", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition3", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition4", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition5", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition6", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition7", "typ": "ivec2", "desc": ""}, {"field": "mPrevCellPosition8", "typ": "ivec2", "desc": ""}, {"field": "mLatestPosition", "typ": "ivec2", "desc": ""}, {"field": "mPrevFalling", "typ": "bool", "desc": ""}, {"field": "mIsInitialized", "typ": "bool", "desc": ""}, {"field": "mVelocityY", "typ": "float", "desc": ""}, {"field": "mAngle", "typ": "float", "desc": ""}, {"field": "mMovementStepAccumulator", "typ": "float", "desc": ""}]}, {"name": "CutThroughWorldDoneHereComponent", "fields": [{"field": "id_of_done_cut", "typ": "uint32", "desc": ""}]}, {"name": "DamageModelComponent", "fields": [{"field": "hp", "typ": "double", "desc": "hit points at the moment"}, {"field": "max_hp", "typ": "double", "desc": "the maximum hp that this can have, we'll set this when loading"}, {"field": "max_hp_cap", "typ": "double", "desc": "the maximum 'max_hp' that this can have, <= 0 means no limits. Used by perks such as GLASS_CANNON"}, {"field": "max_hp_old", "typ": "double", "desc": "used for UI rendering"}, {"field": "critical_damage_resistance", "typ": "float", "desc": "0.0 = all critical damage multiplier is applied. 1.0 = no critical damage multiplier is applied"}, {"field": "invincibility_frames", "typ": "int", "desc": "if positive, doesn't take damage"}, {"field": "falling_damages", "typ": "bool", "desc": "do we take fall damage"}, {"field": "falling_damage_height_min", "typ": "float", "desc": "how far do we need to fall to take damage, we start with this height, the peasant takes min damage from this"}, {"field": "falling_damage_height_max", "typ": "float", "desc": "after this the peasant always takes the maximum fall damage"}, {"field": "falling_damage_damage_min", "typ": "float", "desc": "when we fall over height_min we take this much, lineary ramping to damage_max"}, {"field": "falling_damage_damage_max", "typ": "float", "desc": "when we fall over height_min we take this much, lineary ramping to damage_max"}, {"field": "air_needed", "typ": "bool", "desc": "Do we breath, can we take damage from not breathing?"}, {"field": "air_in_lungs", "typ": "float", "desc": "How much air do we have in our lungs? - after the air runs out we take damage"}, {"field": "air_in_lungs_max", "typ": "float", "desc": "how much air can we have in our lungs, it's filled to this point if we're not in water"}, {"field": "air_lack_of_damage", "typ": "float", "desc": "(* dt)... damage in a second if we're in the water"}, {"field": "minimum_knockback_force", "typ": "float", "desc": "Minimum knockback force required to do the knockback"}, {"field": "materials_damage", "typ": "bool", "desc": "should materials do damage or not?"}, {"field": "material_damage_min_cell_count", "typ": "int", "desc": "if material damage is received from less than 'material_damage_min_cell_count' this frame, it is ignored"}, {"field": "materials_that_damage", "typ": "std::string", "desc": "list of materials that do damage, separated by ',' e.g. 'acid, fire, smoke'"}, {"field": "materials_how_much_damage", "typ": "std::string", "desc": "list of damage amount per material in materials_that_damage, separated by ','"}, {"field": "materials_damage_proportional_to_maxhp", "typ": "bool", "desc": "if damage from materials is proportional to max hp, instead of just damage"}, {"field": "physics_objects_damage", "typ": "bool", "desc": "if true, will take damage from physics objects that hit it"}, {"field": "materials_create_messages", "typ": "bool", "desc": "should collisions with certain materials create messages or not?"}, {"field": "materials_that_create_messages", "typ": "std::string", "desc": "list of materials that generate CollisionWithCell messages, separated by ',' e.g. 'acid, fire, smoke'"}, {"field": "ragdoll_filenames_file", "typ": "std::string", "desc": "the file from which to load a ragdoll on death'"}, {"field": "ragdoll_material", "typ": "std::string", "desc": "what material is the ragdoll made out of"}, {"field": "ragdoll_offset_x", "typ": "float", "desc": "where should the ragdoll be created relative to our entity position'"}, {"field": "ragdoll_offset_y", "typ": "float", "desc": "where should the ragdoll be created relative to our entity position'"}, {"field": "blood_material", "typ": "std::string", "desc": "this is the material that gets thrown as particles when this entity takes damage"}, {"field": "blood_spray_material", "typ": "std::string", "desc": "this is the material that gets thrown as particles when this entity sprays blood on death"}, {"field": "blood_spray_create_some_cosmetic", "typ": "bool", "desc": "if true, we force some blood spray particles to be cosmetic (can be enabled to avoid making a huge mess of blood spray)"}, {"field": "blood_multiplier", "typ": "float", "desc": "how much blood, this is the multiplier used for sprouting lots or little blood"}, {"field": "ragdoll_blood_amount_absolute", "typ": "int", "desc": "if > -1, this is the absolute amount of blood we share between particle emitters in the ragdoll"}, {"field": "blood_sprite_directional", "typ": "std::string", "desc": "this sprite is loaded at damage position if we take damage that creates a blood effect"}, {"field": "blood_sprite_large", "typ": "std::string", "desc": "this sprite is loaded at damage position if we take explosion/heavy damage"}, {"field": "healing_particle_effect_entity", "typ": "std::string", "desc": "if this is set, will load this entity as a child of this entity, when this entity is healed"}, {"field": "create_ragdoll", "typ": "bool", "desc": "if 0, we skip ragdoll creation on death"}, {"field": "ragdollify_child_entity_sprites", "typ": "bool", "desc": "if 1, we ragdollify child entity sprites"}, {"field": "ragdollify_root_angular_damping", "typ": "float", "desc": "If ragdoll_filenames_file= and > 0, the angular damping of the first ragdoll body is set to this value."}, {"field": "ragdollify_disintegrate_nonroot", "typ": "bool", "desc": "If ragdoll_filenames_file= and true, all but the first sprite on the root entity will be disintegrated instead of being turned into physics bodies."}, {"field": "wait_for_kill_flag_on_death", "typ": "bool", "desc": "if 1, we wont kill the entity along with kill fx and ragdoll until 'kill' is 1"}, {"field": "kill_now", "typ": "bool", "desc": "if 1, we wont kill the entity along with kill fx and ragdoll until 'kill_now' is 1"}, {"field": "drop_items_on_death", "typ": "bool", "desc": "drop the abilities as items on death?"}, {"field": "ui_report_damage", "typ": "bool", "desc": "If 1, damage numbers are displayed when this entity is damaged"}, {"field": "ui_force_report_damage", "typ": "bool", "desc": "If 1, damage numbers are displayed when this entity is damaged, even if the numbers are disabled in settings"}, {"field": "in_liquid_shooting_electrify_prob", "typ": "int", "desc": "when shooting underwater how likely are we to electrify the water"}, {"field": "wet_status_effect_damage", "typ": "float", "desc": "how much damage per 10 frames is done if entity has 'wet' status effect"}, {"field": "is_on_fire", "typ": "bool", "desc": "Tells us we're on fire or not"}, {"field": "fire_probability_of_ignition", "typ": "float", "desc": "what is the probability that we'll ignite, 0 means won't ever ignite"}, {"field": "fire_how_much_fire_generates", "typ": "int", "desc": "how many fire particles do we generate each frame"}, {"field": "fire_damage_ignited_amount", "typ": "float", "desc": "how much damage does being ignited do?"}, {"field": "fire_damage_amount", "typ": "float", "desc": "how much damage does fire do?, 0.2 is pretty good"}, {"field": "mLastElectricityResistanceFrame", "typ": "int", "desc": "Last frame electricity has no effect. Should not be private!"}, {"field": "mLastFrameReportedBlock", "typ": "int", "desc": "Last frame a damage block message was displayed for this entity"}, {"field": "mLastMaxHpChangeFrame", "typ": "int", "desc": "used for UI rendering"}, {"field": "damage_multipliers", "typ": "ConfigDamagesByType", "desc": "the multipliers applied to different types of damage"}, {"field": "ragdoll_fx_forced", "typ": "RAGDOLL_FX::Enum", "desc": "if set, will force this ragdoll fx to happen everytime"}, {"field": "mIsOnFire", "typ": "bool", "desc": "private variable to check when we're on fire and not"}, {"field": "mFireProbability", "typ": "int", "desc": "this gets decreased if we can't ignite anything else"}, {"field": "mFireFramesLeft", "typ": "int", "desc": "this is the remaining frames we're on fire"}, {"field": "mFireDurationFrames", "typ": "int", "desc": "this is the total duration in frames we're on fire"}, {"field": "mFireTriedIgniting", "typ": "bool", "desc": "private variable to check when we could have been ignited or not"}, {"field": "mLastCheckX", "typ": "int", "desc": "an optimization, so we don't have to check everything every frame"}, {"field": "mLastCheckY", "typ": "int", "desc": "an optimization, so we don't have to check everything every frame"}, {"field": "mLastCheckTime", "typ": "int", "desc": "an optimization, so we don't have to check everything every frame"}, {"field": "mLastMaterialDamageFrame", "typ": "int", "desc": "this is the last frame we took material damage"}, {"field": "mFallIsOnGround", "typ": "bool", "desc": "for fall damage, keeps a private variable about if we're on ground or not"}, {"field": "mFallHighestY", "typ": "float", "desc": "private var to keep track of how high have we flown to"}, {"field": "mFallCount", "typ": "int", "desc": "how many times have we fallen? This is used to make sure we don't take damage from the first fall"}, {"field": "mAirAreWeInWater", "typ": "bool", "desc": "a private variable to track our state in drowning"}, {"field": "mAirFramesNotInWater", "typ": "int", "desc": "how many frames have been with air to breathe"}, {"field": "mAirDoWeHave", "typ": "bool", "desc": "a private variable to track our state in drowning"}, {"field": "mTotalCells", "typ": "int", "desc": "how many cells are there total"}, {"field": "mLiquidCount", "typ": "int", "desc": "how many of the cells are liquid"}, {"field": "mLiquidMaterialWeAreIn", "typ": "int", "desc": "stores the liquid material we're in... may not be the most accurate"}, {"field": "int", "typ": "std::vector<", "desc": "NOTE! Sorted! a list of materials that do damage (materials_that_damage)"}, {"field": "float", "typ": "std::vector<", "desc": "NOTE! Sorted! a list of materials that do damage (materials_that_damage)"}, {"field": "int", "typ": "std::vector<", "desc": "NOTE! Sorted! a list of materials that create messages (materials_that_create_messages)"}, {"field": "int", "typ": "std::vector<", "desc": "Number of cells per collided with this frame. Order matches mCollisionMessageMaterials"}, {"field": "float", "typ": "std::vector<", "desc": "A list of damage per material that damages us. In same order as materials"}, {"field": "mFallDamageThisFrame", "typ": "float", "desc": "Amount of fall damage received this frame"}, {"field": "mElectricityDamageThisFrame", "typ": "float", "desc": "Amount of electricity damage received this frame"}, {"field": "mPhysicsDamageThisFrame", "typ": "float", "desc": "max physics damage we have taken this round"}, {"field": "mPhysicsDamageVecThisFrame", "typ": "vec2", "desc": "direction of physics damage"}, {"field": "mPhysicsDamageLastFrame", "typ": "int", "desc": "frame number when we took physics damage"}, {"field": "mPhysicsDamageEntity", "typ": "EntityTypeID", "desc": "the physics entity that hit us"}, {"field": "mPhysicsDamageTelekinesisCasterEntity", "typ": "EntityTypeID", "desc": "who moved an object that hit us via telekinesis"}, {"field": "mLastDamageFrame", "typ": "int", "desc": "frame number when we took any damage"}, {"field": "mHpBeforeLastDamage", "typ": "double", "desc": "how much hp did we have a while ago?"}, {"field": "mFireDamageBuffered", "typ": "float", "desc": "used to optimized cases where lots of entities are taking fire damage"}, {"field": "mFireDamageBufferedNextDeliveryFrame", "typ": "int32", "desc": ""}]}, {"name": "DamageNearbyEntitiesComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}, {"field": "damage_min", "typ": "float", "desc": ""}, {"field": "damage_max", "typ": "float", "desc": ""}, {"field": "target_vec_max_len", "typ": "float", "desc": ""}, {"field": "knockback_multiplier", "typ": "float", "desc": ""}, {"field": "time_between_damaging", "typ": "int", "desc": ""}, {"field": "damage_description", "typ": "std::string", "desc": ""}, {"field": "target_tag", "typ": "std::string", "desc": ""}, {"field": "damage_type", "typ": "DAMAGE_TYPES::Enum", "desc": "the damage type"}, {"field": "ragdoll_fx", "typ": "RAGDOLL_FX::Enum", "desc": ""}, {"field": "mVelocity", "typ": "vec2", "desc": ""}, {"field": "mNextDamageFrame", "typ": "int", "desc": ""}]}, {"name": "DebugFollowMouseComponent", "fields": []}, {"name": "DebugLogMessagesComponent", "fields": [{"field": "TEMP_TEMPY", "typ": "float", "desc": ""}, {"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}]}, {"name": "DebugSpatialVisualizerComponent", "fields": [{"field": "min_x", "typ": "float", "desc": ""}, {"field": "min_y", "typ": "float", "desc": ""}, {"field": "max_x", "typ": "float", "desc": ""}, {"field": "max_y", "typ": "float", "desc": ""}, {"field": "int", "typ": "unsigned", "desc": ""}]}, {"name": "DieIfSpeedBelowComponent", "fields": [{"field": "min_speed", "typ": "float", "desc": "The entity that owns this component is killed if its speed (via VelocityComponent) falls below this value."}, {"field": "mMinSpeedSquared", "typ": "float", "desc": ""}]}, {"name": "DroneLauncherComponent", "fields": [{"field": "drone_entity_file", "typ": "std::string", "desc": ""}]}, {"name": "DrugEffectComponent", "fields": [{"field": "drug_fx_target", "typ": "ConfigDrugFx", "desc": ""}, {"field": "m_drug_fx_current", "typ": "ConfigDrugFx", "desc": ""}]}, {"name": "DrugEffectModifierComponent", "fields": [{"field": "fx_add", "typ": "ConfigDrugFx", "desc": ""}, {"field": "fx_multiply", "typ": "ConfigDrugFx", "desc": ""}]}, {"name": "ElectricChargeComponent", "fields": [{"field": "charge_time_frames", "typ": "int", "desc": ""}, {"field": "fx_velocity_max", "typ": "float", "desc": ""}, {"field": "electricity_emission_interval_frames", "typ": "int", "desc": ""}, {"field": "fx_emission_interval_min", "typ": "int", "desc": ""}, {"field": "fx_emission_interval_max", "typ": "int", "desc": ""}, {"field": "charge", "typ": "int", "desc": ""}]}, {"name": "ElectricityComponent", "fields": [{"field": "energy", "typ": "int", "desc": ""}, {"field": "probability_to_heat", "typ": "float", "desc": ""}, {"field": "speed", "typ": "int", "desc": ""}, {"field": "splittings_min", "typ": "int", "desc": ""}, {"field": "splittings_max", "typ": "int", "desc": ""}, {"field": "splitting_energy_min", "typ": "int", "desc": ""}, {"field": "splitting_energy_max", "typ": "int", "desc": ""}, {"field": "hack_is_material_crack", "typ": "bool", "desc": ""}, {"field": "hack_crack_ice", "typ": "bool", "desc": ""}, {"field": "hack_is_set_fire", "typ": "bool", "desc": "if set will set the thing on fire where this is located at"}, {"field": "mSplittingsLeft", "typ": "int", "desc": ""}, {"field": "mSplittingEnergy", "typ": "int", "desc": ""}, {"field": "mAvgDir", "typ": "vec2", "desc": ""}, {"field": "mPrevPos", "typ": "ivec2", "desc": ""}, {"field": "mPrevMaterial", "typ": "int", "desc": ""}, {"field": "mShouldPlaySound", "typ": "bool", "desc": ""}]}, {"name": "ElectricityReceiverComponent", "fields": [{"field": "offset_x", "typ": "int", "desc": ""}, {"field": "offset_y", "typ": "int", "desc": ""}, {"field": "radius", "typ": "int", "desc": ""}, {"field": "active_time_frames", "typ": "int", "desc": ""}, {"field": "switch_on_msg_interval_frames", "typ": "int", "desc": ""}, {"field": "electrified_msg_interval_frames", "typ": "int", "desc": ""}, {"field": "mLastFrameElectrified", "typ": "int", "desc": ""}, {"field": "mNextElectrifiedMsgFrame", "typ": "int", "desc": ""}, {"field": "mNextSwitchOnMsgFrame", "typ": "int", "desc": ""}]}, {"name": "ElectricitySourceComponent", "fields": [{"field": "radius", "typ": "int", "desc": ""}, {"field": "emission_interval_frames", "typ": "int", "desc": ""}, {"field": "mNextFrameEmitElectricity", "typ": "int", "desc": ""}]}, {"name": "EndingMcGuffinComponent", "fields": [{"field": "TEMP_TEMPY", "typ": "float", "desc": ""}, {"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}]}, {"name": "EnergyShieldComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}, {"field": "damage_multiplier", "typ": "float", "desc": ""}, {"field": "max_energy", "typ": "float", "desc": ""}, {"field": "energy_required_to_shield", "typ": "float", "desc": ""}, {"field": "recharge_speed", "typ": "float", "desc": ""}, {"field": "sector_degrees", "typ": "float", "desc": "if less than 180 we only provide partial cover to the current direction of the entity"}, {"field": "energy", "typ": "float", "desc": ""}, {"field": "mPrevPosition", "typ": "vec2", "desc": ""}]}, {"name": "ExplodeOnDamageComponent", "fields": [{"field": "explode_on_death_percent", "typ": "float", "desc": "rolls a dice (0 - 1) if we explode on death"}, {"field": "explode_on_damage_percent", "typ": "float", "desc": "rolls a dice (0 - 1) if we explode on damage"}, {"field": "physics_body_modified_death_probability", "typ": "float", "desc": "if we get message about the physics body being modified, do we explode on what percent"}, {"field": "physics_body_destruction_required", "typ": "float", "desc": "how big of percent of our body, do we need to lose before we explode"}, {"field": "config_explosion", "typ": "ConfigExplosion", "desc": "if we have explosion, it's the setup for it"}, {"field": "mDone", "typ": "bool", "desc": ""}]}, {"name": "ExplosionComponent", "fields": [{"field": "timeout_frames", "typ": "int", "desc": "for timer"}, {"field": "timeout_frames_random", "typ": "int", "desc": "a random value between 0 and 'timout_frames_random' is added to timer"}, {"field": "kill_entity", "typ": "bool", "desc": "if 1, we kill the entity when exploding"}, {"field": "mTimerTriggerFrame", "typ": "int", "desc": ""}, {"field": "config_explosion", "typ": "ConfigExplosion", "desc": "setup for out explosion"}]}, {"name": "FishAIComponent", "fields": [{"field": "direction", "typ": "int", "desc": ""}, {"field": "speed", "typ": "float", "desc": ""}, {"field": "aabb_min", "typ": "vec2", "desc": ""}, {"field": "aabb_max", "typ": "vec2", "desc": ""}, {"field": "velocity", "typ": "vec2", "desc": ""}, {"field": "stuck_counter", "typ": "int", "desc": ""}, {"field": "mLastCheckPos", "typ": "vec2", "desc": ""}]}, {"name": "FlyingComponent", "fields": [{"field": "type", "typ": "int", "desc": "type of flight, 1 = perlin noise"}, {"field": "perlin_freq", "typ": "float", "desc": "frequency of the perlin noise sampling"}, {"field": "perlin_time_freq", "typ": "float", "desc": "t *= perlin_time_freq"}, {"field": "perlin_wind_x", "typ": "float", "desc": "wind velocity that gets added to the samples"}, {"field": "perlin_wind_y", "typ": "float", "desc": "wind velocity that gets added to the samples"}]}, {"name": "FogOfWarRadiusComponent", "fields": [{"field": "radius", "typ": "float", "desc": "256 is the default player has"}]}, {"name": "FogOfWarRemoverComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}]}, {"name": "GameAreaEffectComponent", "fields": [{"field": "radius", "typ": "float", "desc": "what's the radius (in pixels) of the area effect"}, {"field": "collide_with_tag", "typ": "std::string", "desc": "the tags we're looking for"}, {"field": "frame_length", "typ": "int", "desc": "if not 0 will reapply this effect after this many frames have gone by"}, {"field": "game_effect_entitities", "typ": "VECTOR_STR", "desc": "just a vector of the game_effect entities"}, {"field": "mEntitiesAppliedOutTo", "typ": "VECTOR_ENTITYID", "desc": ""}, {"field": "mEntitiesAppliedFrame", "typ": "VECTOR_INT", "desc": ""}]}, {"name": "GameEffectComponent", "fields": [{"field": "custom_effect_id", "typ": "std::string", "desc": "if 'effect' is set to 'CUSTOM', this will define effect uniqueness."}, {"field": "frames", "typ": "int", "desc": "how many frames does it affect -1 = forever"}, {"field": "exclusivity_group", "typ": "int", "desc": "if > 0, previous game effects with the same exclusivity group as new one will be removed when calling LoadGameEffectEntityTo"}, {"field": "report_block_msg", "typ": "bool", "desc": "to disable the block message that rises"}, {"field": "disable_movement", "typ": "bool", "desc": "if set, will disable movement"}, {"field": "ragdoll_effect_custom_entity_file", "typ": "std::string", "desc": "an entity that is loaded to each ragdoll part if 'ragdoll_effect' is set to 'CUSTOM_RAGDOLL_ENTITY'"}, {"field": "ragdoll_fx_custom_entity_apply_only_to_largest_body", "typ": "bool", "desc": "if 1, 'ragdoll_effect_custom_entity_file' is loaded only to the largest piece in the ragdoll "}, {"field": "polymorph_target", "typ": "std::string", "desc": "when doing a polymorph, this is what we convert it to"}, {"field": "mSerializedData", "typ": "USTRING", "desc": "polymorph stores the serialized entity here..."}, {"field": "mCaster", "typ": "EntityID", "desc": "Contains a handle to the caster of this GameEffect"}, {"field": "mCasterHerdId", "typ": "int", "desc": "Contains the herd if of the caster of this GameEffect"}, {"field": "teleportation_probability", "typ": "int", "desc": "How likely is it that we teleport, larger = less often"}, {"field": "teleportation_delay_min_frames", "typ": "int", "desc": "Never teleports more often that this"}, {"field": "teleportation_radius_min", "typ": "float", "desc": ""}, {"field": "teleportation_radius_max", "typ": "float", "desc": ""}, {"field": "teleportations_num", "typ": "int", "desc": "How many times has this GameEffectComponent teleported the owner?"}, {"field": "no_heal_max_hp_cap", "typ": "double", "desc": "If current hp is less than this, we store it here. Then we make sure the hp never exceeds this."}, {"field": "caused_by_ingestion_status_effect", "typ": "bool", "desc": "Did this effect occur because someone ate something?"}, {"field": "caused_by_stains", "typ": "bool", "desc": "was this caused by stains"}, {"field": "mCharmDisabledCameraBound", "typ": "bool", "desc": "When charmed, will try to disable CameraBound. This keeps track if we've done it, so we can enable it back"}, {"field": "mCharmEnabledTeleporting", "typ": "bool", "desc": "When charmed, will try to enable teleporting (tag:teleportable_NOT). This keeps track if we've done it, so we can disable it again"}, {"field": "mInvisible", "typ": "bool", "desc": "Are we invisible?"}, {"field": "mCounter", "typ": "int", "desc": "Counts stuff"}, {"field": "mCooldown", "typ": "int", "desc": "Counts cooldown"}, {"field": "mIsExtension", "typ": "bool", "desc": "If 1, this is an effect extension and shouldn't create an extension when removed"}, {"field": "mIsSpent", "typ": "bool", "desc": "NOTE( Petri ): 29.4.2024 - this is used internally to make RESPAWN perk disabled in the UI"}, {"field": "effect", "typ": "GAME_EFFECT::Enum", "desc": "GAME_EFFECT"}, {"field": "ragdoll_effect", "typ": "RAGDOLL_FX::Enum", "desc": "if set, will use this for ragdoll effect"}, {"field": "ragdoll_material", "typ": "int", "desc": "converts to string name of the material that ragdoll is made out of"}, {"field": "causing_status_effect", "typ": "StatusEffectType", "desc": "Status effect that caused this game effect, if any"}]}, {"name": "GameLogComponent", "fields": [{"field": "report_death", "typ": "bool", "desc": "switches on reporting things"}, {"field": "report_damage", "typ": "bool", "desc": "if set, will report when receiving damage"}, {"field": "report_new_biomes", "typ": "bool", "desc": "if false, won't report when player enters new biomes"}, {"field": "mVisitiedBiomes", "typ": "VISITED_VEC", "desc": "list of visited biomes"}, {"field": "mNewBiomeCheckFrame", "typ": "int", "desc": ""}]}, {"name": "GameStatsComponent", "fields": [{"field": "name", "typ": "std::string", "desc": "no one uses the name variable on entity, so we have to do this to make it happen"}, {"field": "stats_filename", "typ": "std::string", "desc": "also generated from the gunk"}, {"field": "is_player", "typ": "bool", "desc": "if true, will use the session file for loading stats"}, {"field": "extra_death_msg", "typ": "std::string", "desc": "set when e.g. polymorphed"}, {"field": "dont_do_logplayerkill", "typ": "bool", "desc": "if 1, StatsLogPlayerKill must be manually called from lua"}, {"field": "player_polymorph_count", "typ": "int", "desc": "skip loading of stats if this higher than 0 and decrament this by one"}]}, {"name": "GasBubbleComponent", "fields": [{"field": "acceleration", "typ": "float", "desc": ""}, {"field": "max_speed", "typ": "float", "desc": ""}, {"field": "mVelocity", "typ": "float", "desc": ""}]}, {"name": "GenomeDataComponent", "fields": [{"field": "is_predator", "typ": "bool", "desc": "Predators are considered threats by other species and hunt for food."}, {"field": "food_chain_rank", "typ": "float", "desc": "0 means king of the hill. Greater number = more likely to get eaten by other species."}, {"field": "berserk_dont_attack_friends", "typ": "bool", "desc": "if 1, this animal will not try to attack player who would normally be its friend"}, {"field": "herd_id", "typ": "LensValue", "desc": "This is used for example to separate people in different tribes."}, {"field": "friend_thundermage", "typ": "LensValue", "desc": "if 1, thunder mage doesn't attack this"}, {"field": "friend_firemage", "typ": "LensValue", "desc": "if 1, fire mage doesn't attack this"}]}, {"name": "GhostComponent", "fields": [{"field": "speed", "typ": "float", "desc": "pixels per second"}, {"field": "new_hunt_target_check_every", "typ": "int", "desc": "how often do we look for targets"}, {"field": "hunt_box_radius", "typ": "float", "desc": ""}, {"field": "aggressiveness", "typ": "float", "desc": "if higher than relations then will attack"}, {"field": "max_distance_from_home", "typ": "float", "desc": "how far from home can we go?"}, {"field": "die_if_no_home", "typ": "bool", "desc": "if set to false will die, if it can't find home"}, {"field": "target_tag", "typ": "std::string", "desc": "if something else (like mortal), will attack the home"}, {"field": "velocity", "typ": "vec2", "desc": ""}, {"field": "mEntityHome", "typ": "EntityID", "desc": "where is our home?"}, {"field": "mFramesWithoutHome", "typ": "int", "desc": ""}, {"field": "mTargetPosition", "typ": "vec2", "desc": ""}, {"field": "mTargetEntityId", "typ": "int", "desc": ""}, {"field": "mRandomTarget", "typ": "vec2", "desc": ""}, {"field": "mNextTargetCheckFrame", "typ": "int", "desc": ""}]}, {"name": "GodInfoComponent", "fields": [{"field": "mana_current", "typ": "float", "desc": "How much mana the player now has to use"}, {"field": "mana_max", "typ": "float", "desc": "Max size of the mana pool"}, {"field": "gold", "typ": "float", "desc": "How much gold the player has"}, {"field": "god_entity", "typ": "Entity*", "desc": ""}]}, {"name": "GunComponent", "fields": [{"field": "mLuaManager", "typ": "LuaManager*", "desc": ""}]}, {"name": "HealthBarComponent", "fields": []}, {"name": "HitEffectComponent", "fields": [{"field": "value", "typ": "int", "desc": "Usage depends on selected 'effect_hit'"}, {"field": "value_string", "typ": "std::string", "desc": "Usage depends on selected 'effect_hit'"}, {"field": "condition_effect", "typ": "GAME_EFFECT::Enum", "desc": "Hit entity needs to have this 'GAME_EFFECT' for effects to apply. If both 'condition_effect' and 'condition_status' are set, they are combined with AND logic"}, {"field": "condition_status", "typ": "StatusEffectType", "desc": "Hit entity needs to have this 'STATUS_EFFECT' for effects to apply"}, {"field": "effect_hit", "typ": "HIT_EFFECT::Enum", "desc": "What kind of 'HIT_EFFECT' is applied to hit entity if condition is true"}]}, {"name": "HitboxComponent", "fields": [{"field": "is_player", "typ": "bool", "desc": ""}, {"field": "is_enemy", "typ": "bool", "desc": ""}, {"field": "is_item", "typ": "bool", "desc": ""}, {"field": "aabb_min_x", "typ": "float", "desc": ""}, {"field": "aabb_max_x", "typ": "float", "desc": ""}, {"field": "aabb_min_y", "typ": "float", "desc": ""}, {"field": "aabb_max_y", "typ": "float", "desc": ""}, {"field": "damage_multiplier", "typ": "float", "desc": "All damage from hits to this hitbox is multiplied with this value before applying it."}, {"field": "offset", "typ": "vec2", "desc": ""}, {"field": "dead", "typ": "bool", "desc": ""}]}, {"name": "HomingComponent", "fields": [{"field": "target_tag", "typ": "std::string", "desc": ""}, {"field": "target_who_shot", "typ": "bool", "desc": "If 1, targets who shot the projectile, ignores 'target_tag'."}, {"field": "detect_distance", "typ": "float", "desc": ""}, {"field": "homing_velocity_multiplier", "typ": "float", "desc": ""}, {"field": "homing_targeting_coeff", "typ": "float", "desc": ""}, {"field": "just_rotate_towards_target", "typ": "bool", "desc": "the default accelerates towards a target. If true will only rotate towards the target."}, {"field": "max_turn_rate", "typ": "float", "desc": "radians. If just_rotate_towards_target then this is the maximum radians it can turn per frame"}, {"field": "predefined_target", "typ": "EntityID", "desc": "If set, we track this entity"}, {"field": "look_for_root_entities_only", "typ": "bool", "desc": "if set, will only look for entities that are _not_ child entities."}]}, {"name": "HotspotComponent", "fields": [{"field": "transform_with_scale", "typ": "bool", "desc": ""}, {"field": "sprite_hotspot_name", "typ": "std::string", "desc": ""}, {"field": "offset", "typ": "vec2", "desc": ""}]}, {"name": "IKLimbAttackerComponent", "fields": [{"field": "radius", "typ": "float", "desc": ""}, {"field": "leg_velocity_coeff", "typ": "float", "desc": ""}, {"field": "targeting_radius", "typ": "float", "desc": ""}, {"field": "targeting_raytrace", "typ": "bool", "desc": ""}, {"field": "target_entities_with_tag", "typ": "std::string", "desc": ""}, {"field": "mTarget", "typ": "vec2", "desc": ""}, {"field": "mTargetEntity", "typ": "EntityID", "desc": ""}, {"field": "mState", "typ": "IKLimbAttackerState", "desc": ""}, {"field": "mStateTimer", "typ": "float", "desc": ""}]}, {"name": "IKLimbComponent", "fields": [{"field": "length", "typ": "float", "desc": ""}, {"field": "thigh_extra_lenght", "typ": "float", "desc": ""}, {"field": "mJointSideInterpolation", "typ": "float", "desc": ""}, {"field": "end_position", "typ": "vec2", "desc": ""}, {"field": "mJointWorldPos", "typ": "vec2", "desc": ""}, {"field": "mEndPrevPos", "typ": "vec2", "desc": ""}, {"field": "mPart0PrevPos", "typ": "vec2", "desc": ""}, {"field": "mPart0PrevRotation", "typ": "float", "desc": ""}, {"field": "mPart1PrevPos", "typ": "vec2", "desc": ""}, {"field": "mPart1PrevRotation", "typ": "float", "desc": ""}]}, {"name": "IKLimbWalkerComponent", "fields": [{"field": "ground_attachment_min_spread", "typ": "float", "desc": ""}, {"field": "ground_attachment_max_tries", "typ": "int", "desc": ""}, {"field": "ground_attachment_max_angle", "typ": "float", "desc": ""}, {"field": "ground_attachment_ray_length_coeff", "typ": "float", "desc": ""}, {"field": "leg_velocity_coeff", "typ": "float", "desc": ""}, {"field": "affect_flying", "typ": "bool", "desc": "if set, will cause the mFlyingTime (in CharacterDataComponent) of the parent to be 0 or 1 depending on if we're touching anything"}, {"field": "mState", "typ": "int", "desc": "0 = detached, 1 = attached"}, {"field": "ray_skip_material", "typ": "int", "desc": "String name of material to not cast rays against. Defaults to 'aluminium'"}, {"field": "mTarget", "typ": "vec2", "desc": ""}, {"field": "mPrevTarget", "typ": "vec2", "desc": ""}, {"field": "mPrevCenterPosition", "typ": "vec2", "desc": ""}]}, {"name": "IKLimbsAnimatorComponent", "fields": [{"field": "future_state_samples", "typ": "int", "desc": "The number of future animation states evaluated to find the next state"}, {"field": "ground_attachment_ray_length_coeff", "typ": "float", "desc": "Limb raycast length is (ground_attachment_ray_length_coeff * limb length)"}, {"field": "leg_velocity_coeff", "typ": "float", "desc": "Limbs are moved towards target position at a pace affected by this value."}, {"field": "affect_flying", "typ": "bool", "desc": "If set, will cause the mFlyingTime (in CharacterDataComponent) of the entity to be 0 or 1 depending on if the limbs are touching ground"}, {"field": "large_movement_penalty_coeff", "typ": "float", "desc": "The movement score is multiplied by this value if a large move would occur"}, {"field": "no_ground_attachment_penalty_coeff", "typ": "float", "desc": "If a limb movement would make it not collide with ground, the movement score is multiplied with this value. Use lower values to make the limbs prioritize attaching to walls."}, {"field": "is_limp", "typ": "bool", "desc": "If 1, will apply verlet animation to simulate ragdoll-like limbs"}, {"field": "ray_skip_material", "typ": "int", "desc": "String name of material to not cast rays against. Defaults to 'aluminium'"}, {"field": "mPrevBodyPosition", "typ": "vec2", "desc": ""}, {"field": "mLimbStates", "typ": "IKLimbStateVec", "desc": ""}, {"field": "mHasGroundAttachmentOnAnyLeg", "typ": "bool", "desc": "Will be set to true if at least one leg is attached to ground."}]}, {"name": "IngestionComponent", "fields": [{"field": "ingestion_size", "typ": "int64", "desc": "How many units of material we currently store"}, {"field": "ingestion_capacity", "typ": "int64", "desc": "How many units of material we can store"}, {"field": "ingestion_cooldown_delay_frames", "typ": "uint32", "desc": "How many frames is ingestion_size retained after last time eating?"}, {"field": "ingestion_reduce_every_n_frame", "typ": "uint32", "desc": "One unit of ingestion_size is removed every N frame"}, {"field": "overingestion_damage", "typ": "float", "desc": "How much damage per overingested cell is applied"}, {"field": "blood_healing_speed", "typ": "float", "desc": "affects healing speed if entity has HEALING_BLOOD game effect. The amount of hp restored per one blood cell."}, {"field": "ingestion_satiation_material_tag", "typ": "std::string", "desc": "If set, only materials with this tag will increase satiation level"}, {"field": "m_ingestion_cooldown_frames", "typ": "int32", "desc": "Next frame ingestion_size cooldown can occur"}, {"field": "m_next_overeating_msg_frame", "typ": "int32", "desc": ""}, {"field": "m_ingestion_satiation_material_tag_cached", "typ": "std::string", "desc": ""}, {"field": "m_ingestion_satiation_material_cache", "typ": "std::set", "desc": ""}, {"field": "m_damage_effect_lifetime", "typ": "int32", "desc": ""}]}, {"name": "InheritTransformComponent", "fields": [{"field": "use_root_parent", "typ": "bool", "desc": "if 1, we use the root of our entity hierarchy instead of the immediate parent"}, {"field": "only_position", "typ": "bool", "desc": "if 1, we only inherit position. it is calculated as follows: parent_position + parent_offset * parent_scale"}, {"field": "parent_hotspot_tag", "typ": "std::string", "desc": "if set, we apply the offset of parent HotSpot with this tag"}, {"field": "parent_sprite_id", "typ": "int", "desc": "if >= 0, the Nth sprite transform in parent entity is inherited"}, {"field": "always_use_immediate_parent_rotation", "typ": "bool", "desc": "if 1, we use the immediate parent for rotation, no matter what other properties say"}, {"field": "rotate_based_on_x_scale", "typ": "bool", "desc": "if 1, the rotation is set to 0 deg if scale >= 0 else to 180 deg"}, {"field": "Transform", "typ": "types::xform", "desc": ""}, {"field": "mUpdateFrame", "typ": "int", "desc": ""}]}, {"name": "InteractableComponent", "fields": [{"field": "radius", "typ": "float", "desc": "Distance from entity position where interaction is possible"}, {"field": "ui_text", "typ": "std::string", "desc": "key or string for the text to display"}, {"field": "name", "typ": "std::string", "desc": "this name is called to the on_interacted function on LuaComponents"}, {"field": "exclusivity_group", "typ": "int", "desc": "If > 0, only 1 instance of this interaction can be display at the same time"}]}, {"name": "Inventory2Component", "fields": [{"field": "quick_inventory_slots", "typ": "int", "desc": ""}, {"field": "full_inventory_slots_x", "typ": "int", "desc": ""}, {"field": "full_inventory_slots_y", "typ": "int", "desc": ""}, {"field": "mSavedActiveItemIndex", "typ": "uint32", "desc": "Used to retain active item across save/load. Don't touch this unless you know what you're doing!"}, {"field": "mActiveItem", "typ": "EntityID", "desc": "NOTE: Don't attempt to directly change the value of this field via lua code. It will probably break the game logic in obvious or subtle ways."}, {"field": "mActualActiveItem", "typ": "EntityID", "desc": "NOTE: Don't attempt to directly change the value of this field via lua code. It will probably break the game logic in obvious or subtle ways."}, {"field": "mActiveStash", "typ": "EntityID", "desc": ""}, {"field": "mThrowItem", "typ": "EntityID", "desc": "Is used to store the item that is being thrown, instead of mActiveItem, since the player can switch items (mActiveItem) during the throwing animation"}, {"field": "mItemHolstered", "typ": "bool", "desc": ""}, {"field": "mInitialized", "typ": "bool", "desc": ""}, {"field": "mForceRefresh", "typ": "bool", "desc": ""}, {"field": "mDontLogNextItemEquip", "typ": "bool", "desc": ""}, {"field": "mSmoothedItemXOffset", "typ": "float", "desc": ""}, {"field": "mLastItemSwitchFrame", "typ": "int", "desc": ""}, {"field": "mIntroEquipItemLerp", "typ": "float", "desc": ""}, {"field": "mSmoothedItemAngleVec", "typ": "vec2", "desc": ""}]}, {"name": "InventoryComponent", "fields": [{"field": "ui_container_type", "typ": "int", "desc": "UI_CONTAINER_TYPES enum"}, {"field": "ui_element_sprite", "typ": "std::string", "desc": "ui back sprite"}, {"field": "actions", "typ": "std::string", "desc": "list of actions, used for serialization"}, {"field": "ui_container_size", "typ": "ivec2", "desc": "ui size, how many items x*y we can fit in"}, {"field": "ui_element_size", "typ": "ivec2", "desc": "ui size"}, {"field": "ui_position_on_screen", "typ": "ivec2", "desc": "where do we load this on screen"}, {"field": "items", "typ": "INVENTORYITEM_VECTOR", "desc": ""}]}, {"name": "InventoryGuiComponent", "fields": [{"field": "has_opened_inventory_edit", "typ": "bool", "desc": ""}, {"field": "wallet_money_target", "typ": "int", "desc": ""}, {"field": "mDisplayFireRateWaitBar", "typ": "bool", "desc": "hax, don't touch!"}, {"field": "imgui", "typ": "ImGuiContext*", "desc": ""}, {"field": "mLastFrameInteracted", "typ": "int", "desc": ""}, {"field": "mLastFrameActionsVisible", "typ": "int", "desc": ""}, {"field": "mLastPurchasedAction", "typ": "Entity*", "desc": ""}, {"field": "mActive", "typ": "bool", "desc": ""}, {"field": "mAlpha", "typ": "float", "desc": ""}, {"field": "mBackgroundOverlayAlpha", "typ": "float", "desc": ""}, {"field": "mFrameShake_ReloadBar", "typ": "int", "desc": "for animations of shaking them bars"}, {"field": "mFrameShake_ManaBar", "typ": "int", "desc": "for animations of shaking them bars"}, {"field": "mFrameShake_FlyBar", "typ": "int", "desc": "for animations of shaking them bars"}, {"field": "mFrameShake_FireRateWaitBar", "typ": "int", "desc": "for animations of shaking them bars"}]}, {"name": "ItemAIKnowledgeComponent", "fields": [{"field": "is_ranged_weapon", "typ": "bool", "desc": ""}, {"field": "is_throwable_weapon", "typ": "bool", "desc": ""}, {"field": "is_melee_weapon", "typ": "bool", "desc": ""}, {"field": "is_self_healing", "typ": "bool", "desc": ""}, {"field": "is_other_healing", "typ": "bool", "desc": ""}, {"field": "is_self_buffing", "typ": "bool", "desc": ""}, {"field": "is_other_buffing", "typ": "bool", "desc": ""}, {"field": "is_weapon", "typ": "bool", "desc": ""}, {"field": "is_known", "typ": "bool", "desc": ""}, {"field": "is_safe", "typ": "bool", "desc": ""}, {"field": "is_consumed", "typ": "bool", "desc": ""}, {"field": "never_use", "typ": "bool", "desc": ""}, {"field": "ranged_min_distance", "typ": "float", "desc": ""}]}, {"name": "ItemActionComponent", "fields": [{"field": "action_id", "typ": "std::string", "desc": "the name ID of the action"}]}, {"name": "ItemAlchemyComponent", "fields": [{"field": "material_make_always_cast", "typ": "int", "desc": ""}, {"field": "material_remove_shuffle", "typ": "int", "desc": ""}, {"field": "material_animate_wand", "typ": "int", "desc": ""}, {"field": "material_animate_wand_alt", "typ": "int", "desc": ""}, {"field": "material_increase_uses_remaining", "typ": "int", "desc": ""}, {"field": "material_sacrifice", "typ": "int", "desc": ""}]}, {"name": "ItemChestComponent", "fields": [{"field": "item_count_min", "typ": "int", "desc": ""}, {"field": "item_count_max", "typ": "int", "desc": ""}, {"field": "level", "typ": "int", "desc": ""}, {"field": "enemy_drop", "typ": "bool", "desc": "enemy_drop, if set will modify the item_count_min, item_count_max..."}, {"field": "actions", "typ": "std::string", "desc": "e.g. 'bullet,bullet,damage' ... actions are parsed into a string"}, {"field": "action_uses_remaining", "typ": "std::string", "desc": "e.g. '10,10,-1' ... action uses remaining counts are parsed into a string"}, {"field": "other_entities_to_spawn", "typ": "std::string", "desc": "file names of other entities we should spawn from this chest, comma separated"}, {"field": "int", "typ": "unsigned", "desc": "this is used to figure out what we spawn from this chest"}]}, {"name": "ItemComponent", "fields": [{"field": "item_name", "typ": "std::string", "desc": "the name of the item"}, {"field": "is_stackable", "typ": "bool", "desc": "does this item stack on other items the same 'item_name' in the inventory?"}, {"field": "is_consumable", "typ": "bool", "desc": "if 1, using this item will reduce 'uses_remaining'. When it reaches zero the item is destroyed"}, {"field": "stats_count_as_item_pick_up", "typ": "bool", "desc": "does this count as an item that was picked up in the stats"}, {"field": "auto_pickup", "typ": "bool", "desc": "if 1, item will be automatically picked up, no pickup hint is shown"}, {"field": "permanently_attached", "typ": "bool", "desc": "if 1, this item can't be removed from a container once it is put inside one"}, {"field": "uses_remaining", "typ": "int", "desc": "how many times can this item be used? -1 = unlimited, will be reset to gun_actions.lua max_uses by inventorygui_system, -2 = unlimited unlimited"}, {"field": "is_identified", "typ": "bool", "desc": "is it known what this item does?"}, {"field": "is_frozen", "typ": "bool", "desc": "if 1, this item can't be modified or moved from a wand"}, {"field": "collect_nondefault_actions", "typ": "bool", "desc": "does player keep this item when respawning?"}, {"field": "remove_on_death", "typ": "bool", "desc": "is this entity destroyed when it's in an inventory and the inventory owner dies?"}, {"field": "remove_on_death_if_empty", "typ": "bool", "desc": "is this entity destroyed when it's in an inventory, empty and the inventory owner dies?"}, {"field": "remove_default_child_actions_on_death", "typ": "bool", "desc": "if true, the default AbilityComponent.child_actions in this items will be removed when it dies"}, {"field": "play_hover_animation", "typ": "bool", "desc": "if 1, the item will play a hovering animation"}, {"field": "play_spinning_animation", "typ": "bool", "desc": "if 1, the item will play a spinning animation, if player_hover_animation is 0"}, {"field": "is_equipable_forced", "typ": "bool", "desc": "if 1, the default logic for determining if an item can be equiped in inventory is overridden and this can be always equipped"}, {"field": "play_pick_sound", "typ": "bool", "desc": "if 1, plays a default sound when picked"}, {"field": "drinkable", "typ": "bool", "desc": "if 0 you cannot drink this, default is 1, because that's how it was implemented and backwards compatibility"}, {"field": "max_child_items", "typ": "int", "desc": "number of items this can hold inside itself. TODO: get rid of all uses of 'ability->gun_config.deck_capacity' and replace them with this!"}, {"field": "ui_sprite", "typ": "std::string", "desc": "sprite displayed for the item in various UIs. If not empty overrides sprites declared by Ability and ItemAction"}, {"field": "ui_description", "typ": "std::string", "desc": "item description displayed in various UIs"}, {"field": "enable_orb_hacks", "typ": "bool", "desc": ""}, {"field": "is_all_spells_book", "typ": "bool", "desc": ""}, {"field": "always_use_item_name_in_ui", "typ": "bool", "desc": ""}, {"field": "custom_pickup_string", "typ": "std::string", "desc": "if set, this is used for the 'Press $0 to pick $1' message"}, {"field": "ui_display_description_on_pick_up_hint", "typ": "bool", "desc": ""}, {"field": "next_frame_pickable", "typ": "int", "desc": ""}, {"field": "npc_next_frame_pickable", "typ": "int", "desc": "NPC have their own next_frame_pickable, because this is used to make NPCs not pick up gold, which also meant player couldn't pick up that gold"}, {"field": "is_pickable", "typ": "bool", "desc": "can this be picked up and placed on someone's inventory"}, {"field": "is_hittable_always", "typ": "bool", "desc": "to override the weirdness that is is_pickable, which affects if this is hittable or not. If true, will always be hittable regardless of is_pickable"}, {"field": "item_pickup_radius", "typ": "float", "desc": "how many pixels away can this item be picked up from"}, {"field": "camera_max_distance", "typ": "float", "desc": "how far can we move the camera from the player when this item is equipped"}, {"field": "camera_smooth_speed_multiplier", "typ": "float", "desc": "how quickly does the camera follow player?"}, {"field": "has_been_picked_by_player", "typ": "bool", "desc": ""}, {"field": "mFramePickedUp", "typ": "int", "desc": ""}, {"field": "spawn_pos", "typ": "vec2", "desc": "the position where this item spawned"}, {"field": "preferred_inventory", "typ": "INVENTORY_KIND::Enum", "desc": "Which inventory do we go to when we're picked up, if it's not full."}, {"field": "inventory_slot", "typ": "ivec2", "desc": "our preferred slot (x,y) in the inventory"}, {"field": "mItemUid", "typ": "int", "desc": ""}, {"field": "mIsIdentified", "typ": "bool", "desc": ""}]}, {"name": "ItemCostComponent", "fields": [{"field": "cost", "typ": "int64", "desc": ""}, {"field": "stealable", "typ": "bool", "desc": "if set - will check that it's within an area called shop"}, {"field": "mExCost", "typ": "int64", "desc": "used to change the text on the sprite"}]}, {"name": "ItemPickUpperComponent", "fields": [{"field": "is_in_npc", "typ": "bool", "desc": ""}, {"field": "pick_up_any_item_buggy", "typ": "bool", "desc": "If true, will pick up _any_ item. Breaks all kinds of things, but maybe mods will find this fun to mess around with"}, {"field": "is_immune_to_kicks", "typ": "bool", "desc": "if set, won't drop the wand if kicked. Mainly used by wand ghosts."}, {"field": "only_pick_this_entity", "typ": "EntityID", "desc": "picks up this entity and only this entity. Overrides the is_in_npc checks that try to limit things to pickuppable wands"}, {"field": "drop_items_on_death", "typ": "bool", "desc": "if true, will drop all items. E.g. if true for player, player drops their wands"}, {"field": "mLatestItemOverlapInfoBoxPosition", "typ": "vec2", "desc": ""}]}, {"name": "ItemRechargeNearGroundComponent", "fields": [{"field": "TEMP_TEMPY", "typ": "float", "desc": ""}, {"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}]}, {"name": "ItemStashComponent", "fields": [{"field": "throw_openable_cooldown_frames", "typ": "int", "desc": ""}, {"field": "init_children", "typ": "bool", "desc": ""}, {"field": "mNextFrameOpenable", "typ": "int", "desc": ""}, {"field": "mFrameOpened", "typ": "int", "desc": ""}]}, {"name": "KickComponent", "fields": [{"field": "can_kick", "typ": "bool", "desc": "e.g. telekinetic kick disables this"}, {"field": "kick_radius", "typ": "float", "desc": ""}, {"field": "telekinesis_throw_speed", "typ": "float", "desc": "this is here, so that STRONG_KICK -perk can affect telekinetic kick as well"}, {"field": "kick_entities", "typ": "std::string", "desc": "comma separated list of entities that are loaded when player kicks"}, {"field": "max_force", "typ": "LensValue", "desc": ""}, {"field": "player_kickforce", "typ": "LensValue", "desc": ""}, {"field": "kick_damage", "typ": "LensValue", "desc": "( 1.f / 25.f )"}, {"field": "kick_knockback", "typ": "LensValue", "desc": "knockback force for entities"}]}, {"name": "LaserEmitterComponent", "fields": [{"field": "is_emitting", "typ": "bool", "desc": "If 1, will emit all the time"}, {"field": "emit_until_frame", "typ": "int32", "desc": "Can be used to activate a laser temporarily"}, {"field": "laser_angle_add_rad", "typ": "float", "desc": "Beam angle = entity angle + laser_angle_add_rad"}, {"field": "laser", "typ": "ConfigLaser", "desc": ""}]}, {"name": "LevitationComponent", "fields": [{"field": "radius", "typ": "float", "desc": "the radius in which we look for entities / bodies to float"}, {"field": "entity_force", "typ": "float", "desc": "how much do we apply the mouse movements to the entitiy"}, {"field": "box2d_force", "typ": "float", "desc": "how much do we apply the mouse movements to the entitiy"}, {"field": "effect_lifetime_frames", "typ": "int", "desc": ""}]}, {"name": "LifetimeComponent", "fields": [{"field": "lifetime", "typ": "int", "desc": "if anything else than -1 will kill this entity when this many frames have passed"}, {"field": "fade_sprites", "typ": "bool", "desc": "if 1, sprites will be faded as lifetime gets lower"}, {"field": "kill_parent", "typ": "bool", "desc": "if 1, will kill the parent entity"}, {"field": "kill_all_parents", "typ": "bool", "desc": "if 1, will kill all the parents entity"}, {"field": "serialize_duration", "typ": "bool", "desc": "if 1, will retain kill_frame and creation_frame over serialization"}, {"field": "kill_frame_serialized", "typ": "int", "desc": "frame that this is killed at"}, {"field": "creation_frame_serialized", "typ": "int", "desc": "frame that this is killed at"}, {"field": "randomize_lifetime", "typ": "ValueRange", "desc": "this is added to the lifetime"}, {"field": "creation_frame", "typ": "int", "desc": "we'll set this to GG.GetFrameNum() when this component is created"}, {"field": "kill_frame", "typ": "int", "desc": "frame that this is killed at"}]}, {"name": "LightComponent", "fields": [{"field": "update_properties", "typ": "bool", "desc": "turn this on if you expect this to function like the other components"}, {"field": "radius", "typ": "float", "desc": "The radius of the light in world pixels."}, {"field": "int", "typ": "unsigned", "desc": "Color red 0-255"}, {"field": "int", "typ": "unsigned", "desc": "Color green 0-255"}, {"field": "int", "typ": "unsigned", "desc": "Color blue 0-255"}, {"field": "offset_x", "typ": "float", "desc": "Offset from the center of entity."}, {"field": "offset_y", "typ": "float", "desc": "Offset from the center of entity."}, {"field": "fade_out_time", "typ": "float", "desc": "time in seconds, if not 0, this is how long this takes to die, when the component is destroyed"}, {"field": "blinking_freq", "typ": "float", "desc": "if less than 1, will blink randomly when rand() < blinking_freq"}, {"field": "mAlpha", "typ": "float", "desc": ""}, {"field": "mSprite", "typ": "as::Sprite*", "desc": ""}]}, {"name": "LightningComponent", "fields": [{"field": "sprite_lightning_file", "typ": "std::string", "desc": "particle effect, from where the file is loaded that lightning is generated from"}, {"field": "is_projectile", "typ": "bool", "desc": "if this is true, it's a projectile lightning and looks for ProjectileComponent and uses the data from there to move it"}, {"field": "explosion_type", "typ": "int", "desc": "1 = lightning trail"}, {"field": "arc_lifetime", "typ": "int", "desc": "remaining number of frames the arc exists"}, {"field": "config_explosion", "typ": "ConfigExplosion", "desc": ""}, {"field": "mExPosition", "typ": "vec2", "desc": "stores the ex position of this entity"}, {"field": "mArcTarget", "typ": "EntityID", "desc": "if 'mArcTarget' points to an existing entity a lighting arc will be created between this entity and 'mArcTarget'"}]}, {"name": "LimbBossComponent", "fields": [{"field": "state", "typ": "int", "desc": ""}, {"field": "mStatePrev", "typ": "int", "desc": ""}, {"field": "mMoveToPositionX", "typ": "float", "desc": ""}, {"field": "mMoveToPositionY", "typ": "float", "desc": ""}]}, {"name": "LiquidDisplacerComponent", "fields": [{"field": "radius", "typ": "int", "desc": ""}, {"field": "velocity_x", "typ": "float", "desc": ""}, {"field": "velocity_y", "typ": "float", "desc": ""}, {"field": "mPrevX", "typ": "int", "desc": ""}, {"field": "mPrevY", "typ": "int", "desc": ""}]}, {"name": "LoadEntitiesComponent", "fields": [{"field": "entity_file", "typ": "std::string", "desc": "path to the entity file we should load"}, {"field": "kill_entity", "typ": "bool", "desc": "if 1, we kill our entity when it is created"}, {"field": "timeout_frames", "typ": "int", "desc": "for timer"}, {"field": "mTimerTriggerFrame", "typ": "int", "desc": ""}, {"field": "count", "typ": "ValueRangeInt", "desc": "how many entities should be loaded (random range)"}]}, {"name": "LocationMarkerComponent", "fields": [{"field": "id", "typ": "int", "desc": ""}]}, {"name": "LooseGroundComponent", "fields": [{"field": "probability", "typ": "float", "desc": "how often do we do this... shoots a ray in random direction and does the loosening"}, {"field": "max_durability", "typ": "int", "desc": "if material durability > max_durability, it is not loosened"}, {"field": "max_distance", "typ": "float", "desc": "how far raytraces to find things to loosen up"}, {"field": "max_angle", "typ": "float", "desc": "how much raytraces go to different directions around the up-vector. pi=full circle"}, {"field": "min_radius", "typ": "int", "desc": "the minimum radius of our loosening of pixels"}, {"field": "max_radius", "typ": "int", "desc": "the maximum radius of our loosening of pixels"}, {"field": "chunk_probability", "typ": "float", "desc": "if > 0, will drop box2d chunks of the ceiling"}, {"field": "chunk_max_angle", "typ": "float", "desc": "how much raytraces go to different directions around the up-vector. pi=full circle"}, {"field": "chunk_count", "typ": "int", "desc": "how many chunks are we allowed to do, -1 = infinite"}, {"field": "collapse_images", "typ": "std::string", "desc": "loads these files randomly to do the collapse shapes"}, {"field": "chunk_material", "typ": "int", "desc": "String name of chunk material"}, {"field": "mChunkCount", "typ": "int", "desc": "how many chunks are we allowed to do, -1 = infinite"}]}, {"name": "LuaComponent", "fields": [{"field": "script_source_file", "typ": "std::string", "desc": ""}, {"field": "execute_on_added", "typ": "bool", "desc": ""}, {"field": "execute_on_removed", "typ": "bool", "desc": ""}, {"field": "execute_every_n_frame", "typ": "int", "desc": "1 = execute every frame. 2 = execute every second frame. 3 = execute every third frame and so on. -1 = execute only on add/remove/event"}, {"field": "execute_times", "typ": "int", "desc": "How many times should the script be executed? < 1 means infinite"}, {"field": "limit_how_many_times_per_frame", "typ": "int", "desc": "-1 = infinite. Use this to limit how many times this can be executed per frame. Currently only used to limit script_shot from being executed forever."}, {"field": "limit_to_every_n_frame", "typ": "int", "desc": "-1 = no limit. Currently only used to limit script_shot from being executed every frame."}, {"field": "limit_all_callbacks", "typ": "bool", "desc": "NOTE( Petri ): 19.8.2023 - by default limit_how_many_times_per_frame and limit_to_every_n_frame only works for script_shot. If this is set to true, will limit all callbacks. Also note that this limit is shared within this component. So if this is true and both script_shot and script_damage_received and both are called within limit_to_every_n_frame frames, only one of them will be called."}, {"field": "remove_after_executed", "typ": "bool", "desc": ""}, {"field": "enable_coroutines", "typ": "bool", "desc": ""}, {"field": "call_init_function", "typ": "bool", "desc": " if 1, calls function init( entity_id:int ) after running the code in the file scope of script_source_file along with all mod appends. Does nothing if execute_on_added is 0"}, {"field": "script_enabled_changed", "typ": "std::string", "desc": "if set, calls function 'enabled_changed( entity_id:int, is_enabled:bool )' when the IsEnabled status of this LuaComponent is changed"}, {"field": "script_damage_received", "typ": "std::string", "desc": "if set, calls function 'damage_received( damage:number, message:string, entity_thats_responsible:int, is_fatal:bool, projectile_thats_responsible:int )' when we receive a message about damage (Message_DamageReceived)"}, {"field": "script_damage_about_to_be_received", "typ": "std::string", "desc": "if set, calls function 'damage_about_to_be_received( damage:number, x:number, y:number, entity_thats_responsible:int, critical_hit_chance:int )' when we receive a message (Message_DamageAboutToBeReceived) -> new_damage:number,new_critical_hit_chance:int"}, {"field": "script_item_picked_up", "typ": "std::string", "desc": "if set, calls function 'item_pickup( int entity_item, int entity_pickupper, string item_name )' when message 'Message_ItemPickUp' is called"}, {"field": "script_shot", "typ": "std::string", "desc": "if set, calls function 'shot( projectile_entity_id )' when we receive Message_Shot"}, {"field": "script_collision_trigger_hit", "typ": "std::string", "desc": "if set, calls function 'collision_trigger( colliding_entity_id )' when we receive Message_CollisionTriggerHit"}, {"field": "script_collision_trigger_timer_finished", "typ": "std::string", "desc": "if set, calls function 'collision_trigger_timer_finished()' when we receive Message_CollisionTriggerTimerFinished"}, {"field": "script_physics_body_modified", "typ": "std::string", "desc": "if set, calls function 'physics_body_modified( is_destroyed )' when physics body has been modified"}, {"field": "script_pressure_plate_change", "typ": "std::string", "desc": "if set, calls function 'pressure_plate_change( new_state )' when PressurePlateComponent decides that things have change"}, {"field": "script_inhaled_material", "typ": "std::string", "desc": "if set, calls function 'inhaled_material( material_name, count )' once per second for each inhaled material"}, {"field": "script_death", "typ": "std::string", "desc": "if set, calls function 'death( int damage_type_bit_field, string damage_message, int entity_thats_responsible, bool drop_items )' when we receive message Message_Death"}, {"field": "script_throw_item", "typ": "std::string", "desc": "if set, calls function 'throw_item( from_x, from_y, to_x, to_y )' when we receive message Message_ThrowItem"}, {"field": "script_material_area_checker_failed", "typ": "std::string", "desc": "if set, calls function 'material_area_checker_failed( pos_x, pos_y, )' when we receive message Message_MaterialAreaCheckerFailed"}, {"field": "script_material_area_checker_success", "typ": "std::string", "desc": "if set, calls function 'material_area_checker_success( pos_x, pos_y, )' when we receive message Message_MaterialAreaCheckerSuccess"}, {"field": "script_electricity_receiver_switched", "typ": "std::string", "desc": "if set, calls function 'electricity_receiver_switched( bool is_electrified )' when we receive message Message_ElectricityReceiverSwitched"}, {"field": "script_electricity_receiver_electrified", "typ": "std::string", "desc": "if set, calls function 'electricity_receiver_electrified()' when we receive message Message_ElectricityReceiverElectrified"}, {"field": "script_kick", "typ": "std::string", "desc": "if set, calls function 'kick( entity_who_kicked )' when we receive message Message_Kick"}, {"field": "script_interacting", "typ": "std::string", "desc": "if set, calls function 'interacting( entity_who_interacted, entity_interacted, interactable_name )' when we receive message Message_Interaction"}, {"field": "script_audio_event_dead", "typ": "std::string", "desc": "if set, calls function 'audio_event_dead( bank_file, event_root )' when we receive message Message_AudioEventDead"}, {"field": "script_wand_fired", "typ": "std::string", "desc": "if set, calls function 'wand_fired( gun_entity_id )' when we receive Message_WandFired"}, {"field": "script_teleported", "typ": "std::string", "desc": "if set, calls function 'teleported( from_x, from_y, to_x, to_y, bool portal_teleport )' when we receive Message_Teleported"}, {"field": "script_portal_teleport_used", "typ": "std::string", "desc": "if set, calls function 'portal_teleport_used( entity_that_was_teleported, from_x, from_y, to_x, to_y )' when we receive Message_PortalTeleportUsed"}, {"field": "script_polymorphing_to", "typ": "std::string", "desc": "if set, calls function 'polymorphing_to( string_entity_we_are_about_to_polymorph_to )' when we receive Message_PolymorphingTo"}, {"field": "script_biome_entered", "typ": "std::string", "desc": "if set, calls function 'biome_entered( string_biome_name, string_biome_old_name )' when this entity changes biomes. Requires BiomeTrackerComponent"}, {"field": "mLastExecutionFrame", "typ": "int", "desc": ""}, {"field": "mTimesExecutedThisFrame", "typ": "int", "desc": "tracks how many times we've executed this frame. This will linger on and store the old value of the old frames. Used internally."}, {"field": "mModAppendsDone", "typ": "bool", "desc": ""}, {"field": "vm_type", "typ": "LUA_VM_TYPE::Enum", "desc": "Do we share a single Lua virtual machine for everyone who runs 'script_source_file' ('SHARED_BY_MANY_COMPONENTS'), create one VM per one LuaComponent and reuse the VM in case the component runs the script multiple times ('ONE_PER_COMPONENT_INSTANCE'), or create a new VM every time the script is executed ('CREATE_NEW_EVERY_EXECUTION', deprecated)?"}, {"field": "mNextExecutionTime", "typ": "int", "desc": ""}, {"field": "mTimesExecuted", "typ": "int", "desc": ""}, {"field": "mLuaManager", "typ": "LuaManager*", "desc": ""}, {"field": "mPersistentValues", "typ": "ValueMap", "desc": ""}]}, {"name": "MagicConvertMaterialComponent", "fields": [{"field": "radius", "typ": "int", "desc": ""}, {"field": "min_radius", "typ": "int", "desc": "allows for convert to happen from x pixels from the center"}, {"field": "is_circle", "typ": "bool", "desc": ""}, {"field": "steps_per_frame", "typ": "int", "desc": ""}, {"field": "from_material_tag", "typ": "std::string", "desc": "the tag of material, e.g. [liquid]"}, {"field": "from_any_material", "typ": "bool", "desc": "if 1, converts any cells of any material to 'to_materia'"}, {"field": "clean_stains", "typ": "bool", "desc": ""}, {"field": "extinguish_fire", "typ": "bool", "desc": ""}, {"field": "fan_the_flames", "typ": "int", "desc": "if > 0, will call UpdateFire() fan_the_flames times"}, {"field": "temperature_reaction_temp", "typ": "int32", "desc": "if != 0, will use the 'cold_freezes_to_materials' and 'warmth_melts_to_materials' in CellData to convert cells different materials"}, {"field": "ignite_materials", "typ": "int", "desc": "if > 0, will call Ignite() with ingite_materials as probability_of_fire"}, {"field": "loop", "typ": "bool", "desc": ""}, {"field": "kill_when_finished", "typ": "bool", "desc": ""}, {"field": "convert_entities", "typ": "bool", "desc": "if 1, kills entities with a damagemodel and converts them to 'to_material'"}, {"field": "stain_frozen", "typ": "bool", "desc": "petri hax"}, {"field": "reaction_audio_amount", "typ": "float", "desc": "if > 0, will generate chemical reaction audio at converted cells"}, {"field": "convert_same_material", "typ": "bool", "desc": "9.10.2020 - added this because at the end this caused the 'white ring' to appear, set it to false if you don't want constant whiteout"}, {"field": "from_material_array", "typ": "std::string", "desc": ""}, {"field": "to_material_array", "typ": "std::string", "desc": ""}, {"field": "mRadius", "typ": "int", "desc": ""}, {"field": "from_material", "typ": "int", "desc": ""}, {"field": "to_material", "typ": "int", "desc": ""}, {"field": "mUseArrays", "typ": "bool", "desc": ""}, {"field": "mFromMaterialArray", "typ": "std::vector", "desc": ""}, {"field": "mToMaterialArray", "typ": "std::vector", "desc": ""}]}, {"name": "MagicXRayComponent", "fields": [{"field": "radius", "typ": "int", "desc": ""}, {"field": "steps_per_frame", "typ": "int", "desc": ""}, {"field": "mStep", "typ": "int", "desc": ""}, {"field": "mRadius", "typ": "int", "desc": ""}]}, {"name": "ManaReloaderComponent", "fields": []}, {"name": "MaterialAreaCheckerComponent", "fields": [{"field": "update_every_x_frame", "typ": "int", "desc": "if something other than 0 or 1, will only update_every_x_frames "}, {"field": "look_for_failure", "typ": "bool", "desc": "if true, will send message Message_MaterialAreaCheckerFailed if the material doesn't exist. If false, will send a message Message_MaterialAreaCheckerSuccess if the aabb is full of material and material2"}, {"field": "count_min", "typ": "int", "desc": "If > 0, and look_for_failure=0, will send message if material count exceeds this number of cells"}, {"field": "always_check_fullness", "typ": "bool", "desc": "if 1, and look_for_failure=0, will always check the whole area for cells"}, {"field": "kill_after_message", "typ": "bool", "desc": "will kill this entity after sending the message"}, {"field": "area_aabb", "typ": "types::aabb", "desc": "aabb offset, we check that this aabb contains only material"}, {"field": "material", "typ": "int", "desc": "String name of material that we check that the aabb contains"}, {"field": "material2", "typ": "int", "desc": "String name of material2 that we check that the aabb contains"}, {"field": "mPosition", "typ": "int", "desc": "keeps track where we are"}, {"field": "mLastFrameChecked", "typ": "int", "desc": "keeps track of how often we've checked"}]}, {"name": "MaterialInventoryComponent", "fields": [{"field": "drop_as_item", "typ": "bool", "desc": "if true, drops a bag that the player can big up"}, {"field": "on_death_spill", "typ": "bool", "desc": "if true, on the death this will explode all the materials into air"}, {"field": "leak_gently", "typ": "bool", "desc": "NOTE( Petri ): 11.8.2023 - set this to false for old style leaky hidden piles situation."}, {"field": "leak_on_damage_percent", "typ": "float", "desc": "if higher than 0 then it might leak when projectile damage happens"}, {"field": "leak_pressure_min", "typ": "float", "desc": "leak pressure coefficient"}, {"field": "leak_pressure_max", "typ": "float", "desc": "leak pressure coefficient"}, {"field": "min_damage_to_leak", "typ": "float", "desc": "the minimum damage that has to be done in order for a leak to occur"}, {"field": "b2_force_on_leak", "typ": "float", "desc": "if 0, nothing happens, elsewise will add a b2 force to the particleemitter which will push the b2body"}, {"field": "death_throw_particle_velocity_coeff", "typ": "float", "desc": "how far do we throw material particles on death?"}, {"field": "kill_when_empty", "typ": "bool", "desc": "if set, will send MessageDeath when materials are drained"}, {"field": "halftime_materials", "typ": "bool", "desc": "if true, will multiply the materials with the given halftimes"}, {"field": "do_reactions", "typ": "int", "desc": "NOTE( Petri ): 15.8.2023 - if > 0, will do CellReactions between the materials. Value is the percent chance of how often. 100 = every frame "}, {"field": "do_reactions_explosions", "typ": "bool", "desc": "requires do_reactions > 0 - are we allowed to do reaction explosions?"}, {"field": "do_reactions_entities", "typ": "bool", "desc": "requires do_reactions > 0 - are we allowed to load entities when doing reactions?"}, {"field": "reaction_speed", "typ": "int", "desc": "Note( Petri ): 17.8.2023 - how 'fast' do we let reactions happen. How many pixels of material do we convert at one time (5-10) seems like a nice speed."}, {"field": "reactions_shaking_speeds_up", "typ": "bool", "desc": "Note( Petri ): 17.8.2023 - added the ability of shaking the bottle to cause reactions to happen quicker. "}, {"field": "max_capacity", "typ": "double", "desc": "how much materials we can store in total. < 0 = infinite"}, {"field": "audio_collision_size_modifier_amount", "typ": "float", "desc": "if > 0, 'fullness of this container' * 'audio_collision_size_modifier_amount' is added to collision audio event size"}, {"field": "last_frame_drank", "typ": "int32", "desc": "last frame someone ingested from this via IngestionSystem"}, {"field": "count_per_material_type", "typ": "MATERIAL_VEC_DOUBLES", "desc": "Count of each material indexed by material type ID"}, {"field": "is_death_handled", "typ": "bool", "desc": ""}, {"field": "ex_position", "typ": "vec2", "desc": "used to figure out movement velocity"}, {"field": "ex_angle", "typ": "float", "desc": "used to figure out movement velocity"}]}, {"name": "MaterialSeaSpawnerComponent", "fields": [{"field": "speed", "typ": "int", "desc": "How many pixels to cover per one direction per one frame"}, {"field": "sine_wavelength", "typ": "float", "desc": "Parameters for sine wave that affects material spawn pattern"}, {"field": "sine_amplitude", "typ": "float", "desc": "Parameters for sine wave that affects material spawn pattern"}, {"field": "noise_scale", "typ": "double", "desc": "Parameters for noise that affects material spawn pattern"}, {"field": "noise_threshold", "typ": "double", "desc": "Parameters for noise that affects material spawn pattern"}, {"field": "m_position", "typ": "int", "desc": ""}, {"field": "frames_run", "typ": "int", "desc": "to help keep the effect"}, {"field": "material", "typ": "int", "desc": "String name of material this creates"}, {"field": "size", "typ": "ivec2", "desc": "Size of the area to cover"}, {"field": "offset", "typ": "ivec2", "desc": "Offset of the center of the area to cover"}]}, {"name": "MaterialSuckerComponent", "fields": [{"field": "material_type", "typ": "int", "desc": "0 = liquid, 1 = sand, 2 = gas (arbitary order)"}, {"field": "barrel_size", "typ": "int", "desc": "how many pixels can we suck up"}, {"field": "num_cells_sucked_per_frame", "typ": "int", "desc": "How many cells at max can we suck per frame?"}, {"field": "set_projectile_to_liquid", "typ": "bool", "desc": "if set, will set the projectile what ever we're sucking...?"}, {"field": "last_material_id", "typ": "int", "desc": "hax... this is set if we use set_projectile_to_liquid"}, {"field": "suck_gold", "typ": "bool", "desc": "if set will just suck gold and update wallet"}, {"field": "suck_health", "typ": "bool", "desc": "if set will just suck healthium material and add 1 hp every sucked healthium"}, {"field": "suck_static_materials", "typ": "bool", "desc": "will suck static materials from the world"}, {"field": "suck_tag", "typ": "std::string", "desc": "if set, will only suck materials with this tag. NOTE, will also require the correct material_type to be set"}, {"field": "mAmountUsed", "typ": "int", "desc": "how full are we"}, {"field": "randomized_position", "typ": "types::iaabb", "desc": "random offset for pos, where we look for pixels"}, {"field": "mGoldAccumulator", "typ": "int", "desc": "accumulates amount of gold picked during consecutive frames"}, {"field": "mLastFramePickedGold", "typ": "int", "desc": "last frame we picked gold"}]}, {"name": "MoveToSurfaceOnCreateComponent", "fields": [{"field": "lookup_radius", "typ": "float", "desc": ""}, {"field": "offset_from_surface", "typ": "float", "desc": ""}, {"field": "ray_count", "typ": "int", "desc": ""}, {"field": "verlet_min_joint_distance", "typ": "float", "desc": ""}]}, {"name": "MusicEnergyAffectorComponent", "fields": [{"field": "energy_target", "typ": "float", "desc": "the energy this makes music go towards"}, {"field": "fade_range", "typ": "float", "desc": "if > 0, fade between 0 and energy_target based on distance to this entity"}, {"field": "trigger_danger_music", "typ": "bool", "desc": "if 1, attempts to trigger danger music no matter what energy level is reached"}, {"field": "fog_of_war_threshold", "typ": "int", "desc": "if fog of war at position of this entity is greater than 'fog_of_war_threshold', this has no effect"}, {"field": "is_enemy", "typ": "bool", "desc": ""}, {"field": "energy_lerp_up_speed_multiplier", "typ": "float", "desc": ""}]}, {"name": "NinjaRopeComponent", "fields": [{"field": "max_length", "typ": "float", "desc": ""}, {"field": "mLength", "typ": "float", "desc": ""}]}, {"name": "NullDamageComponent", "fields": [{"field": "null_chance", "typ": "float", "desc": "if less than 1, then will roll the die to see if it will NULL all damage. Stick this into your projectile entity"}]}, {"name": "OrbComponent", "fields": [{"field": "orb_id", "typ": "int", "desc": "must be unique for every orb in the world"}]}, {"name": "ParticleEmitterComponent", "fields": [{"field": "emitted_material_name", "typ": "std::string", "desc": ""}, {"field": "create_real_particles", "typ": "bool", "desc": "used to be emit_real_particles - creates these particles in the grid, if that happens velocity and lifetime are ignored"}, {"field": "emit_real_particles", "typ": "bool", "desc": "this creates particles that will behave like particles, but work outside of the screen"}, {"field": "emit_cosmetic_particles", "typ": "bool", "desc": "particle does have collisions, but no other physical interactions with the world. the particles are culled outside camera region"}, {"field": "cosmetic_force_create", "typ": "bool", "desc": "cosmetic particles are created inside grid cells"}, {"field": "render_back", "typ": "bool", "desc": "for cosmetic particles, if they are rendered on front or in the back..."}, {"field": "render_ultrabright", "typ": "bool", "desc": "if 1, particles made of a glowing material will be 3x as bright as usually"}, {"field": "collide_with_grid", "typ": "bool", "desc": "for cosmetic particles, if 1 the particles collide with grid and only exist in screen space"}, {"field": "collide_with_gas_and_fire", "typ": "bool", "desc": "does it collide with gas and fire, works with create_real_particles and raytraced images "}, {"field": "particle_single_width", "typ": "bool", "desc": "for cosmetic particles, forces them (gas,fire) to be only 1 pixel wide "}, {"field": "emit_only_if_there_is_space", "typ": "bool", "desc": "This is turned for potions after they take some damage and start leaking"}, {"field": "emitter_lifetime_frames", "typ": "int", "desc": "emitter lifetime in frames. -1 = infinite"}, {"field": "fire_cells_dont_ignite_damagemodel", "typ": "bool", "desc": "if set, and fire cells are created, this changes their default behaviour of igniting DamageModels"}, {"field": "color_is_based_on_pos", "typ": "bool", "desc": "if true, will get the particle color based on the world position (instead of randomizing it)"}, {"field": "custom_alpha", "typ": "float", "desc": "if >= 0, will use this as particle alpha"}, {"field": "x_pos_offset_min", "typ": "float", "desc": ""}, {"field": "y_pos_offset_min", "typ": "float", "desc": ""}, {"field": "x_pos_offset_max", "typ": "float", "desc": ""}, {"field": "y_pos_offset_max", "typ": "float", "desc": ""}, {"field": "area_circle_sector_degrees", "typ": "float", "desc": ""}, {"field": "x_vel_min", "typ": "float", "desc": ""}, {"field": "x_vel_max", "typ": "float", "desc": ""}, {"field": "y_vel_min", "typ": "float", "desc": ""}, {"field": "y_vel_max", "typ": "float", "desc": ""}, {"field": "direction_random_deg", "typ": "float", "desc": ""}, {"field": "velocity_always_away_from_center", "typ": "float", "desc": "if set, will make the velocity's rotation always away from center of randomized aabb"}, {"field": "lifetime_min", "typ": "float", "desc": ""}, {"field": "lifetime_max", "typ": "float", "desc": ""}, {"field": "airflow_force", "typ": "float", "desc": ""}, {"field": "airflow_time", "typ": "float", "desc": ""}, {"field": "airflow_scale", "typ": "float", "desc": ""}, {"field": "friction", "typ": "float", "desc": ""}, {"field": "attractor_force", "typ": "float", "desc": "If > 0, an attractor is created at the position of the entity that owns this component"}, {"field": "emission_interval_min_frames", "typ": "int", "desc": ""}, {"field": "emission_interval_max_frames", "typ": "int", "desc": ""}, {"field": "emission_chance", "typ": "int", "desc": ""}, {"field": "delay_frames", "typ": "int", "desc": "if set will delay this many frames until starts"}, {"field": "is_emitting", "typ": "bool", "desc": ""}, {"field": "use_material_inventory", "typ": "bool", "desc": "if set, it'll use MaterialInventoryComponent as the source of the particles emitted"}, {"field": "is_trail", "typ": "bool", "desc": "if set, will do a trail based on the previous position and current position"}, {"field": "trail_gap", "typ": "float", "desc": "if > 0, trail particles will be generated this far from each other between our old and new position, else [count_min-count_max] particles will be generated on the line"}, {"field": "render_on_grid", "typ": "bool", "desc": "if set, particle render positions will be snapped to cell grid"}, {"field": "fade_based_on_lifetime", "typ": "bool", "desc": "if set, particle's position in its lifetime will determine the rendering alpha"}, {"field": "draw_as_long", "typ": "bool", "desc": "if set, particle will rendered as a trail along it's movement vector"}, {"field": "b2_force", "typ": "float", "desc": "if 0 nothing happens, if 1 will apply a force to the physics body (if has one), also requires that we use the material inventory"}, {"field": "set_magic_creation", "typ": "bool", "desc": "if set will set the magic creation 1 in the cells and do the white glow effect"}, {"field": "image_animation_file", "typ": "std::string", "desc": "file to use for image-based animation"}, {"field": "image_animation_colors_file", "typ": "std::string", "desc": "file to use for image-based animation"}, {"field": "image_animation_speed", "typ": "float", "desc": "how long do we stay on one frame of image-based animation. 0.5 means two game frames per one animation frame. 2.0 means two animation frames per one game frame, and so on. 0 means we always emit at time 0 of the animation."}, {"field": "image_animation_loop", "typ": "bool", "desc": "does image-based animation keep looping while this component is active?"}, {"field": "image_animation_phase", "typ": "float", "desc": "the point in time [0,1] where the image-based animation will start the first cycle"}, {"field": "image_animation_emission_probability", "typ": "float", "desc": "[0,1], probability of emitting image based particles is multiplied with this"}, {"field": "image_animation_raytrace_from_center", "typ": "bool", "desc": "enable this to disable image_animations (from the center) going through the world"}, {"field": "image_animation_use_entity_rotation", "typ": "bool", "desc": "if 1, image animation emission will be rotated based on entity rotation"}, {"field": "ignore_transform_updated_msg", "typ": "bool", "desc": "if 1, mExPosition and m_last_emit_position will not be updated when receiving Message_TransformUpdated"}, {"field": "color", "typ": "uint32", "desc": ""}, {"field": "offset", "typ": "vec2", "desc": ""}, {"field": "area_circle_radius", "typ": "ValueRange", "desc": "If > 0, the particles will be emitted inside a circular area defined by the min and max bounds of 'area_circle_radius'."}, {"field": "gravity", "typ": "vec2", "desc": ""}, {"field": "count_min", "typ": "LensValue", "desc": ""}, {"field": "count_max", "typ": "LensValue", "desc": ""}, {"field": "mExPosition", "typ": "vec2", "desc": "is used with is_trail"}, {"field": "mMaterialInventoryMax", "typ": "int", "desc": "this is how we figure out the pressure, when using material_inventory"}, {"field": "m_material_id", "typ": "LensValue", "desc": ""}, {"field": "m_next_emit_frame", "typ": "int", "desc": ""}, {"field": "m_has_emitted", "typ": "bool", "desc": ""}, {"field": "m_last_emit_position", "typ": "ivec2", "desc": ""}, {"field": "m_image_based_animation_time", "typ": "float", "desc": ""}, {"field": "m_collision_angles", "typ": "float*", "desc": ""}, {"field": "m_particle_attractor_id", "typ": "int16", "desc": ""}]}, {"name": "PathFindingComponent", "fields": [{"field": "search_depth_max_no_goal", "typ": "int", "desc": "TODO: Comment"}, {"field": "iterations_max_no_goal", "typ": "int", "desc": "TODO: Comment"}, {"field": "search_depth_max_with_goal", "typ": "int", "desc": "TODO: Comment"}, {"field": "iterations_max_with_goal", "typ": "int", "desc": "TODO: Comment"}, {"field": "cost_of_flying", "typ": "float", "desc": "TODO: Comment"}, {"field": "distance_to_reach_node_x", "typ": "int", "desc": "TODO: Comment"}, {"field": "distance_to_reach_node_y", "typ": "int", "desc": "TODO: Comment"}, {"field": "frames_to_get_stuck", "typ": "int", "desc": "TODO: Comment"}, {"field": "frames_between_searches", "typ": "int", "desc": "TODO: Comment"}, {"field": "y_walking_compensation", "typ": "float", "desc": "TODO: Comment"}, {"field": "can_fly", "typ": "bool", "desc": "TODO: Comment"}, {"field": "can_walk", "typ": "bool", "desc": "TODO: Comment"}, {"field": "can_jump", "typ": "bool", "desc": "TODO: Comment"}, {"field": "can_dive", "typ": "bool", "desc": "TODO: Comment"}, {"field": "can_swim_on_surface", "typ": "bool", "desc": "TODO: Comment"}, {"field": "never_consider_line_of_sight", "typ": "bool", "desc": "if 1, we require a path to have an entity at the goal, having line of sight to the entity is not enough"}, {"field": "space_required", "typ": "float", "desc": "how far (in cells) must a point on our route be from the nearest wall to consider it passable?"}, {"field": "max_jump_distance_from_camera", "typ": "float", "desc": "TODO: Comment"}, {"field": "jump_speed", "typ": "float", "desc": "TODO: Comment"}, {"field": "initial_jump_lob", "typ": "float", "desc": "TODO: Comment"}, {"field": "initial_jump_max_distance_x", "typ": "float", "desc": "TODO: Comment"}, {"field": "initial_jump_max_distance_y", "typ": "float", "desc": "TODO: Comment"}, {"field": "read_state", "typ": "int", "desc": "Read only value to get mState as an integer. Used to detect when the worst cheesers are trying to cheese our beloved squidward."}, {"field": "jump_trajectories", "typ": "VECTOR_JUMPPARAMS", "desc": "TODO: Comment"}, {"field": "input", "typ": "PathFindingInput", "desc": "TODO: Comment"}, {"field": "waiting_for", "typ": "bool", "desc": "TODO: Comment"}, {"field": "next_search_frame", "typ": "int", "desc": "TODO: Comment"}, {"field": "path", "typ": "VECTOR_PATHNODE", "desc": "TODO: Comment"}, {"field": "path_next_node", "typ": "PathFindingResultNode", "desc": "TODO: Comment"}, {"field": "path_next_node_vector_to", "typ": "vec2", "desc": "TODO: Comment"}, {"field": "path_next_node_distance_to", "typ": "float", "desc": "TODO: Comment"}, {"field": "path_previous_node", "typ": "PathFindingNodeHandle", "desc": "TODO: Comment"}, {"field": "path_frames_stuck", "typ": "int", "desc": "TODO: Comment"}, {"field": "path_is_stuck", "typ": "bool", "desc": "TODO: Comment"}, {"field": "path_last_frame_with_job", "typ": "int", "desc": "TODO: Comment"}, {"field": "mLogic", "typ": "PathFindingLogic*", "desc": "this defines what is an acceptable path"}, {"field": "mFallbackLogic", "typ": "PathFindingLogic*", "desc": "we use this to define an acceptable path if mLogic doesn't return one"}, {"field": "mSelectedLogic", "typ": "PathFindingLogic*", "desc": "TODO: Comment"}, {"field": "mEnabled", "typ": "bool", "desc": "TODO: Comment"}, {"field": "mTimesStuck", "typ": "int", "desc": "TODO: Comment"}, {"field": "mNextClearDontApproachListFrame", "typ": "int", "desc": "TODO: Comment"}, {"field": "mNodeProximityCheckCorrectionY", "typ": "float", "desc": "TODO: Comment"}, {"field": "debug_path", "typ": "VECTOR_PATHNODE", "desc": "TODO: Comment"}, {"field": "jump_velocity_multiplier", "typ": "LensValue", "desc": "TODO: Comment"}]}, {"name": "PathFindingGridMarkerComponent", "fields": [{"field": "marker_work_flag", "typ": "int", "desc": ""}, {"field": "marker_offset_x", "typ": "float", "desc": ""}, {"field": "marker_offset_y", "typ": "float", "desc": ""}, {"field": "player_marker_radius", "typ": "float", "desc": ""}, {"field": "mNode", "typ": "PathFindingNodeHandle", "desc": "we change the work state of this node. thus we need to keep a reference to it"}]}, {"name": "PhysicsAIComponent", "fields": [{"field": "target_vec_max_len", "typ": "float", "desc": ""}, {"field": "force_coeff", "typ": "float", "desc": ""}, {"field": "force_balancing_coeff", "typ": "float", "desc": ""}, {"field": "force_max", "typ": "float", "desc": ""}, {"field": "torque_coeff", "typ": "float", "desc": ""}, {"field": "torque_balancing_coeff", "typ": "float", "desc": ""}, {"field": "torque_max", "typ": "float", "desc": ""}, {"field": "torque_damaged_max", "typ": "float", "desc": ""}, {"field": "torque_jump_random", "typ": "float", "desc": ""}, {"field": "damage_deactivation_probability", "typ": "int", "desc": ""}, {"field": "damage_deactivation_time_min", "typ": "int", "desc": ""}, {"field": "damage_deactivation_time_max", "typ": "int", "desc": ""}, {"field": "die_on_remaining_mass_percentage", "typ": "float", "desc": ""}, {"field": "levitate", "typ": "bool", "desc": ""}, {"field": "v0_jump_logic", "typ": "bool", "desc": ""}, {"field": "v0_swim_logic", "typ": "bool", "desc": ""}, {"field": "v0_body_id_logic", "typ": "bool", "desc": ""}, {"field": "swim_check_y_min", "typ": "int", "desc": ""}, {"field": "swim_check_y_max", "typ": "int", "desc": ""}, {"field": "swim_check_side_x", "typ": "int", "desc": ""}, {"field": "swim_check_side_y", "typ": "int", "desc": ""}, {"field": "keep_inside_world", "typ": "bool", "desc": "fix to the bug in which the spiders spawned inside the holy mountain, if set will try not to go into places which aren't loaded "}, {"field": "free_if_static", "typ": "bool", "desc": "set true for the boss, because box2d might turn this body into a static body, if it thinks it's glitching out. "}, {"field": "rotation_speed", "typ": "float", "desc": ""}, {"field": "mStartingMass", "typ": "float", "desc": ""}, {"field": "mMainBodyFound", "typ": "bool", "desc": ""}, {"field": "mNextFrameActive", "typ": "int", "desc": ""}, {"field": "mRotationTarget", "typ": "float", "desc": ""}, {"field": "mLastPositionWhenHadPath", "typ": "vec2", "desc": ""}, {"field": "mHasLastPosition", "typ": "bool", "desc": ""}]}, {"name": "PhysicsBody2Component", "fields": [{"field": "mBodyId", "typ": "b2ObjectID", "desc": "this is mBody->GetBodyId() - not to be confused with uid, has to be tracked separately, since the mBody pointer is not unique"}, {"field": "linear_damping", "typ": "float", "desc": ""}, {"field": "angular_damping", "typ": "float", "desc": ""}, {"field": "allow_sleep", "typ": "bool", "desc": ""}, {"field": "fixed_rotation", "typ": "bool", "desc": ""}, {"field": "is_bullet", "typ": "bool", "desc": ""}, {"field": "is_static", "typ": "bool", "desc": ""}, {"field": "buoyancy", "typ": "float", "desc": ""}, {"field": "hax_fix_going_through_ground", "typ": "bool", "desc": "if 1, will lift the body upwards if it is inside ground"}, {"field": "hax_fix_going_through_sand", "typ": "bool", "desc": "hax_fix_going_through_ground has to be set, if set will lift the body upwards if it is inside sand"}, {"field": "hax_wait_till_pixel_scenes_loaded", "typ": "bool", "desc": ""}, {"field": "go_through_sand", "typ": "bool", "desc": "if 1, will go through sand PhysicsBridge::mGoThroughSand = 1"}, {"field": "auto_clean", "typ": "bool", "desc": "if 1, the simulation might destroy this body if it's hidden under sand. Problematic if you have a small piece with joint attached to something like the wheels of minecart. Set to 0 in cases like that"}, {"field": "force_add_update_areas", "typ": "bool", "desc": "if 1, we will mark our predicted aabb as a box2d update area."}, {"field": "update_entity_transform", "typ": "bool", "desc": ""}, {"field": "kill_entity_if_body_destroyed", "typ": "bool", "desc": "if 1, will kill the entity when physics body is destroyed"}, {"field": "kill_entity_after_initialized", "typ": "bool", "desc": "if 1, will destroy the entity after initialization has been done based the entity's PhysicsBodyComponents and JointComponents"}, {"field": "manual_init", "typ": "bool", "desc": "if 1, initialization occurs only when done via for example lua component and Physic2InitFromComponents()"}, {"field": "destroy_body_if_entity_destroyed", "typ": "bool", "desc": "if 1, root body is destroyed if the entity is destroyed"}, {"field": "root_offset_x", "typ": "float", "desc": "TODO"}, {"field": "root_offset_y", "typ": "float", "desc": "TODO"}, {"field": "init_offset_x", "typ": "float", "desc": "TODO"}, {"field": "init_offset_y", "typ": "float", "desc": "TODO"}, {"field": "mActiveState", "typ": "bool", "desc": "private variable, please don't mess around with this"}, {"field": "mPixelCountOrig", "typ": "uint32", "desc": "the number of pixels the body had when it was originally created"}, {"field": "mLocalPosition", "typ": "vec2", "desc": "private variable, please don't mess around with this"}, {"field": "mBody", "typ": "b2Body*", "desc": ""}, {"field": "mInitialized", "typ": "bool", "desc": "private variable, please don't mess around with this"}, {"field": "mPixelCount", "typ": "uint32", "desc": "if set, tracks the number of csolidcells the body has"}, {"field": "mRefreshed", "typ": "bool", "desc": "this is sure the bodies are only parsed once"}]}, {"name": "PhysicsBodyCollisionDamageComponent", "fields": [{"field": "speed_threshold", "typ": "float", "desc": ""}, {"field": "damage_multiplier", "typ": "float", "desc": ""}]}, {"name": "PhysicsBodyComponent", "fields": [{"field": "is_external", "typ": "bool", "desc": "if mBody is set from outside, will ignore all the things"}, {"field": "hax_fix_going_through_ground", "typ": "bool", "desc": "if set will lift the body upwards if it is inside ground"}, {"field": "hax_fix_going_through_sand", "typ": "bool", "desc": "hax_fix_going_through_ground has to be set, if set will lift the body upwards if it is inside sand"}, {"field": "hax_wait_till_pixel_scenes_loaded", "typ": "bool", "desc": ""}, {"field": "uid", "typ": "int", "desc": "if the entity has multiple physics bodies and has specific shapes for those and possible joints, you should use this. 0 is default for shapes"}, {"field": "is_enabled", "typ": "bool", "desc": "Use this to kill the physics body of. if is_enabled is set to false, will destroy the physics body"}, {"field": "linear_damping", "typ": "float", "desc": ""}, {"field": "angular_damping", "typ": "float", "desc": ""}, {"field": "allow_sleep", "typ": "bool", "desc": ""}, {"field": "fixed_rotation", "typ": "bool", "desc": ""}, {"field": "buoyancy", "typ": "float", "desc": ""}, {"field": "gravity_scale_if_has_no_image_shapes", "typ": "float", "desc": ""}, {"field": "is_bullet", "typ": "bool", "desc": ""}, {"field": "is_static", "typ": "bool", "desc": ""}, {"field": "is_kinematic", "typ": "bool", "desc": ""}, {"field": "is_character", "typ": "bool", "desc": "if it is a character, then we need to few interesting things from time to time"}, {"field": "go_through_sand", "typ": "bool", "desc": "if set, will go through sand PhysicsBridge::mGoThroughSand = 1"}, {"field": "gridworld_box2d", "typ": "bool", "desc": "default is 1. You should only change this if you know the body isn't going to touch gridworld"}, {"field": "auto_clean", "typ": "bool", "desc": "if set, the simulation might destroy this body if it's hidden under sand. Problematic if you have a small piece with joint attached to something like the wheels of minecart. Set to 0 in cases like that"}, {"field": "on_death_leave_physics_body", "typ": "bool", "desc": "if set, will leave the b2body into the world, even if the entity is killed"}, {"field": "on_death_really_leave_body", "typ": "bool", "desc": "camera bound... god damn... we need something special when we want to leave the body"}, {"field": "update_entity_transform", "typ": "bool", "desc": "WARNING! Don't touch this unless you know what you're doing. If false, doesn't update the entitys transform to match the physics body. This is used with multi body entities, to use the correct body to update the entity, e.g. minecart"}, {"field": "force_add_update_areas", "typ": "bool", "desc": "if 1, we will mark our predicted aabb as a box2d update area."}, {"field": "kills_entity", "typ": "bool", "desc": "if set, will kill the entity when physics body is destroyed"}, {"field": "projectiles_rotate_toward_velocity", "typ": "bool", "desc": "for physics projectiles, if true will initially rotate the body based on the velocity"}, {"field": "randomize_init_velocity", "typ": "bool", "desc": "randomizes the init velocity"}, {"field": "mActiveState", "typ": "bool", "desc": "private variable, please don't mess around with this"}, {"field": "initial_velocity", "typ": "vec2", "desc": "if you want a velocity at the start, set it here"}, {"field": "mBody", "typ": "b2Body*", "desc": ""}, {"field": "mBodyId", "typ": "b2ObjectID", "desc": "this is mBody->GetBodyId() - not to be confused with uid shit, has to be tracked separately, since the mBody pointer is not unique"}, {"field": "mPixelCount", "typ": "int", "desc": "if set, tracks the number of csolidcells the body has"}, {"field": "mLocalPosition", "typ": "b2Vec2", "desc": ""}, {"field": "mRefreshed", "typ": "bool", "desc": "this is sure the bodies are only parsed once"}]}, {"name": "PhysicsImageShapeComponent", "fields": [{"field": "is_root", "typ": "bool", "desc": "if 1, PhysicsBody2Component will use this to figure out where the entity is located"}, {"field": "body_id", "typ": "int", "desc": "used to figure out which bodies are attached to each other when creating joints"}, {"field": "use_sprite", "typ": "bool", "desc": "will try to find the SpriteComponent and use that"}, {"field": "is_circle", "typ": "bool", "desc": "tries to fit this into a circle, looks at bounding box of the pixels and sets the circle to the center of that with radius being the line from there to a straight edge"}, {"field": "centered", "typ": "bool", "desc": "if this is true, moves offset to be in the center of the image, overwrites the offset_x, offset_y"}, {"field": "offset_x", "typ": "float", "desc": "offset x in pixels"}, {"field": "offset_y", "typ": "float", "desc": "offset y in pixels"}, {"field": "z", "typ": "float", "desc": "offset in the z direction"}, {"field": "image_file", "typ": "std::string", "desc": "the png file from which the body is created from"}, {"field": "material", "typ": "int", "desc": "the material from which the body is created"}, {"field": "mBody", "typ": "b2Body*", "desc": "used in joint creation phase"}]}, {"name": "PhysicsJoint2Component", "fields": [{"field": "joint_id", "typ": "uint16", "desc": "Use this to create a relation between PhysicsJointMutator and a joint. The PhysicsJointMutator must exist when the physics objects are initialized for the first time. This id should be unique inside one entity. Defaults to 0"}, {"field": "break_force", "typ": "float", "desc": "if > 0, will break if theres a force too strong."}, {"field": "break_distance", "typ": "float", "desc": "if > 0, will break if the anchors on the bodies get further than this."}, {"field": "break_on_body_modified", "typ": "bool", "desc": "if > 1, will break if an attached body is modified"}, {"field": "break_on_shear_angle_deg", "typ": "float", "desc": "if > 0, will break if the angle between the linked bodies becomes greater than this"}, {"field": "body1_id", "typ": "int", "desc": ""}, {"field": "body2_id", "typ": "int", "desc": ""}, {"field": "offset_x", "typ": "float", "desc": ""}, {"field": "offset_y", "typ": "float", "desc": ""}, {"field": "ray_x", "typ": "float", "desc": ""}, {"field": "ray_y", "typ": "float", "desc": ""}, {"field": "surface_attachment_offset_x", "typ": "float", "desc": ""}, {"field": "surface_attachment_offset_y", "typ": "float", "desc": ""}, {"field": "type", "typ": "JOINT_TYPE::Enum", "desc": "Enum - REVOLUTE_JOINT, WELD_JOINT, REVOLUTE_JOINT_ATTACH_TO_NEARBY_SURFACE or WELD_JOINT_ATTACH_TO_NEARBY_SURFACE"}]}, {"name": "PhysicsJoint2MutatorComponent", "fields": [{"field": "joint_id", "typ": "uint16", "desc": "Use this to create a relation between PhysicsJointMutator and a joint created by PhysicsJoint2Component. The PhysicsJoint2Mutator must exist when the physics objects are initialized for the first time."}, {"field": "destroy", "typ": "bool", "desc": "if 1, the joint will break and this component will be destroyed."}, {"field": "motor_speed", "typ": "float", "desc": "if != 0 and this is linked to a revolute joint, the joint motor will be enabled at this speed"}, {"field": "motor_max_torque", "typ": "float", "desc": "max torque for motor"}, {"field": "mBox2DJointId", "typ": "uint64", "desc": "Private, don't touch this! Stores the joint's id in the physics engine."}, {"field": "mPreviousMotorSpeed", "typ": "float", "desc": ""}, {"field": "mPreviousMotorMaxTorque", "typ": "float", "desc": ""}]}, {"name": "PhysicsJointComponent", "fields": [{"field": "nail_to_wall", "typ": "bool", "desc": ""}, {"field": "grid_joint", "typ": "bool", "desc": "if 1, will do a grid joint that works correctly with a body when it is destroyed / chipped away"}, {"field": "breakable", "typ": "bool", "desc": "if 1, will break if theres a force too strong"}, {"field": "body1_id", "typ": "int", "desc": ""}, {"field": "body2_id", "typ": "int", "desc": ""}, {"field": "pos_x", "typ": "float", "desc": ""}, {"field": "pos_y", "typ": "float", "desc": ""}, {"field": "delta_x", "typ": "float", "desc": "For mouse joint only ... moves the mouse joint by *dt "}, {"field": "delta_y", "typ": "float", "desc": "For mouse joint only ... moves the mouse joint by *dt "}, {"field": "mMotorEnabled", "typ": "bool", "desc": "enable motor, by setting this to true"}, {"field": "mMotorSpeed", "typ": "float", "desc": "if enabled this gets set to speed"}, {"field": "mMaxMotorTorque", "typ": "float", "desc": "max torque for motor"}, {"field": "type", "typ": "JOINT_TYPE::Enum", "desc": "Enum - JOINT_TYPE"}, {"field": "mJoint", "typ": "b2Joint*", "desc": ""}]}, {"name": "PhysicsKeepInWorldComponent", "fields": [{"field": "check_whole_aabb", "typ": "bool", "desc": "All that is needed is to include one of the components with PhysicsBodyComponent or PhysicsBody2Component and it will be frozen when it hits outer edges of the world. NOTE! This will override the auto_clean variable, auto_clean will be set to false. If this is true, will check all the 4 corners of the bounding box"}, {"field": "predict_aabb", "typ": "bool", "desc": "Will add the velocity * 1.5 to the aabb to predict where the body will end up at. This will greatly help keep the body inside simulated world."}, {"field": "keep_at_last_valid_pos", "typ": "bool", "desc": "Will try to keep the object at the latest valid position"}, {"field": "mExPosition", "typ": "vec2", "desc": ""}, {"field": "mExRotation", "typ": "float", "desc": ""}]}, {"name": "PhysicsPickUpComponent", "fields": [{"field": "pick_up_strength", "typ": "float", "desc": ""}, {"field": "transform", "typ": "types::xform", "desc": ""}, {"field": "original_left_joint_pos", "typ": "vec2", "desc": ""}, {"field": "original_right_joint_pos", "typ": "vec2", "desc": ""}, {"field": "isBroken", "typ": "bool", "desc": ""}, {"field": "leftJointPos", "typ": "vec2", "desc": ""}, {"field": "rightJointPos", "typ": "vec2", "desc": ""}, {"field": "leftJoint", "typ": "b2WeldJoint*", "desc": ""}, {"field": "rightJoint", "typ": "b2WeldJoint*", "desc": ""}]}, {"name": "PhysicsRagdollComponent", "fields": [{"field": "filename", "typ": "std::string", "desc": "file that should include just a list of other files, that have all the parts"}, {"field": "filenames", "typ": "std::string", "desc": "a list of body parts as png images, separate the files by ','. e.g. 'data/temp/ragdoll/leg.png, data/temp/ragdoll/head.png,...'"}, {"field": "offset_x", "typ": "float", "desc": "offset of where the ragdoll will be created"}, {"field": "offset_y", "typ": "float", "desc": "offset of where the ragdoll will be created"}, {"field": "bodies", "typ": "std::vector*", "desc": ""}]}, {"name": "PhysicsShapeComponent", "fields": [{"field": "recreate", "typ": "bool", "desc": ""}, {"field": "is_circle", "typ": "bool", "desc": ""}, {"field": "is_box", "typ": "bool", "desc": ""}, {"field": "is_capsule", "typ": "bool", "desc": ""}, {"field": "is_based_on_sprite", "typ": "bool", "desc": "if set, will use sprite component to figure out a box that fits this"}, {"field": "friction", "typ": "float", "desc": ""}, {"field": "restitution", "typ": "float", "desc": ""}, {"field": "density", "typ": "float", "desc": ""}, {"field": "local_position_x", "typ": "float", "desc": ""}, {"field": "local_position_y", "typ": "float", "desc": ""}, {"field": "radius_x", "typ": "float", "desc": ""}, {"field": "radius_y", "typ": "float", "desc": ""}, {"field": "capsule_x_percent", "typ": "float", "desc": ""}, {"field": "capsule_y_percent", "typ": "float", "desc": ""}, {"field": "material", "typ": "int", "desc": "the material to use for collision audio"}]}, {"name": "PhysicsThrowableComponent", "fields": [{"field": "throw_force_coeff", "typ": "float", "desc": ""}, {"field": "max_throw_speed", "typ": "float", "desc": ""}, {"field": "min_torque", "typ": "float", "desc": ""}, {"field": "max_torque", "typ": "float", "desc": ""}, {"field": "tip_check_offset_min", "typ": "float", "desc": ""}, {"field": "tip_check_offset_max", "typ": "float", "desc": ""}, {"field": "tip_check_random_rotation_deg", "typ": "float", "desc": ""}, {"field": "attach_min_speed", "typ": "float", "desc": ""}, {"field": "attach_to_surfaces_knife_style", "typ": "bool", "desc": ""}, {"field": "hp", "typ": "int", "desc": "WIP WIP"}, {"field": "mHasJoint", "typ": "bool", "desc": ""}]}, {"name": "PixelSceneComponent", "fields": [{"field": "pixel_scene", "typ": "std::string", "desc": "loads this pixel scene file"}, {"field": "pixel_scene_visual", "typ": "std::string", "desc": "this is the colors that get used for the pixels, if empty will use material colors"}, {"field": "pixel_scene_background", "typ": "std::string", "desc": "this is the background file that gets loaded, if empty won't do anything"}, {"field": "background_z_index", "typ": "int", "desc": "the standard z_index of pixel scene backgrounds"}, {"field": "offset_x", "typ": "float", "desc": "how much off from the entity x,y will this be. Top left corner is where it loads the pixel scene"}, {"field": "offset_y", "typ": "float", "desc": ""}, {"field": "skip_biome_checks", "typ": "bool", "desc": "biome check is on by default - it will check that pixel scene is loaded so that every corner is in the same biome"}, {"field": "skip_edge_textures", "typ": "bool", "desc": "if on - won't do the edge textures for the pixel scene"}]}, {"name": "PixelSpriteComponent", "fields": [{"field": "image_file", "typ": "std::string", "desc": "loads pixelsprite based on this file"}, {"field": "anchor_x", "typ": "int", "desc": "the anchor and center_offset"}, {"field": "anchor_y", "typ": "int", "desc": "the anchor and center_offset"}, {"field": "material", "typ": "std::string", "desc": "what's the material that things are made out of, TODO - change this into MetaCustom"}, {"field": "diggable", "typ": "bool", "desc": "if 1, this can be broken with digger"}, {"field": "clean_overlapping_pixels", "typ": "bool", "desc": "cleans up the pixels that are ovelapping in the world"}, {"field": "kill_when_sprite_dies", "typ": "bool", "desc": "kills the entity, if the pixel sprite is dead (empty)"}, {"field": "create_box2d_bodies", "typ": "bool", "desc": "if true, will create new pixel sprites with box2d bodies, instead of gridworld cells"}, {"field": "mPixelSprite", "typ": "PixelSprite*", "desc": ""}]}, {"name": "PlatformShooterPlayerComponent", "fields": [{"field": "aiming_reticle_distance_from_character", "typ": "float", "desc": ""}, {"field": "camera_max_distance_from_character", "typ": "float", "desc": ""}, {"field": "alcohol_drunken_speed", "typ": "float", "desc": ""}, {"field": "blood_fungi_drunken_speed", "typ": "float", "desc": ""}, {"field": "blood_worm_drunken_speed", "typ": "float", "desc": ""}, {"field": "eating_cells_per_frame", "typ": "int", "desc": ""}, {"field": "eating_probability", "typ": "int", "desc": ""}, {"field": "eating_delay_frames", "typ": "int", "desc": ""}, {"field": "stoned_speed", "typ": "float", "desc": ""}, {"field": "center_camera_on_this_entity", "typ": "bool", "desc": ""}, {"field": "move_camera_with_aim", "typ": "bool", "desc": "if true, moves camera with the aim."}, {"field": "eating_area_min", "typ": "ivec2", "desc": ""}, {"field": "eating_area_max", "typ": "ivec2", "desc": ""}, {"field": "mSmoothedCameraPosition", "typ": "vec2", "desc": ""}, {"field": "mSmoothedAimingVector", "typ": "vec2", "desc": ""}, {"field": "mCameraRecoil", "typ": "float", "desc": ""}, {"field": "mCameraRecoilTarget", "typ": "float", "desc": ""}, {"field": "mCrouching", "typ": "bool", "desc": ""}, {"field": "mCameraDistanceLerped", "typ": "float", "desc": ""}, {"field": "mRequireTriggerPull", "typ": "bool", "desc": ""}, {"field": "mWarpDelay", "typ": "int", "desc": ""}, {"field": "mItemTemporarilyHidden", "typ": "int", "desc": ""}, {"field": "mDesiredCameraPos", "typ": "vec2", "desc": ""}, {"field": "mHasGamepadControlsPrev", "typ": "bool", "desc": ""}, {"field": "mForceFireOnNextUpdate", "typ": "bool", "desc": ""}, {"field": "mFastMovementParticlesAlphaSmoothed", "typ": "float", "desc": ""}, {"field": "mTeleBoltFramesDuringLastSecond", "typ": "uint64", "desc": ""}, {"field": "mCamCorrectionTeleSmoothed", "typ": "float", "desc": ""}, {"field": "mCamCorrectionGainSmoothed", "typ": "vec2", "desc": ""}, {"field": "mCameraErrorPrev", "typ": "Vec2ArrayInline", "desc": ""}, {"field": "mCamErrorAveraged", "typ": "vec2", "desc": ""}, {"field": "mCamMovingFastPrev", "typ": "bool", "desc": ""}, {"field": "mCamFrameStartedMovingFast", "typ": "int", "desc": ""}, {"field": "mCamFrameLastMovingFastExplosion", "typ": "int", "desc": ""}, {"field": "mCessationDo", "typ": "bool", "desc": ""}, {"field": "mCessationLifetime", "typ": "int", "desc": ""}]}, {"name": "PlayerCollisionComponent", "fields": [{"field": "getting_crushed_threshold", "typ": "int", "desc": ""}, {"field": "moving_up_before_getting_crushed_threshold", "typ": "int", "desc": ""}, {"field": "getting_crushed_counter", "typ": "int", "desc": "1.12.2018 - Is this still used?"}, {"field": "stuck_in_ground_counter", "typ": "int", "desc": "used this mostly for player to figure out if it's stuck in ground"}, {"field": "DEBUG_stuck_in_static_ground", "typ": "int", "desc": "used to report error + also to free the player in case something horrible has gone wrong"}, {"field": "mCollidedHorizontally", "typ": "bool", "desc": ""}, {"field": "mPhysicsCollisionHax", "typ": "b2Body*", "desc": "hax"}]}, {"name": "PlayerStatsComponent", "fields": [{"field": "lives", "typ": "int", "desc": ""}, {"field": "max_hp", "typ": "float", "desc": ""}, {"field": "speed", "typ": "float", "desc": ""}]}, {"name": "PositionSeedComponent", "fields": [{"field": "pos_x", "typ": "float", "desc": ""}, {"field": "pos_y", "typ": "float", "desc": ""}]}, {"name": "PotionComponent", "fields": [{"field": "spray_velocity_coeff", "typ": "float", "desc": ""}, {"field": "spray_velocity_normalized_min", "typ": "float", "desc": ""}, {"field": "body_colored", "typ": "bool", "desc": ""}, {"field": "throw_bunch", "typ": "bool", "desc": ""}, {"field": "throw_how_many", "typ": "int", "desc": ""}, {"field": "dont_spray_static_materials", "typ": "bool", "desc": "NOTE( Petri ): 15.8.2023 - if this is set to true, will only spray dynamic materials, that dont cause bugs (i.e. will not spray hard rock, box2d materials)"}, {"field": "dont_spray_just_leak_gas_materials", "typ": "bool", "desc": "NOTE( Petri ): 15.8.2023 - if this is set to true, will only leak gas materials instead of 'spraying' them."}, {"field": "never_color", "typ": "bool", "desc": "Petri: body_colored didn't seem to work, so I added never_color. It can be set to true if you never want the potion to be colored"}, {"field": "custom_color_material", "typ": "int", "desc": "if set, will always use the color from this material"}]}, {"name": "PressurePlateComponent", "fields": [{"field": "check_every_x_frames", "typ": "int", "desc": "how often do we check the world"}, {"field": "state", "typ": "int", "desc": "0 is up, 1 is down"}, {"field": "material_percent", "typ": "float", "desc": "how much material should there be in the aabbs that we go down "}, {"field": "aabb_min", "typ": "vec2", "desc": ""}, {"field": "aabb_max", "typ": "vec2", "desc": ""}, {"field": "mNextFrame", "typ": "int", "desc": ""}]}, {"name": "ProjectileComponent", "fields": [{"field": "lifetime", "typ": "int", "desc": "lifetime, -1 means it's endless, otherwise it's the frame count"}, {"field": "lifetime_randomness", "typ": "int", "desc": "final lifetime will be lifetime + random(-lifetime_randomness,lifetime_randomness)"}, {"field": "on_lifetime_out_explode", "typ": "bool", "desc": "when lifetime runs out, should we explode?"}, {"field": "collide_with_world", "typ": "bool", "desc": "true by default. Some projectiles you don't want to collide with the world, e.g. blackholes"}, {"field": "speed_min", "typ": "float", "desc": ""}, {"field": "speed_max", "typ": "float", "desc": ""}, {"field": "friction", "typ": "float", "desc": ""}, {"field": "direction_random_rad", "typ": "float", "desc": "when fired, randomizes the velocity -this, this"}, {"field": "direction_nonrandom_rad", "typ": "float", "desc": "when fired, multiplies this with projectile_i and adds it to direction"}, {"field": "lob_min", "typ": "float", "desc": ""}, {"field": "lob_max", "typ": "float", "desc": ""}, {"field": "camera_shake_when_shot", "typ": "float", "desc": ""}, {"field": "shoot_light_flash_radius", "typ": "float", "desc": ""}, {"field": "int", "typ": "unsigned", "desc": ""}, {"field": "int", "typ": "unsigned", "desc": ""}, {"field": "int", "typ": "unsigned", "desc": ""}, {"field": "create_shell_casing", "typ": "bool", "desc": "should we create shell casings?"}, {"field": "shell_casing_material", "typ": "std::string", "desc": "material of the shell casing"}, {"field": "muzzle_flash_file", "typ": "std::string", "desc": "this entity is created along with the projectile, oriented along the projectile's path"}, {"field": "bounces_left", "typ": "int", "desc": ""}, {"field": "bounce_energy", "typ": "float", "desc": "when bouncing, velocity is multiplied by this"}, {"field": "bounce_always", "typ": "bool", "desc": "if true, will do a fake bounce if can't do the proper bounce, but will always try to bounce"}, {"field": "bounce_at_any_angle", "typ": "bool", "desc": "if true, will bounce at any reflection angle"}, {"field": "attach_to_parent_trigger", "typ": "bool", "desc": "if true, will attach to the projectile entity that created this projectile via a trigger"}, {"field": "bounce_fx_file", "typ": "std::string", "desc": "this entity is created at the bounce position. it gets the bounce angle as rotation."}, {"field": "angular_velocity", "typ": "float", "desc": "this is only applied if velocity_sets_rotation == false"}, {"field": "velocity_sets_rotation", "typ": "bool", "desc": "whether we set the rotation based on velocity, as in spear or if we update the rotation with angular_velocity"}, {"field": "velocity_sets_scale", "typ": "bool", "desc": "if true, the sprite width is made equal to the distance traveled since last frame"}, {"field": "velocity_sets_scale_coeff", "typ": "float", "desc": "Larger value means velocity affects the scale more"}, {"field": "velocity_sets_y_flip", "typ": "bool", "desc": "if true, the sprite is flipped based on which side the projectile is currently traveling"}, {"field": "velocity_updates_animation", "typ": "float", "desc": "updates the animation based on far the sprite moved"}, {"field": "ground_penetration_coeff", "typ": "float", "desc": "if > 0, this, along with VelocityComponent.mass affects how far we penetrate in materials"}, {"field": "ground_penetration_max_durability_to_destroy", "typ": "int", "desc": "if 0, will not penetrate into materials with durability greater than this"}, {"field": "go_through_this_material", "typ": "std::string", "desc": "if set, we never collide with this material"}, {"field": "do_moveto_update", "typ": "bool", "desc": "this should probably be true, to get normal projectile behaviour, but you might want to disable this for some physics-based projectiles, like bombs"}, {"field": "on_death_duplicate_remaining", "typ": "int", "desc": "if greater than 0, the projectile creates two clones of itself on death. 'on_death_duplicate_remaining' on the clones is reduced by one"}, {"field": "on_death_gfx_leave_sprite", "typ": "bool", "desc": "if true, finds all the sprites and leaves as sand cells into the grid"}, {"field": "on_death_explode", "typ": "bool", "desc": "if true, does explosion with config_explosion"}, {"field": "on_death_emit_particle", "typ": "bool", "desc": "if true, emits on_death_emit_particle_type on death"}, {"field": "on_death_emit_particle_count", "typ": "int", "desc": "how many particles should we emit"}, {"field": "die_on_liquid_collision", "typ": "bool", "desc": "if true, dies on collision with liquids"}, {"field": "die_on_low_velocity", "typ": "bool", "desc": "if true, dies when speed goes below die_on_low_velocity_limit"}, {"field": "die_on_low_velocity_limit", "typ": "float", "desc": "please see die_on_low_velocity"}, {"field": "on_death_emit_particle_type", "typ": "std::string", "desc": ""}, {"field": "on_death_particle_check_concrete", "typ": "bool", "desc": "if you want it to stick as concrete, you should enable this"}, {"field": "ground_collision_fx", "typ": "bool", "desc": "if 1, spurt some particles when colliding with mortals"}, {"field": "explosion_dont_damage_shooter", "typ": "bool", "desc": "if true, explosion doesn't damage the entity who shot this"}, {"field": "on_death_item_pickable_radius", "typ": "float", "desc": "if > 0, makes items closer than this radius pickable on death"}, {"field": "penetrate_world", "typ": "bool", "desc": "if true, the projectile doesn't collide with ground, liquids, physical objects etc"}, {"field": "penetrate_world_velocity_coeff", "typ": "float", "desc": "if 'penetrate_world' is true, the projectile moves with a velocity multiplied by this value when inside world"}, {"field": "penetrate_entities", "typ": "bool", "desc": "if true, the projectile doesn't stop when it collides with entities. damages each entity only once"}, {"field": "on_collision_die", "typ": "bool", "desc": "if true, this is killed as soon as it hits the ground"}, {"field": "on_collision_remove_projectile", "typ": "bool", "desc": "if true, ProjectileComponent is removed from the entitiy"}, {"field": "on_collision_spawn_entity", "typ": "bool", "desc": "if true, spawns the spawn_entity"}, {"field": "spawn_entity", "typ": "std::string", "desc": "this is spawned if hit something an on_collision_spawn_entity = 1"}, {"field": "spawn_entity_is_projectile", "typ": "bool", "desc": "if true, will use ShootProjectile instead of LoadEntity()"}, {"field": "physics_impulse_coeff", "typ": "float", "desc": "projectile applies an impulse to physics bodies it hits. Impulse = physics_impulse_coeff * velocity"}, {"field": "damage_every_x_frames", "typ": "int", "desc": "if set != -1, will only do damage every x frames, used for fields and such, which would otherwise do damage every frame"}, {"field": "damage_scaled_by_speed", "typ": "bool", "desc": "if 1, damage is multiplied by (projectile speed / original projectile speed) ratio"}, {"field": "damage_scale_max_speed", "typ": "float", "desc": "if > 0 and damage_scaled_by_speed = 1, will use this instead of mInitialSpeed when calculating the damage"}, {"field": "collide_with_entities", "typ": "bool", "desc": "if 1, looks for entities with tag, collide_with_tag and collides with them, giving them damage"}, {"field": "collide_with_tag", "typ": "std::string", "desc": "default: mortal, if you needed can be changed to something more specific"}, {"field": "dont_collide_with_tag", "typ": "std::string", "desc": "if set will ignore entities with this tag"}, {"field": "collide_with_shooter_frames", "typ": "int", "desc": "remember friendly_fire 1, if -1 won't collide with shooter at all, otherwise uses the value as frame count and while it's running won't damage the shooter "}, {"field": "friendly_fire", "typ": "bool", "desc": "if true, will damage same herd id"}, {"field": "damage", "typ": "float", "desc": "how much Projectile damage does this do when it hits something"}, {"field": "knockback_force", "typ": "float", "desc": "How far do entities get thrown if a knockback occurs. final_knockback = ProjectileComponent.knockback_force * VelocityComponent.mVelocity * VelocityComponent.mass / who_we_hit.mass"}, {"field": "ragdoll_force_multiplier", "typ": "float", "desc": "velocity * ragdoll_force_multiplier is applied to any ragdolls that are created by entities killed by this"}, {"field": "hit_particle_force_multiplier", "typ": "float", "desc": "hit particle velocity = projectile_velocity * hit_particle_force_multiplier * some randomness"}, {"field": "blood_count_multiplier", "typ": "float", "desc": "how much blood does this projectile cause"}, {"field": "damage_game_effect_entities", "typ": "std::string", "desc": "a list of game_effects entities separated with ','. e.g. 'data/entities/misc/effect_electrocution.xml,data/entities/misc/effect_on_fire.xml' "}, {"field": "never_hit_player", "typ": "bool", "desc": "If 1, does not hit player no matter what herds this and player belong to"}, {"field": "collect_materials_to_shooter", "typ": "bool", "desc": "if 1, looks up the 'who_shot' entity and its MaterialInventoryComponent on destruction and updates it based on the cells destroyed on our explosion."}, {"field": "play_damage_sounds", "typ": "bool", "desc": ""}, {"field": "mLastFrameDamaged", "typ": "int", "desc": ""}, {"field": "config", "typ": "ConfigGunActionInfo", "desc": ""}, {"field": "config_explosion", "typ": "ConfigExplosion", "desc": "if we have explosion, it's the setup for it"}, {"field": "damage_by_type", "typ": "ConfigDamagesByType", "desc": "the amounts of different types of damage this does"}, {"field": "damage_critical", "typ": "ConfigDamageCritical", "desc": "config for critical hit"}, {"field": "projectile_type", "typ": "PROJECTILE_TYPE::Enum", "desc": ""}, {"field": "shell_casing_offset", "typ": "vec2", "desc": "where the shell casing will be created relative to projectile, y is flipped if projectile direction is to the left."}, {"field": "ragdoll_fx_on_collision", "typ": "RAGDOLL_FX::Enum", "desc": "if not NORMAL, do a special ragdoll"}, {"field": "mWhoShot", "typ": "EntityID", "desc": "entity (creature) that shot this"}, {"field": "mWhoShotEntityTypeID", "typ": "EntityTypeID", "desc": "used for stats"}, {"field": "mShooterHerdId", "typ": "int", "desc": "the herdid of mWhoShot, unless friendly fire"}, {"field": "mStartingLifetime", "typ": "int", "desc": ""}, {"field": "mEntityThatShot", "typ": "EntityID", "desc": "for triggers, if shot from a trigger this should point to the projectile entity that shot this. Otherwise this should be the same as mWhoShot. NOTE! Not really tested properly so might break."}, {"field": "mTriggers", "typ": "ProjectileTriggers", "desc": ""}, {"field": "mDamagedEntities", "typ": "VEC_ENTITY", "desc": ""}, {"field": "mInitialSpeed", "typ": "float", "desc": ""}]}, {"name": "RotateTowardsComponent", "fields": [{"field": "entity_with_tag", "typ": "std::string", "desc": "will rotate this entity towards the closest entity with tag"}]}, {"name": "SetLightAlphaFromVelocityComponent", "fields": [{"field": "max_velocity", "typ": "float", "desc": ""}, {"field": "mPrevPosition", "typ": "vec2", "desc": ""}]}, {"name": "SetStartVelocityComponent", "fields": [{"field": "velocity", "typ": "vec2", "desc": "This is added together with random velocity"}, {"field": "randomize_angle", "typ": "ValueRange", "desc": "Random angle min max range in radians, clockwise. 0.0 points directly rightward."}, {"field": "randomize_speed", "typ": "ValueRange", "desc": "Random speed min max range"}]}, {"name": "ShotEffectComponent", "fields": [{"field": "extra_modifier", "typ": "std::string", "desc": "name of modifier function executed per projectile from 'gun_extra_modifiers.lua'"}, {"field": "condition_effect", "typ": "GAME_EFFECT::Enum", "desc": "Shooting entity needs to have this 'GAME_EFFECT' for effects to apply. If both 'condition_effect' and 'condition_status' are set, they are combined with AND logic"}, {"field": "condition_status", "typ": "StatusEffectType", "desc": "Shooting entity needs to have this 'STATUS_EFFECT' for effects to apply"}]}, {"name": "SimplePhysicsComponent", "fields": [{"field": "can_go_up", "typ": "bool", "desc": "if set, will not try to move this upwards"}, {"field": "mOldPosition", "typ": "vec2", "desc": "used for box2d simple physics"}]}, {"name": "SineWaveComponent", "fields": [{"field": "sinewave_freq", "typ": "float", "desc": "sinewave_m * sinf( sinewave_freq * lifetime++)"}, {"field": "sinewave_m", "typ": "float", "desc": "sinewave_m * sinf( sinewave_freq * lifetime++)"}, {"field": "lifetime", "typ": "int", "desc": "-1 seems to fix some problems with this... sinewave_m * sinf( sinewave_freq * lifetime++)"}]}, {"name": "SpriteAnimatorComponent", "fields": [{"field": "target_sprite_comp_name", "typ": "std::string", "desc": ""}, {"field": "rotate_to_surface_normal", "typ": "bool", "desc": ""}, {"field": "mStates", "typ": "STACK_ANIMATIONSTATE", "desc": ""}, {"field": "mCachedTargetSpriteTag", "typ": "ComponentTags", "desc": ""}, {"field": "mSendOnFinishedMessageName", "typ": "std::string", "desc": ""}]}, {"name": "SpriteComponent", "fields": [{"field": "image_file", "typ": "std::string", "desc": ""}, {"field": "ui_is_parent", "typ": "bool", "desc": "Adds this to the GG.GetUISprite() as a child, instead of the mSpriteContainer"}, {"field": "is_text_sprite", "typ": "bool", "desc": "if you want to load a text sprite, set this to true and image_file to a font file"}, {"field": "offset_x", "typ": "float", "desc": ""}, {"field": "offset_y", "typ": "float", "desc": ""}, {"field": "alpha", "typ": "float", "desc": ""}, {"field": "visible", "typ": "bool", "desc": ""}, {"field": "emissive", "typ": "bool", "desc": ""}, {"field": "additive", "typ": "bool", "desc": ""}, {"field": "fog_of_war_hole", "typ": "bool", "desc": "if 1, the alpha channel of this texture punctures a hole in the fog of war"}, {"field": "smooth_filtering", "typ": "bool", "desc": ""}, {"field": "rect_animation", "typ": "std::string", "desc": ""}, {"field": "next_rect_animation", "typ": "std::string", "desc": ""}, {"field": "text", "typ": "std::string", "desc": ""}, {"field": "z_index", "typ": "float", "desc": "0 = world grid, -1 = enemies, -1.5 = items in world, player = 0.6"}, {"field": "update_transform", "typ": "bool", "desc": ""}, {"field": "update_transform_rotation", "typ": "bool", "desc": ""}, {"field": "kill_entity_after_finished", "typ": "bool", "desc": ""}, {"field": "has_special_scale", "typ": "bool", "desc": "if this is set, sets special_scale_x and _y to scale"}, {"field": "special_scale_x", "typ": "float", "desc": "this overrides the scale of the entity, if has_special_scale"}, {"field": "special_scale_y", "typ": "float", "desc": "this overrides the scale of the entity, if has_special_scale"}, {"field": "never_ragdollify_on_death", "typ": "bool", "desc": ""}, {"field": "transform_offset", "typ": "vec2", "desc": ""}, {"field": "offset_animator_offset", "typ": "vec2", "desc": "used by SpriteOffsetAnimator"}, {"field": "mSprite", "typ": "as::Sprite*", "desc": ""}, {"field": "mRenderList", "typ": "SpriteRenderList*", "desc": ""}, {"field": "mRenderListHandle", "typ": "int32", "desc": ""}]}, {"name": "SpriteOffsetAnimatorComponent", "fields": [{"field": "x_amount", "typ": "float", "desc": ""}, {"field": "x_speed", "typ": "float", "desc": ""}, {"field": "y_amount", "typ": "float", "desc": ""}, {"field": "y_speed", "typ": "float", "desc": ""}, {"field": "sprite_id", "typ": "int", "desc": ""}, {"field": "x_phase", "typ": "float", "desc": ""}, {"field": "x_phase_offset", "typ": "float", "desc": ""}]}, {"name": "SpriteParticleEmitterComponent", "fields": [{"field": "sprite_file", "typ": "std::string", "desc": "filepath to the sprite(s), supports the $[0-3] syntax"}, {"field": "sprite_centered", "typ": "bool", "desc": "sets the offset to the center of the image"}, {"field": "sprite_random_rotation", "typ": "bool", "desc": "rotates the sprite randomly in 90 degree angles"}, {"field": "render_back", "typ": "bool", "desc": "if true, will set this particle to be behind entities (won't emit light)"}, {"field": "delay", "typ": "float", "desc": "delay in seconds..."}, {"field": "lifetime", "typ": "float", "desc": "lifetime in seconds..."}, {"field": "additive", "typ": "bool", "desc": "if 1, the sprites will be rendered using additive blending"}, {"field": "emissive", "typ": "bool", "desc": "if 1, the sprites will be rendered onto the emissive render target"}, {"field": "velocity_slowdown", "typ": "float", "desc": "what percent of the velocity is slowed by *dt"}, {"field": "rotation", "typ": "float", "desc": "original rotation in rads"}, {"field": "angular_velocity", "typ": "float", "desc": "how much rotation there is in a second"}, {"field": "use_velocity_as_rotation", "typ": "bool", "desc": "do we rotate the sprite based on the velocity"}, {"field": "use_rotation_from_velocity_component", "typ": "bool", "desc": "if set, will set the initial rotation based on the velocity component's velocity"}, {"field": "use_rotation_from_entity", "typ": "bool", "desc": "if set, will 'inherit' rotation from the entity"}, {"field": "entity_velocity_multiplier", "typ": "float", "desc": "0 = doesn't use the velocity from spawning entity at all, 1 = uses all"}, {"field": "z_index", "typ": "float", "desc": "Depth of created particles"}, {"field": "randomize_position_inside_hitbox", "typ": "bool", "desc": "if set, will randomize position inside the hitbox aabb"}, {"field": "velocity_always_away_from_center", "typ": "bool", "desc": "if set, will make the velocity's rotation always away from center of randomized aabb"}, {"field": "camera_bound", "typ": "bool", "desc": "if true, will be culled if not near the camera"}, {"field": "camera_distance", "typ": "float", "desc": "if the distance from camera (edges) is higher than this, this will be culled"}, {"field": "is_emitting", "typ": "bool", "desc": "disable this from emitting..."}, {"field": "count_min", "typ": "int", "desc": "how many particles do we spawn at one time"}, {"field": "count_max", "typ": "int", "desc": "how many particles do we spawn at one time"}, {"field": "emission_interval_min_frames", "typ": "int", "desc": "how often do we emit particles"}, {"field": "emission_interval_max_frames", "typ": "int", "desc": "how often do we emit particles"}, {"field": "entity_file", "typ": "std::string", "desc": "if set, this entity is loaded to the emission position by the emitter when it emits"}, {"field": "color", "typ": "types::fcolor", "desc": "original color"}, {"field": "color_change", "typ": "types::fcolor", "desc": "how much the color changes in a second"}, {"field": "velocity", "typ": "vec2", "desc": "original velocity"}, {"field": "gravity", "typ": "vec2", "desc": "gravity"}, {"field": "scale", "typ": "vec2", "desc": "original scale"}, {"field": "scale_velocity", "typ": "vec2", "desc": "scale velocity per second"}, {"field": "randomize_lifetime", "typ": "ValueRange", "desc": "this is added to the lifetime"}, {"field": "randomize_position", "typ": "types::aabb", "desc": "random offset for pos"}, {"field": "randomize_velocity", "typ": "types::aabb", "desc": "add this randomized velocity inside this o the velocity"}, {"field": "randomize_scale", "typ": "types::aabb", "desc": "add this randomized vector2 to scale"}, {"field": "randomize_rotation", "typ": "ValueRange", "desc": "this is added to the rotation "}, {"field": "randomize_angular_velocity", "typ": "ValueRange", "desc": "this is added to angular_velocity"}, {"field": "randomize_alpha", "typ": "ValueRange", "desc": "this is added to the alpha"}, {"field": "randomize_animation_speed_coeff", "typ": "ValueRange", "desc": "if set, animation speed is multiplied by a random value inside this range"}, {"field": "expand_randomize_position", "typ": "vec2", "desc": "will add dt*this to randomize_position_aabb every frame"}, {"field": "mNextEmitFrame", "typ": "int", "desc": ""}]}, {"name": "SpriteStainsComponent", "fields": [{"field": "sprite_id", "typ": "int", "desc": "which sprite (in the order in which they appear in the entity) are we going to stain?"}, {"field": "fade_stains_towards_srite_top", "typ": "bool", "desc": "if 1, shades get less opaque near the top of the sprite"}, {"field": "stain_shaken_drop_chance_multiplier", "typ": "LensValue", "desc": "how quickly stains are dropped relative to normal drop speed"}, {"field": "mData", "typ": "SpriteStains*", "desc": ""}, {"field": "mTextureHandle", "typ": "VirtualTextureHandle", "desc": ""}, {"field": "mState", "typ": "SpriteStainsState", "desc": ""}]}, {"name": "StatusEffectDataComponent", "fields": [{"field": "stain_effects", "typ": "VECTOR_FLOAT", "desc": ""}, {"field": "stain_effect_cooldowns", "typ": "VECTOR_INT32", "desc": ""}, {"field": "effects_previous", "typ": "VECTOR_FLOAT", "desc": ""}, {"field": "ingestion_effects", "typ": "VECTOR_FLOAT", "desc": ""}, {"field": "ingestion_effect_causes", "typ": "VEC_OF_MATERIALS", "desc": ""}, {"field": "ingestion_effect_causes_many", "typ": "VECTOR_INT32", "desc": ""}, {"field": "mLastAttackingPlayerFrame", "typ": "int", "desc": ""}, {"field": "mStainEffectsSmoothedForUI", "typ": "VECTOR_FLOAT", "desc": ""}, {"field": "mHasChildIconsCached", "typ": "bool", "desc": ""}]}, {"name": "StreamingKeepAliveComponent", "fields": [{"field": "TEMP_TEMPY", "typ": "float", "desc": ""}, {"field": "TEMP_TEMP_TEMP", "typ": "float", "desc": ""}]}, {"name": "TelekinesisComponent", "fields": [{"field": "min_size", "typ": "uint32", "desc": "Minimum size of physics body that can be grabbed, in cells/pixels"}, {"field": "max_size", "typ": "uint32", "desc": "Maximum size of physics body that can be grabbed, in cells/pixels"}, {"field": "radius", "typ": "float", "desc": "Maximum object search distance"}, {"field": "throw_speed", "typ": "float", "desc": "Affects object speed when it is thrown"}, {"field": "target_distance", "typ": "float", "desc": "Affects how far objects float from owner when held. Object size will also affect the floating distance."}, {"field": "kick_to_use", "typ": "bool", "desc": "If 1, telekinesis interaction will occur when kick input is detected in root entity's ControlsComponent"}, {"field": "mState", "typ": "int32", "desc": ""}, {"field": "mBodyID", "typ": "uint64", "desc": ""}, {"field": "mStartBodyMaxExtent", "typ": "float", "desc": ""}, {"field": "mStartAimAngle", "typ": "float", "desc": ""}, {"field": "mStartBodyAngle", "typ": "float", "desc": ""}, {"field": "mStartBodyDistance", "typ": "float", "desc": ""}, {"field": "mStartTime", "typ": "float", "desc": ""}, {"field": "mMinBodyDistance", "typ": "float", "desc": ""}, {"field": "mInteract", "typ": "bool", "desc": "If set to true, telekinesis interaction will occur. Will automatically turn to false at the end of component update."}]}, {"name": "TeleportComponent", "fields": [{"field": "target_x_is_absolute_position", "typ": "bool", "desc": "If set, target position x is in world coordinates, otherwise it's an offset"}, {"field": "target_y_is_absolute_position", "typ": "bool", "desc": "If set, target position y is in world coordinates, otherwise it's an offset"}, {"field": "source_particle_fx_file", "typ": "std::string", "desc": "This entity is loaded at the source position when teleportation occurs"}, {"field": "target_particle_fx_file", "typ": "std::string", "desc": "This entity is loaded at the target position when teleportation occurs"}, {"field": "load_collapse_entity", "typ": "bool", "desc": "if we don't want things to collapse after the teleport"}, {"field": "target", "typ": "vec2", "desc": "Where should we teleport"}, {"field": "safety_counter", "typ": "int", "desc": "used to keep track that we're not stuck in waiting for a pixel scene to load, that is not going to be loaded"}, {"field": "teleported_entities", "typ": "ENTITY_VEC", "desc": ""}, {"field": "source_location_camera_aabb", "typ": "types::aabb", "desc": ""}]}, {"name": "TeleportProjectileComponent", "fields": [{"field": "min_distance_from_wall", "typ": "float", "desc": ""}, {"field": "actionable_lifetime", "typ": "int", "desc": ""}, {"field": "reset_shooter_y_vel", "typ": "bool", "desc": "If 1, will set shooter y velocity to 0 on teleport"}, {"field": "mWhoShot", "typ": "EntityID", "desc": ""}]}, {"name": "TextLogComponent", "fields": [{"field": "key", "typ": "std::string", "desc": ""}, {"field": "image_filename", "typ": "std::string", "desc": ""}, {"field": "mCachedName", "typ": "std::string", "desc": ""}]}, {"name": "TorchComponent", "fields": [{"field": "probability_of_ignition_attempt", "typ": "int", "desc": "how likely are we to ignite colliding cells"}, {"field": "suffocation_check_offset_y", "typ": "float", "desc": "check offset in world coordinates from our position"}, {"field": "frames_suffocated_to_extinguish", "typ": "int", "desc": "how many frames the torch needs to be suffocated before it stops emitting fire"}, {"field": "extinguishable", "typ": "bool", "desc": "if 1, the torch needs to be re-ignited in case it is turned off"}, {"field": "fire_audio_weight", "typ": "float", "desc": "how loud is the sound of our fire? 0 = no sound"}, {"field": "mFlickerOffset", "typ": "float", "desc": ""}, {"field": "mFramesSuffocated", "typ": "int", "desc": ""}, {"field": "mIsOn", "typ": "bool", "desc": ""}, {"field": "mFireIsBurningPrev", "typ": "bool", "desc": ""}]}, {"name": "UIIconComponent", "fields": [{"field": "icon_sprite_file", "typ": "std::string", "desc": ""}, {"field": "name", "typ": "std::string", "desc": ""}, {"field": "description", "typ": "std::string", "desc": ""}, {"field": "display_above_head", "typ": "bool", "desc": ""}, {"field": "display_in_hud", "typ": "bool", "desc": ""}, {"field": "is_perk", "typ": "bool", "desc": ""}]}, {"name": "UIInfoComponent", "fields": [{"field": "name", "typ": "std::string", "desc": ""}]}, {"name": "VariableStorageComponent", "fields": [{"field": "name", "typ": "std::string", "desc": ""}, {"field": "value_string", "typ": "std::string", "desc": ""}, {"field": "value_int", "typ": "int", "desc": ""}, {"field": "value_bool", "typ": "bool", "desc": ""}, {"field": "value_float", "typ": "float", "desc": ""}]}, {"name": "VelocityComponent", "fields": [{"field": "gravity_x", "typ": "float", "desc": ""}, {"field": "gravity_y", "typ": "float", "desc": ""}, {"field": "mass", "typ": "float", "desc": ""}, {"field": "air_friction", "typ": "float", "desc": ""}, {"field": "terminal_velocity", "typ": "float", "desc": ""}, {"field": "apply_terminal_velocity", "typ": "bool", "desc": ""}, {"field": "updates_velocity", "typ": "bool", "desc": ""}, {"field": "displace_liquid", "typ": "bool", "desc": ""}, {"field": "affect_physics_bodies", "typ": "bool", "desc": "if true, will move the physics body by the difference of mVelocity to the previous frame"}, {"field": "limit_to_max_velocity", "typ": "bool", "desc": "if true will limit the velocity to 61440. You can turn this off, but it's not recommended, since there are some nasty bugs that can happen with extremely high velocities."}, {"field": "liquid_death_threshold", "typ": "int", "desc": "if > 0, entity will die if liquid hit count is greater than this."}, {"field": "liquid_drag", "typ": "float", "desc": "1 = slows down in liquid, 0 = doesn't slow down at all"}, {"field": "mVelocity", "typ": "vec2", "desc": ""}, {"field": "mPrevVelocity", "typ": "vec2", "desc": "used to update physics bodies"}, {"field": "mLatestLiquidHitCount", "typ": "int", "desc": ""}, {"field": "mAverageLiquidHitCount", "typ": "int", "desc": ""}, {"field": "mPrevPosition", "typ": "ivec2", "desc": ""}]}, {"name": "VerletPhysicsComponent", "fields": [{"field": "num_points", "typ": "int", "desc": ""}, {"field": "num_links", "typ": "int", "desc": ""}, {"field": "width", "typ": "int", "desc": ""}, {"field": "resting_distance", "typ": "float", "desc": ""}, {"field": "mass_min", "typ": "float", "desc": ""}, {"field": "mass_max", "typ": "float", "desc": ""}, {"field": "stiffness", "typ": "float", "desc": ""}, {"field": "velocity_dampening", "typ": "float", "desc": ""}, {"field": "liquid_damping", "typ": "float", "desc": "how much we dampen when in liquid"}, {"field": "gets_entity_velocity_coeff", "typ": "float", "desc": ""}, {"field": "collide_with_cells", "typ": "bool", "desc": ""}, {"field": "simulate_gravity", "typ": "bool", "desc": ""}, {"field": "simulate_wind", "typ": "bool", "desc": ""}, {"field": "wind_change_speed", "typ": "float", "desc": ""}, {"field": "constrain_stretching", "typ": "bool", "desc": ""}, {"field": "pixelate_sprite_transforms", "typ": "bool", "desc": ""}, {"field": "scale_sprite_x", "typ": "bool", "desc": ""}, {"field": "follow_entity_transform", "typ": "bool", "desc": ""}, {"field": "animation_amount", "typ": "float", "desc": ""}, {"field": "animation_speed", "typ": "float", "desc": ""}, {"field": "animation_energy", "typ": "float", "desc": ""}, {"field": "cloth_sprite_z_index", "typ": "float", "desc": ""}, {"field": "stain_cells_probability", "typ": "int", "desc": "0 = never, 1 = most likely, 10 = less likely - and so on"}, {"field": "m_is_culled_previous", "typ": "bool", "desc": "Developer note: this needs to be serialized in case we serialize SpriteComponent.is_visible"}, {"field": "type", "typ": "VERLET_TYPE::Enum", "desc": ""}, {"field": "animation_target_offset", "typ": "vec2", "desc": ""}, {"field": "cloth_color_edge", "typ": "uint32", "desc": ""}, {"field": "cloth_color", "typ": "uint32", "desc": ""}, {"field": "m_position_previous", "typ": "vec2", "desc": ""}, {"field": "colors", "typ": "UintArrayInline", "desc": ""}, {"field": "materials", "typ": "UintArrayInline", "desc": ""}, {"field": "masses", "typ": "FloatArrayInline", "desc": ""}, {"field": "positions", "typ": "Vec2ArrayInline", "desc": ""}, {"field": "positions_prev", "typ": "Vec2ArrayInline", "desc": ""}, {"field": "velocities", "typ": "Vec2ArrayInline", "desc": ""}, {"field": "dampenings", "typ": "FloatArrayInline", "desc": ""}, {"field": "freedoms", "typ": "FloatArrayInline", "desc": ""}, {"field": "links", "typ": "VerletLinkArrayInline", "desc": ""}, {"field": "sprite", "typ": "VerletSprite*", "desc": ""}]}, {"name": "VerletWeaponComponent", "fields": [{"field": "damage_radius", "typ": "float", "desc": ""}, {"field": "physics_force_radius", "typ": "float", "desc": ""}, {"field": "damage_min_step", "typ": "float", "desc": ""}, {"field": "damage_max", "typ": "float", "desc": ""}, {"field": "damage_coeff", "typ": "float", "desc": ""}, {"field": "impulse_coeff", "typ": "float", "desc": ""}, {"field": "fade_duration_frames", "typ": "int", "desc": ""}, {"field": "physics_impulse_coeff", "typ": "float", "desc": ""}, {"field": "mPlayerCooldownEnd", "typ": "int", "desc": ""}]}, {"name": "VerletWorldJointComponent", "fields": [{"field": "verlet_point_index", "typ": "int", "desc": "Index of the verlet point we attach"}, {"field": "world_position", "typ": "vec2", "desc": "Where we attach the verlet point"}, {"field": "mUpdated", "typ": "bool", "desc": ""}, {"field": "mCell", "typ": "grid::ICell*", "desc": ""}]}, {"name": "WalletComponent", "fields": [{"field": "money", "typ": "int64", "desc": ""}, {"field": "money_spent", "typ": "int64", "desc": "tracks how much money the player has spent"}, {"field": "mMoneyPrevFrame", "typ": "int64", "desc": "HAX to give player towards infinite moneys"}, {"field": "mHasReachedInf", "typ": "bool", "desc": "once it hits this value... keep it there"}]}, {"name": "WalletValuableComponent", "fields": [{"field": "money_value", "typ": "int", "desc": ""}]}, {"name": "WorldStateComponent", "fields": [{"field": "is_initialized", "typ": "bool", "desc": ""}, {"field": "time", "typ": "float", "desc": ""}, {"field": "time_total", "typ": "float", "desc": ""}, {"field": "time_dt", "typ": "float", "desc": "to make the time go really fast or slow?"}, {"field": "day_count", "typ": "int", "desc": ""}, {"field": "rain", "typ": "float", "desc": "should be called clouds, controls amount of cloud cover in the sky"}, {"field": "rain_target", "typ": "float", "desc": "should be called clouds_target, controls amount of cloud cover in the sky"}, {"field": "fog", "typ": "float", "desc": ""}, {"field": "fog_target", "typ": "float", "desc": ""}, {"field": "intro_weather", "typ": "bool", "desc": "if set, will set the weather to be nice all the time"}, {"field": "wind", "typ": "float", "desc": ""}, {"field": "wind_speed", "typ": "float", "desc": ""}, {"field": "wind_speed_sin_t", "typ": "float", "desc": ""}, {"field": "wind_speed_sin", "typ": "float", "desc": ""}, {"field": "clouds_01_target", "typ": "float", "desc": ""}, {"field": "clouds_02_target", "typ": "float", "desc": ""}, {"field": "gradient_sky_alpha_target", "typ": "float", "desc": ""}, {"field": "sky_sunset_alpha_target", "typ": "float", "desc": ""}, {"field": "lightning_count", "typ": "int", "desc": "this gets decreased to 0, this is the frame count of how many times do we do our awesome lightning effect"}, {"field": "next_portal_id", "typ": "uint32", "desc": ""}, {"field": "session_stat_file", "typ": "std::string", "desc": "if empty, we'll create one. This tracks the play time, death, kills... etch"}, {"field": "player_polymorph_count", "typ": "int", "desc": "how many times player has been polymorphed"}, {"field": "player_polymorph_random_count", "typ": "int", "desc": "how many times player has been random polymorphed"}, {"field": "player_did_infinite_spell_count", "typ": "int", "desc": "how many times player has done a secret trick"}, {"field": "player_did_damage_over_1milj", "typ": "int", "desc": "how many times player has player done damage of over 1000000"}, {"field": "player_living_with_minus_hp", "typ": "int", "desc": "how many times player has been detected with minus health"}, {"field": "global_genome_relations_modifier", "typ": "float", "desc": "Genome_GetHerdRelation adds this value to the results. 100 = good relations, 0 is bad "}, {"field": "mods_have_been_active_during_this_run", "typ": "bool", "desc": ""}, {"field": "twitch_has_been_active_during_this_run", "typ": "bool", "desc": ""}, {"field": "next_cut_through_world_id", "typ": "uint32", "desc": ""}, {"field": "perk_infinite_spells", "typ": "bool", "desc": "if true, almost all spells will have unlimited uses. (black holes, matter eater, heals excluded"}, {"field": "perk_trick_kills_blood_money", "typ": "bool", "desc": "if true, trick kills will produce blood money (heals player)"}, {"field": "perk_hp_drop_chance", "typ": "int", "desc": "if > 0, then there's chance that killing an enemy will drop bloodmoney_50"}, {"field": "perk_gold_is_forever", "typ": "bool", "desc": "drop_money.lua - checks if this is true and removes Lifetime_Component from gold nuggets"}, {"field": "perk_rats_player_friendly", "typ": "bool", "desc": "if 1, rats don't attack player herd and the other way round. this is a persistent change"}, {"field": "EVERYTHING_TO_GOLD", "typ": "bool", "desc": "if true everything will be gold + used to track if the wallet should go to infinite"}, {"field": "material_everything_to_gold", "typ": "std::string", "desc": ""}, {"field": "material_everything_to_gold_static", "typ": "std::string", "desc": ""}, {"field": "INFINITE_GOLD_HAPPENING", "typ": "bool", "desc": "the secret ending with infinite gold"}, {"field": "ENDING_HAPPINESS_HAPPENING", "typ": "bool", "desc": "if true, will do the animations for happiness ending"}, {"field": "ENDING_HAPPINESS_FRAMES", "typ": "int", "desc": "to keep track of the animation"}, {"field": "ENDING_HAPPINESS", "typ": "bool", "desc": "this is set if ending happiness has happened"}, {"field": "mFlashAlpha", "typ": "float", "desc": "to keep track of the animation"}, {"field": "DEBUG_LOADED_FROM_AUTOSAVE", "typ": "int", "desc": "how many times have loaded from autosaves"}, {"field": "DEBUG_LOADED_FROM_OLD_VERSION", "typ": "int", "desc": "how many times have we loaded from an old version of the game"}, {"field": "player_spawn_location", "typ": "vec2", "desc": ""}, {"field": "lua_globals", "typ": "MAP_STRING_STRING", "desc": ""}, {"field": "pending_portals", "typ": "VEC_PENDINGPORTAL", "desc": ""}, {"field": "apparitions_per_level", "typ": "VECTOR_INT32", "desc": ""}, {"field": "npc_parties", "typ": "VEC_NPCPARTY", "desc": ""}, {"field": "orbs_found_thisrun", "typ": "VECTOR_INT32", "desc": ""}, {"field": "flags", "typ": "VECTOR_STRING", "desc": ""}, {"field": "changed_materials", "typ": "VECTOR_STRING", "desc": "pairs of materials changed via ConvertMaterialEverywhere(). stored so these can be restored when loading a save"}, {"field": "cuts_through_world", "typ": "VEC_CUTTHROUGHWORLD", "desc": ""}, {"field": "gore_multiplier", "typ": "LensValue", "desc": ""}, {"field": "trick_kill_gold_multiplier", "typ": "LensValue", "desc": ""}, {"field": "damage_flash_multiplier", "typ": "LensValue", "desc": ""}, {"field": "open_fog_of_war_everywhere", "typ": "LensValue", "desc": "same as the trailer mode, open fog of war everywhere"}, {"field": "consume_actions", "typ": "LensValue", "desc": "same as the trailer mode, spells with limited uses are not consumed if this is false"}, {"field": "rain_target_extra", "typ": "float", "desc": ""}, {"field": "fog_target_extra", "typ": "float", "desc": ""}, {"field": "perk_rats_player_friendly_prev", "typ": "bool", "desc": ""}]}, {"name": "WormAIComponent", "fields": [{"field": "speed", "typ": "float", "desc": ""}, {"field": "speed_hunt", "typ": "float", "desc": ""}, {"field": "direction_adjust_speed", "typ": "float", "desc": ""}, {"field": "direction_adjust_speed_hunt", "typ": "float", "desc": ""}, {"field": "random_target_box_radius", "typ": "float", "desc": ""}, {"field": "new_hunt_target_check_every", "typ": "int", "desc": ""}, {"field": "new_random_target_check_every", "typ": "int", "desc": ""}, {"field": "hunt_box_radius", "typ": "float", "desc": ""}, {"field": "cocoon_food_required", "typ": "int", "desc": "how much food do we need to consume before we can cocoon"}, {"field": "cocoon_entity", "typ": "std::string", "desc": "if empty, won't cocoon, if set it'll spawn this after it's eaten enough"}, {"field": "give_up_area_radius", "typ": "float", "desc": ""}, {"field": "give_up_time_frames", "typ": "int", "desc": ""}, {"field": "debug_follow_mouse", "typ": "bool", "desc": ""}, {"field": "mRandomTarget", "typ": "vec2", "desc": ""}, {"field": "mTargetEntityId", "typ": "int", "desc": ""}, {"field": "mNextTargetCheckFrame", "typ": "int", "desc": ""}, {"field": "mNextHuntTargetCheckFrame", "typ": "int", "desc": ""}, {"field": "mGiveUpStarted", "typ": "int", "desc": ""}, {"field": "mGiveUpAreaMinX", "typ": "int", "desc": ""}, {"field": "mGiveUpAreaMinY", "typ": "int", "desc": ""}, {"field": "mGiveUpAreaMaxX", "typ": "int", "desc": ""}, {"field": "mGiveUpAreaMaxY", "typ": "int", "desc": ""}]}, {"name": "WormAttractorComponent", "fields": [{"field": "direction", "typ": "int", "desc": "1 = attracts worms, -1 detracts worms"}, {"field": "radius", "typ": "float", "desc": "radius of detracting worms"}]}, {"name": "WormComponent", "fields": [{"field": "speed", "typ": "float", "desc": ""}, {"field": "acceleration", "typ": "float", "desc": ""}, {"field": "gravity", "typ": "float", "desc": ""}, {"field": "tail_gravity", "typ": "float", "desc": ""}, {"field": "part_distance", "typ": "float", "desc": ""}, {"field": "ground_check_offset", "typ": "int", "desc": ""}, {"field": "hitbox_radius", "typ": "float", "desc": ""}, {"field": "bite_damage", "typ": "float", "desc": "how much damage does this do when it hits an entity"}, {"field": "target_kill_radius", "typ": "float", "desc": ""}, {"field": "target_kill_ragdoll_force", "typ": "float", "desc": ""}, {"field": "jump_cam_shake", "typ": "float", "desc": ""}, {"field": "jump_cam_shake_distance", "typ": "float", "desc": ""}, {"field": "eat_anim_wait_mult", "typ": "float", "desc": ""}, {"field": "ragdoll_filename", "typ": "std::string", "desc": ""}, {"field": "is_water_worm", "typ": "bool", "desc": "if true, tries to stay in liquids"}, {"field": "max_speed", "typ": "float", "desc": "max speed, used when attracted to a point"}, {"field": "ground_decceleration", "typ": "LensValue", "desc": ""}, {"field": "mTargetVec", "typ": "vec2", "desc": ""}, {"field": "mGravVelocity", "typ": "float", "desc": ""}, {"field": "mSpeed", "typ": "float", "desc": ""}, {"field": "mTargetPosition", "typ": "vec2", "desc": ""}, {"field": "mTargetSpeed", "typ": "float", "desc": ""}, {"field": "mOnGroundPrev", "typ": "bool", "desc": ""}, {"field": "mMaterialIdPrev", "typ": "int", "desc": ""}, {"field": "mFrameNextDamage", "typ": "int", "desc": ""}, {"field": "mDirectionAdjustSpeed", "typ": "float", "desc": ""}, {"field": "mPrevPositions", "typ": "WormPartPositions", "desc": ""}]}, {"name": "WormPlayerComponent", "fields": [{"field": "mPrevPosition", "typ": "vec2", "desc": ""}, {"field": "mDirection", "typ": "vec2", "desc": "if mDirection == 0,0 nothings works"}]}] \ No newline at end of file diff --git a/ewext/noita_api_macro/src/lib.rs b/ewext/noita_api_macro/src/lib.rs new file mode 100644 index 00000000..2afd58da --- /dev/null +++ b/ewext/noita_api_macro/src/lib.rs @@ -0,0 +1,96 @@ +use heck::ToSnekCase; +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; + +#[derive(Deserialize)] +enum Typ { + #[serde(rename = "int")] + Int, + #[serde(rename = "uint32")] + UInt, + #[serde(rename = "float")] + Float, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "std::string")] + StdString, + #[serde(rename = "vec2")] + Vec2, + #[serde(other)] + Other, +} + +impl Typ { + fn as_rust_type(&self) -> proc_macro2::TokenStream { + match self { + Typ::Int => quote!(i32), + Typ::UInt => quote!(u32), + Typ::Float => quote!(f32), + Typ::Bool => quote!(bool), + Typ::StdString => todo!(), + Typ::Vec2 => todo!(), + Typ::Other => todo!(), + } + } +} + +#[derive(Deserialize)] +struct Field { + field: String, + typ: Typ, + desc: String, +} + +#[derive(Deserialize)] +struct Component { + name: String, + fields: Vec, +} +#[proc_macro] +pub fn generate_components(_item: TokenStream) -> TokenStream { + let components: Vec = serde_json::from_str(include_str!("components.json")).unwrap(); + + let res = components.into_iter().map(generate_code_for_component); + quote! {#(#res)*}.into() +} + +fn convert_field_name(field_name: &str) -> String { + if field_name == "type" { + return "type_fld".to_owned(); + } + if field_name == "loop" { + return "loop_fld".to_owned(); + } + field_name.to_snek_case() +} + +fn generate_code_for_component(com: Component) -> proc_macro2::TokenStream { + let component_name = format_ident!("{}", com.name); + + let impls = com.fields.iter().filter_map(|field| { + let field_name = format_ident!("{}", convert_field_name(&field.field)); + let field_doc = &field.desc; + match field.typ { + Typ::Int | Typ::UInt | Typ::Float | Typ::Bool => { + let field_type = field.typ.as_rust_type(); + let set_method_name = format_ident!("set_{}", field_name); + Some(quote! { + #[doc = #field_doc] + fn #field_name(self) -> #field_type { todo!() } + fn #set_method_name(self, value: #field_type) { todo!() } + }) + } + _ => None, + } + }); + + quote! { + #[derive(Clone, Copy, PartialEq, Eq)] + struct #component_name(u32); + + impl #component_name { + #(#impls)* + } + } +} diff --git a/ewext/src/noita.rs b/ewext/src/noita.rs index 1c9a1170..f6673efc 100644 --- a/ewext/src/noita.rs +++ b/ewext/src/noita.rs @@ -1,6 +1,9 @@ use std::{ffi::c_void, mem}; pub(crate) mod ntypes; +mod api { + noita_api_macro::generate_components!(); +} #[repr(packed)] pub(crate) struct NoitaPixelRun { diff --git a/scripts/parse_components.py b/scripts/parse_components.py index 511c208c..54e33e16 100644 --- a/scripts/parse_components.py +++ b/scripts/parse_components.py @@ -1,23 +1,40 @@ import shlex import json +all_types = set() + +renames = { + "std_string": "std::string", +} + def parse_component(component): it = iter(component) - name = next(it) + c_name = next(it) + c_name = c_name.strip("\n") + if "-" in c_name or "\n" in c_name: + print(component) + exit(-1) fields = [] + for line in it: line = line.strip() if line.startswith("-"): continue typ, name, *range_info, desc = shlex.split(line) + name = name.strip("\n") + if name == "-": + print(f"Field of type {typ} skipped") + continue + typ = renames.get(typ, typ) fields.append({ "field": name, "typ": typ, "desc": desc, }) + all_types.add(typ) #print(name, typ, desc, range_info) return { - "name": name, + "name": c_name, "fields": fields, } @@ -26,7 +43,8 @@ path = "/home/quant/.local/share/Steam/steamapps/common/Noita/tools_modding/comp components = [] current = [] -for line in open(path): + +for i, line in enumerate(open(path)): if line == "\n": if current: components.append(current) @@ -37,5 +55,7 @@ for line in open(path): assert not current parsed = [parse_component(component) for component in components] -json.dump(parsed, open("components.json", "w"), indent=None) +json.dump(parsed, open("ewext/noita_api_macro/src/components.json", "w"), indent=None) + +#print(*all_types, sep="\n")