You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							115 lines
						
					
					
						
							4.6 KiB
						
					
					
				
			
		
		
	
	
							115 lines
						
					
					
						
							4.6 KiB
						
					
					
				| local ipairs, minetest, vector, nodecore, include | |
|     = ipairs, minetest, vector, nodecore, include | |
| 
 | |
| local hud     = include("hud") | |
| local rotate  = include("rotate") | |
| local utility = include("utility") | |
| 
 | |
| -- TODO: Add crosshair indicators for rotating around edges. | |
| -- TODO: Replace `on_rightclick` of intended nodes? | |
| -- TODO: Allow right-clicking while holding sneak with an empty hand to invert rotation. | |
| -- TODO: Add some more comments. | |
| 
 | |
| -- Distance at which we want to rotate by "pushing" on an edge. | |
| local EDGE_DISTANCE = 1.5 / 16 -- 1.5 texels (at 16² texture resolution) | |
| 
 | |
| -- Contains a per-player state that holds information, created by a raycast | |
| -- done in `playerstep`, about the node this player might rotate. This is used | |
| -- to decide what to do when a rightclick occurs and to update the their HUD. | |
| local rotating_state = {} | |
| 
 | |
| -- Register rotatable nodes. | |
| do | |
|     local function nodecore_filtered_lookup(eq_func) | |
|         local lookup = {} | |
|         for i = 0, 23 do | |
|             local facedir = nodecore.facedirs[i] | |
|             for j = 1, #lookup do | |
|                 local other = nodecore.facedirs[lookup[j]] | |
|                 if eq_func(facedir, other) then lookup[i] = j; break end | |
|             end | |
|             lookup[i] = lookup[i] or i | |
|         end | |
|         return lookup | |
|     end | |
| 
 | |
|     local LENS_FILTERED_LOOKUP = nodecore_filtered_lookup( | |
|         function(a, b) return vector.equals(a.f, b.f) end) | |
|     local PRISM_FILTERED_LOOKUP = nodecore_filtered_lookup( | |
|         function(a, b) return vector.equals(a.f, b.r) and vector.equals(a.r, b.f) end) | |
|     local PANEL_FILTERED_LOOKUP = nodecore_filtered_lookup( | |
|         function(a, b) return vector.equals(a.f, b.r) and vector.equals(a.r, b.f) end) | |
| 
 | |
|     for _, lens_state in ipairs({ "", "_on", "_glow", "_glow_start" }) do | |
|         rotate.register_rotatable("nc_optics:lens" .. lens_state, LENS_FILTERED_LOOKUP) end | |
|     for _, prism_state in ipairs({ "", "_on", "_gated" }) do | |
|         rotate.register_rotatable("nc_optics:prism" .. prism_state, PRISM_FILTERED_LOOKUP) end | |
| 
 | |
|     rotate.register_rotatable("nc_doors:panel_plank" , PANEL_FILTERED_LOOKUP) | |
|     rotate.register_rotatable("nc_doors:panel_cobble", PANEL_FILTERED_LOOKUP) | |
| end | |
| 
 | |
| -- Calculates the state for the specified `player` and player `data`. | |
| -- This state contains information about how the player would rotate a block. | |
| local function calculate_rotating_state(player, data) | |
|     local state = {} | |
| 
 | |
|     local pointed_thing = data.raycast() | |
|     if (not pointed_thing) or pointed_thing.type ~= "node" then return end | |
| 
 | |
|     state.pos  = pointed_thing.under | |
|     state.node = minetest.get_node(state.pos) | |
|     if not rotate.is_rotatable(state.node) then return end | |
| 
 | |
|     if vector.equals(pointed_thing.above, pointed_thing.under) then return end | |
|     state.face = pointed_thing.above - pointed_thing.under | |
| 
 | |
|     local edge, distance = utility.find_closest_edge(state.node, pointed_thing) | |
|     -- FIXME: Something in `find_closest_edge` is causing an unexpected edge to be returned, hence the additional check. | |
|     if edge and distance <= EDGE_DISTANCE and state.face:multiply(edge):length() ~= 0 then | |
|         state.axis = state.face:cross(edge):normalize() | |
|         state.mode = "edge" | |
|     else | |
|         state.axis = state.face | |
|         state.mode = "face" | |
|     end | |
| 
 | |
|     state.invert = player:get_player_control().sneak | |
| 
 | |
|     local degrees = state.invert and 90 or -90 | |
|     state.facedir = rotate.rotate_facedir(state.node.param2, state.axis, degrees) | |
|     state.facedir = rotate.fix_rotatable_facedir(state.node, state.facedir) | |
|     state.success = state.facedir and state.facedir ~= state.node.param2 | |
| 
 | |
|     return state | |
| end | |
| 
 | |
| nodecore.register_playerstep({ | |
|     label = "extended rotating update", | |
|     action = function(player, data) | |
|         local name = player:get_player_name() | |
|         rotating_state[name] = calculate_rotating_state(player, data) | |
|         hud.update_player_hud(player, rotating_state[name]) | |
|     end, | |
| }) | |
| 
 | |
| minetest.register_on_leaveplayer(function(player) | |
|     local name = player:get_player_name() | |
|     rotating_state[name] = nil | |
| end) | |
| 
 | |
| minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing) | |
|     if not minetest.is_player(puncher) then return end | |
|     local name  = puncher:get_player_name() | |
|     local state = rotating_state[name] | |
|     if not state then return end | |
| 
 | |
|     -- Make sure we're still the same node that we raycasted in `playerstep`. | |
|     if not vector.equals(pos, state.pos) then return end | |
|     if node.name ~= state.node.name or node.param2 ~= state.node.param2 then return end | |
| 
 | |
|     -- Only continue if the node can actually be sucessfully rotated. | |
|     if not state.success then return end | |
| 
 | |
|     minetest.swap_node(pos, nodecore.underride({ param2 = state.facedir }, node)) | |
| end)
 | |
| 
 |