diff --git a/quant.ew/data/scripts/props/chain_to_ceiling.lua b/quant.ew/data/scripts/props/chain_to_ceiling.lua
new file mode 100644
index 00000000..c7b2e1a0
--- /dev/null
+++ b/quant.ew/data/scripts/props/chain_to_ceiling.lua
@@ -0,0 +1,153 @@
+--[[
+Creates a chain to ceiling from a given offset.
+Script looks for variable storages named "chain_n_x" and "chain_n_y" where n is 0...9 and generates one or more chains based on them.
+If no variable storages are found a single chain with 0 offsets is created.
+NOTE: Requires PhysicsBody2Component with body_id 100
+
+XML:
+
+
+
+
+
+
+
+
+--]]
+
+dofile_once("data/scripts/lib/utilities.lua")
+
+local entity_id = GetUpdatedEntityID()
+local pos_x, pos_y = EntityGetTransform( entity_id )
+
+local segment_length = 16
+local search_dist = 200
+local break_force_mid = 1.25
+local break_force_end = 3
+local break_distance_end = 20
+local break_distance_mid = 3
+
+if not DoesWorldExistAt( pos_x, pos_y - search_dist, pos_x, pos_y ) then return end
+
+local count = 0
+local function get_random_chain()
+ count = count + 1
+ local n = ProceduralRandomi(pos_x + entity_id, pos_y + count, 0, 2)
+ return "data/props_breakable_gfx/chain_vertical_16_0" .. n .. ".png"
+end
+
+-- get offsets from components (if available)
+local offsets_x = { }
+local offsets_y = { }
+for i=0,9 do
+ -- get stored values based on naming convention
+ local s = "chain_" .. i
+ local comp_x = get_variable_storage_component(entity_id, s .. "_x")
+ local comp_y = get_variable_storage_component(entity_id, s .. "_y")
+ if comp_x ~= nil then offsets_x[#offsets_x+1] = ComponentGetValue2(comp_x, "value_int") end
+ if comp_y ~= nil then offsets_y[#offsets_y+1] = ComponentGetValue2(comp_y, "value_int") end
+end
+offsets_x[1] = offsets_x[1] or 0
+offsets_y[1] = offsets_y[1] or 0
+
+-- modified joint strength (optional)
+local comp_strength = get_variable_storage_component(entity_id, "chain_strength_multiplier")
+if comp_strength ~= nil then
+ local mult = ComponentGetValue2(comp_strength, "value_float")
+ break_force_mid = break_force_mid * mult
+ break_force_end = break_force_end * mult
+end
+
+
+local mat = CellFactory_GetType( "metal_chain_nohit" )
+-- create chains
+for i=1,#offsets_x do
+ local offset_x = offsets_x[i]
+ local offset_y = offsets_y[i]
+ local x = pos_x + offset_x
+ local y = pos_y + offset_y
+
+ local ceiling_found,_,ceiling_y = RaytracePlatforms( x, y, x, y - search_dist )
+ local dist = y - ceiling_y
+
+ if ceiling_found == true and dist > segment_length then
+ -- to local space
+ y = offset_y - segment_length
+ x = offset_x
+
+ local body_id = i * 100 + 200 -- try to generate ids that don't clash
+
+ -- bottom segment. attach to root with id 100
+ EntityAddComponent2( entity_id, "PhysicsImageShapeComponent",
+ {
+ body_id = body_id,
+ offset_x = x,
+ offset_y = y,
+ image_file = get_random_chain(),
+ material = mat
+ })
+ EntityAddComponent2( entity_id, "PhysicsJoint2Component",
+ {
+ type = "REVOLUTE_JOINT",
+ offset_x = x + 1.5, -- center the joint
+ offset_y = y + segment_length, -- joint is on the bottom of the shape
+ body1_id = 100,
+ body2_id = body_id,
+ break_force = break_force_end,
+ break_distance = break_distance_end,
+ })
+ y = y - segment_length
+
+ -- middle segments
+ for i2=0, dist / segment_length - 2 do
+ body_id = body_id + 1
+ EntityAddComponent2( entity_id, "PhysicsImageShapeComponent",
+ {
+ body_id = body_id,
+ offset_x = x,
+ offset_y = y,
+ image_file = get_random_chain(),
+ material = mat
+ })
+ EntityAddComponent2( entity_id, "PhysicsJoint2Component",
+ {
+ type = "REVOLUTE_JOINT",
+ offset_x = x + 1.5,
+ offset_y = y + segment_length,
+ body1_id = body_id - 1,
+ body2_id = body_id,
+ break_force = break_force_mid,
+ break_distance = break_distance_mid,
+ })
+ y = y - segment_length
+ end
+
+ -- attach to ceiling
+ EntityAddComponent2( entity_id, "PhysicsJoint2Component",
+ {
+ type = "REVOLUTE_JOINT_ATTACH_TO_NEARBY_SURFACE",
+ offset_x = x + 1.5,
+ offset_y = y + segment_length,
+ body1_id = body_id,
+ break_force = break_force_end,
+ break_distance = break_distance_end,
+ ray_x = 0,
+ ray_y = -32,
+ })
+ end
+end
+
+-- done
+-- PhysicsBody2InitFromComponents( entity_id )
+EntitySetComponentIsEnabled( entity_id, GetUpdatedComponentID(), false)
\ No newline at end of file
diff --git a/quant.ew/files/core/constants.lua b/quant.ew/files/core/constants.lua
index e8a5f173..d42b5b76 100644
--- a/quant.ew/files/core/constants.lua
+++ b/quant.ew/files/core/constants.lua
@@ -1,6 +1,5 @@
local module = {}
---TODO fix chained entities spawning repeatably on clients
module.phys_sync_allowed = {
-- Starting prop
["data/entities/props/physics_skateboard.xml"] = true,
@@ -20,19 +19,63 @@ module.phys_sync_allowed = {
["data/entities/props/physics_crate.xml"] = true,
["data/entities/props/physics_barrel_oil.xml"] = true,
+ ["data/entities/props/physics_barrel_water.xml"] = true,
["data/entities/props/physics_barrel_radioactive.xml"] = true,
["data/entities/props/physics_seamine.xml"] = true,
--- ["data/entities/props/suspended_tank_radioactive.xml"] = true,
--- ["data/entities/props/suspended_tank_acid.xml"] = true,
["data/entities/props/physics_box_harmless.xml"] = true,
["data/entities/props/physics_tubelamp.xml"] = true,
+ ["data/entities/props/suspended_container.xml"] = true,
+ ["data/entities/props/suspended_seamine.xml"] = true,
+ ["data/entities/props/suspended_tank_acid.xml"] = true,
+ ["data/entities/props/suspended_tank_radioactive.xml"] = true,
+
+ ["data/entities/props/physics_wheel_small.xml"] = true,
+ ["data/entities/props/physics_wheel_stand_01.xml"] = true,
+ ["data/entities/props/physics_wheel_stand_02.xml"] = true,
+ ["data/entities/props/physics_wheel_stand_03.xml"] = true,
+ ["data/entities/props/physics_wheel_tiny.xml"] = true,
+ ["data/entities/props/physics_wheel.xml"] = true,
+
+ ["data/entities/props/physics_fungus_acid_big.xml"] = true,
+ ["data/entities/props/physics_fungus_acid_hugeish.xml"] = true,
+ ["data/entities/props/physics_fungus_acid_huge.xml"] = true,
+ ["data/entities/props/physics_fungus_acid_small.xml"] = true,
+ ["data/entities/props/physics_fungus_acid.xml"] = true,
+ ["data/entities/props/physics_fungus_big.xml"] = true,
+ ["data/entities/props/physics_fungus_hugeish.xml"] = true,
+ ["data/entities/props/physics_fungus_huge.xml"] = true,
+ ["data/entities/props/physics_fungus_small.xml"] = true,
+ ["data/entities/props/physics_fungus_trap.xml"] = true,
+ ["data/entities/props/physics_fungus.xml"] = true,
+
["data/entities/props/physics_torch_stand.xml"] = true,
["data/entities/props/vault_apparatus_01.xml"] = true,
["data/entities/props/vault_apparatus_02.xml"] = true,
["data/entities/props/physics_pressure_tank.xml"] = true,
+ ["data/entities/props/furniture_bed.xml"] = true,
+ ["data/entities/props/furniture_bunk.xml"] = true,
+ ["data/entities/props/furniture_castle_chair.xml"] = true,
+ ["data/entities/props/furniture_castle_divan.xml"] = true,
+ ["data/entities/props/furniture_castle_statue.xml"] = true,
+ ["data/entities/props/furniture_castle_table.xml"] = true,
+ ["data/entities/props/furniture_castle_wardrobe.xml"] = true,
+ ["data/entities/props/furniture_cryopod.xml"] = true,
+ ["data/entities/props/furniture_dresser.xml"] = true,
+ ["data/entities/props/furniture_footlocker.xml"] = true,
+ ["data/entities/props/furniture_locker.xml"] = true,
+ ["data/entities/props/furniture_rocking_chair.xml"] = true,
+ ["data/entities/props/furniture_stool.xml"] = true,
+ ["data/entities/props/furniture_table.xml"] = true,
+ ["data/entities/props/furniture_tombstone_01.xml"] = true,
+ ["data/entities/props/furniture_tombstone_02.xml"] = true,
+ ["data/entities/props/furniture_tombstone_03.xml"] = true,
+ ["data/entities/props/furniture_wardrobe.xml"] = true,
+ ["data/entities/props/furniture_wood_chair.xml"] = true,
+ ["data/entities/props/furniture_wood_table.xml"] = true,
+
["data/entities/props/crystal_red.xml"] = true,
["data/entities/props/crystal_pink.xml"] = true,
["data/entities/props/crystal_green.xml"] = true,
@@ -40,7 +83,6 @@ module.phys_sync_allowed = {
["data/entities/props/physics_vase.xml"] = true,
["data/entities/props/physics_vase_longleg.xml"] = true,
-
["data/entities/props/physics_sun_rock.xml"] = true,
["data/entities/props/physics_darksun_rock.xml"] = true,
@@ -52,11 +94,11 @@ module.phys_sync_allowed = {
["data/entities/props/temple_statue_01.xml"] = true,
["data/entities/props/temple_statue_01_green.xml"] = true,
["data/entities/props/temple_statue_02.xml"] = true,
--- ["data/entities/props/physics/temple_lantern.xml"] = true,
--- ["data/entities/buildings/physics_worm_deflector_base.xml"] = true,
--- ["data/entities/buildings/physics_worm_deflector_crystal.xml"] = true,
--- ["data/entities/misc/greed_curse/greed_crystal.xml"] = true,
--- ["data/entities/props/physics/lantern_small.xml"] = true,
+ ["data/entities/props/physics/temple_lantern.xml"] = true,
+ ["data/entities/buildings/physics_worm_deflector_base.xml"] = true,
+ --["data/entities/buildings/physics_worm_deflector_crystal.xml"] = true,
+ --["data/entities/misc/greed_curse/greed_crystal.xml"] = true,
+ ["data/entities/props/physics/lantern_small.xml"] = true,
-- Traps
["data/entities/props/physics_trap_circle_acid.xml"] = true,