Compare commits
	
		
			18 Commits
		
	
	
		
			302a28934d
			...
			e1cffdedbf
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e1cffdedbf | ||
|  | d3aedd2b98 | ||
|  | 68c1729990 | ||
|  | 9b58f8db29 | ||
|  | 7784b13da5 | ||
|  | 0dd530312b | ||
|  | e78bbd6f98 | ||
|  | bfd952b51a | ||
|  | b7873e8e02 | ||
|  | d6b2a39c99 | ||
|  | 1b54011b68 | ||
|  | 15e743629e | ||
|  | 1bf862f932 | ||
|  | 1a9704f184 | ||
|  | 8baa789eb1 | ||
|  | b0158f5674 | ||
|  | 073c92d487 | ||
|  | 737f366741 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1 +1,3 @@ | ||||
| *~ | ||||
| *.patch | ||||
| *.diff | ||||
|   | ||||
| @@ -1,96 +1,140 @@ | ||||
| mesecon.queue.actions={} -- contains all ActionQueue actions | ||||
| --[[ | ||||
| Mesecons uses something it calls an ActionQueue. | ||||
|  | ||||
| function mesecon.queue:add_function(name, func) | ||||
| 	mesecon.queue.funcs[name] = func | ||||
| The ActionQueue holds functions and actions. | ||||
| Functions are added on load time with a specified name. | ||||
| Actions are preserved over server restarts. | ||||
|  | ||||
| Each action consists of a position, the name of an added function to be called, | ||||
| the params that should be used in this function call (additionally to the pos), | ||||
| the time after which it should be executed, an optional overwritecheck and a | ||||
| priority. | ||||
|  | ||||
| If time = 0, the action will be executed in the next globalstep, otherwise the | ||||
| earliest globalstep when it will be executed is the after next globalstep. | ||||
|  | ||||
| It is guaranteed, that for two actions ac1, ac2 where ac1 ~= ac2, | ||||
| ac1.time == ac2.time, ac1.priority == ac2.priority and ac1 was added earlier | ||||
| than ac2, ac1 will be executed before ac2 (but in the same globalstep). | ||||
|  | ||||
| Note: Do not pass references in params, as they can not be preserved. | ||||
|  | ||||
| Also note: Some of the guarantees here might be dropped at some time. | ||||
| ]] | ||||
|  | ||||
|  | ||||
| -- localize for speed | ||||
| local queue = mesecon.queue | ||||
|  | ||||
| queue.actions = {} -- contains all ActionQueue actions | ||||
|  | ||||
| function queue:add_function(name, func) | ||||
| 	queue.funcs[name] = func | ||||
| end | ||||
|  | ||||
| -- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten | ||||
| -- use overwritecheck nil to never overwrite, but just add the event to the queue | ||||
| -- priority specifies the order actions are executed within one globalstep, highest first | ||||
| -- should be between 0 and 1 | ||||
| function mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority) | ||||
| function queue:add_action(pos, func, params, time, overwritecheck, priority) | ||||
| 	-- Create Action Table: | ||||
| 	time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution | ||||
| 	priority = priority or 1 | ||||
| 	local action = {	pos=mesecon.tablecopy(pos), | ||||
| 	local action = { | ||||
| 		pos = mesecon.tablecopy(pos), | ||||
| 		func = func, | ||||
| 		params = mesecon.tablecopy(params or {}), | ||||
| 		time = time, | ||||
| 		owcheck = (overwritecheck and mesecon.tablecopy(overwritecheck)) or nil, | ||||
| 				priority=priority} | ||||
| 		priority = priority | ||||
| 	} | ||||
|  | ||||
| 	local toremove = nil | ||||
| 	-- Otherwise, add the action to the queue | ||||
| 	if overwritecheck then -- check if old action has to be overwritten / removed: | ||||
| 		for i, ac in ipairs(mesecon.queue.actions) do | ||||
| 			if(vector.equals(pos, ac.pos) | ||||
| 			and mesecon.cmpAny(overwritecheck, ac.owcheck)) then | ||||
| 				toremove = i | ||||
| 	 -- check if old action has to be overwritten / removed: | ||||
| 	if overwritecheck then | ||||
| 		for i, ac in ipairs(queue.actions) do | ||||
| 			if vector.equals(pos, ac.pos) | ||||
| 					and mesecon.cmpAny(overwritecheck, ac.owcheck) then | ||||
| 				-- remove the old action | ||||
| 				table.remove(queue.actions, i) | ||||
| 				break | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	if (toremove ~= nil) then | ||||
| 		table.remove(mesecon.queue.actions, toremove) | ||||
| 	end | ||||
|  | ||||
| 	table.insert(mesecon.queue.actions, action) | ||||
| 	table.insert(queue.actions, action) | ||||
| end | ||||
|  | ||||
| -- execute the stored functions on a globalstep | ||||
| -- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function | ||||
| -- this makes sure that resuming mesecons circuits when restarting minetest works fine | ||||
| -- this makes sure that resuming mesecons circuits when restarting minetest works fine (hm, where do we do this?) | ||||
| -- However, even that does not work in some cases, that's why we delay the time the globalsteps | ||||
| -- start to be execute by 5 seconds | ||||
| local get_highest_priority = function (actions) | ||||
| 	local highestp = -1 | ||||
| 	local highesti | ||||
| 	for i, ac in ipairs(actions) do | ||||
| 		if ac.priority > highestp then | ||||
| 			highestp = ac.priority | ||||
| 			highesti = i | ||||
| 		end | ||||
| 	end | ||||
| -- start to be execute by 4 seconds | ||||
|  | ||||
| 	return highesti | ||||
| end | ||||
|  | ||||
| local m_time = 0 | ||||
| local resumetime = mesecon.setting("resumetime", 4) | ||||
| minetest.register_globalstep(function (dtime) | ||||
| 	m_time = m_time + dtime | ||||
| 	-- don't even try if server has not been running for XY seconds; resumetime = time to wait | ||||
| 	-- after starting the server before processing the ActionQueue, don't set this too low | ||||
| 	if (m_time < resumetime) then return end | ||||
| 	local actions = mesecon.tablecopy(mesecon.queue.actions) | ||||
| local function globalstep_func(dtime) | ||||
| 	local actions = queue.actions | ||||
| 	-- split into two categories: | ||||
| 	-- actions_now: actions to execute now | ||||
| 	-- queue.actions: actions to execute later | ||||
| 	local actions_now = {} | ||||
| 	queue.actions = {} | ||||
|  | ||||
| 	mesecon.queue.actions = {} | ||||
|  | ||||
| 	-- sort actions into two categories: | ||||
| 	-- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions) | ||||
| 	for i, ac in ipairs(actions) do | ||||
| 	for _, ac in ipairs(actions) do | ||||
| 		if ac.time > 0 then | ||||
| 			ac.time = ac.time - dtime -- executed later | ||||
| 			table.insert(mesecon.queue.actions, ac) | ||||
| 			-- action ac is to be executed later | ||||
| 			-- ~> insert into queue.actions | ||||
| 			ac.time = ac.time - dtime | ||||
| 			table.insert(queue.actions, ac) | ||||
| 		else | ||||
| 			-- action ac is to be executed now | ||||
| 			-- ~> insert into actions_now | ||||
| 			table.insert(actions_now, ac) | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	while(#actions_now > 0) do -- execute highest priorities first, until all are executed | ||||
| 		local hp = get_highest_priority(actions_now) | ||||
| 		mesecon.queue:execute(actions_now[hp]) | ||||
| 		table.remove(actions_now, hp) | ||||
| 	-- stable-sort the executed actions after their priority | ||||
| 	-- some constructions might depend on the execution order, hence we first | ||||
| 	-- execute the actions that had a lower index in actions_now | ||||
| 	local old_action_order = {} | ||||
| 	for i, ac in ipairs(actions_now) do | ||||
| 		old_action_order[ac] = i | ||||
| 	end | ||||
| 	table.sort(actions_now, function(ac1, ac2) | ||||
| 		if ac1.priority ~= ac2.priority then | ||||
| 			return ac1.priority > ac2.priority | ||||
| 		else | ||||
| 			return old_action_order[ac1] < old_action_order[ac2] | ||||
| 		end | ||||
| 	end) | ||||
|  | ||||
| function mesecon.queue:execute(action) | ||||
| 	-- execute highest priorities first, until all are executed | ||||
| 	for _, ac in ipairs(actions_now) do | ||||
| 		queue:execute(ac) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| -- delay the time the globalsteps start to be execute by 4 seconds | ||||
| do | ||||
| 	local m_time = 0 | ||||
| 	local resumetime = mesecon.setting("resumetime", 4) | ||||
| 	local globalstep_func_index = #minetest.registered_globalsteps + 1 | ||||
|  | ||||
| 	minetest.register_globalstep(function(dtime) | ||||
| 		m_time = m_time + dtime | ||||
| 		-- don't even try if server has not been running for XY seconds; resumetime = time to wait | ||||
| 		-- after starting the server before processing the ActionQueue, don't set this too low | ||||
| 		if m_time < resumetime then | ||||
| 			return | ||||
| 		end | ||||
| 		-- replace this globalstep function | ||||
| 		minetest.registered_globalsteps[globalstep_func_index] = globalstep_func | ||||
| 	end) | ||||
| end | ||||
|  | ||||
| function queue:execute(action) | ||||
| 	-- ignore if action queue function name doesn't exist, | ||||
| 	-- (e.g. in case the action queue savegame was written by an old mesecons version) | ||||
| 	if mesecon.queue.funcs[action.func] then | ||||
| 		mesecon.queue.funcs[action.func](action.pos, unpack(action.params)) | ||||
| 	if queue.funcs[action.func] then | ||||
| 		queue.funcs[action.func](action.pos, unpack(action.params)) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| @@ -98,8 +142,8 @@ end | ||||
| -- Store and read the ActionQueue to / from a file | ||||
| -- so that upcoming actions are remembered when the game | ||||
| -- is restarted | ||||
| mesecon.queue.actions = mesecon.file2table("mesecon_actionqueue") | ||||
| queue.actions = mesecon.file2table("mesecon_actionqueue") | ||||
|  | ||||
| minetest.register_on_shutdown(function() | ||||
| 	mesecon.table2file("mesecon_actionqueue", mesecon.queue.actions) | ||||
| 	mesecon.table2file("mesecon_actionqueue", queue.actions) | ||||
| end) | ||||
|   | ||||
| Before Width: | Height: | Size: 323 B After Width: | Height: | Size: 191 B | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 844 B | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 838 B | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 851 B | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 846 B | 
| Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 222 B | 
| Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 504 B | 
| Before Width: | Height: | Size: 204 B After Width: | Height: | Size: 144 B | 
| Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 377 B | 
| Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 362 B | 
| @@ -186,19 +186,11 @@ function mesecon.invertRule(r) | ||||
| 	return vector.multiply(r, -1) | ||||
| end | ||||
|  | ||||
| function mesecon.tablecopy(table) -- deep table copy | ||||
| 	if type(table) ~= "table" then return table end -- no need to copy | ||||
| 	local newtable = {} | ||||
|  | ||||
| 	for idx, item in pairs(table) do | ||||
| 		if type(item) == "table" then | ||||
| 			newtable[idx] = mesecon.tablecopy(item) | ||||
| 		else | ||||
| 			newtable[idx] = item | ||||
| function mesecon.tablecopy(obj) -- deep copy | ||||
| 	if type(obj) == "table" then | ||||
| 		return table.copy(obj) | ||||
| 	end | ||||
| 	end | ||||
|  | ||||
| 	return newtable | ||||
| 	return obj | ||||
| end | ||||
|  | ||||
| function mesecon.cmpAny(t1, t2) | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								mesecons_blinkyplant/doc/blinkyplant/preview.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 54 KiB | 
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 367 B | 
| Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 394 B | 
| Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 68 KiB | 
| Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 4.3 KiB | 
| Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 371 B | 
| Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 409 B | 
| Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 220 B | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 33 KiB | 
| Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 183 B | 
| Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 183 B | 
| Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 50 KiB | 
| Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 4.2 KiB | 
| @@ -33,19 +33,9 @@ end | ||||
|  | ||||
| -- Register the 2 (states) x 4 (delay times) delayers | ||||
|  | ||||
| for i = 1, 4 do | ||||
| local groups = {} | ||||
| if i == 1 then | ||||
| 	groups = {bendy=2,snappy=1,dig_immediate=2} | ||||
| else | ||||
| 	groups = {bendy=2,snappy=1,dig_immediate=2, not_in_creative_inventory=1} | ||||
| end | ||||
| local delaytime = { 0.1, 0.3, 0.5, 1.0 } | ||||
|  | ||||
| local delaytime | ||||
| if 		i == 1 then delaytime = 0.1 | ||||
| elseif	i == 2 then delaytime = 0.3 | ||||
| elseif	i == 3 then delaytime = 0.5 | ||||
| elseif	i == 4 then delaytime = 1.0 end | ||||
| for i = 1, 4 do | ||||
|  | ||||
| local boxes = { | ||||
| 	 { -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 },		-- the main slab | ||||
| @@ -61,9 +51,36 @@ local boxes = { | ||||
| 	 { 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 } | ||||
| } | ||||
|  | ||||
| minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { | ||||
| 	description = "Delayer", | ||||
| -- Delayer definition defaults | ||||
| local def = { | ||||
| 	drawtype = "nodebox", | ||||
| 	walkable = true, | ||||
| 	selection_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, | ||||
| 	}, | ||||
| 	node_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = boxes | ||||
| 	}, | ||||
| 	paramtype = "light", | ||||
| 	paramtype2 = "facedir", | ||||
| 	sunlight_propagates = true, | ||||
| 	is_ground_content = false, | ||||
| 	delayer_time = delaytime[i], | ||||
| 	sounds = default.node_sound_stone_defaults(), | ||||
| 	on_blast = mesecon.on_blastnode, | ||||
| 	drop = "mesecons_delayer:delayer_off_1", | ||||
| } | ||||
|  | ||||
| -- Deactivated delayer definition defaults | ||||
| local off_groups = {bendy=2,snappy=1,dig_immediate=2} | ||||
| if i > 1 then | ||||
| 	off_groups.not_in_creative_inventory = 1 | ||||
| end | ||||
|  | ||||
| local off_state = { | ||||
| 	description = "Delayer", | ||||
| 	tiles = { | ||||
| 		"mesecons_delayer_off_"..tostring(i)..".png", | ||||
| 		"mesecons_delayer_bottom.png", | ||||
| @@ -74,35 +91,18 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { | ||||
| 	}, | ||||
| 	inventory_image = "mesecons_delayer_off_1.png", | ||||
| 	wield_image = "mesecons_delayer_off_1.png", | ||||
| 	walkable = true, | ||||
| 	selection_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, | ||||
| 	}, | ||||
| 	node_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = boxes | ||||
| 	}, | ||||
| 	groups = groups, | ||||
| 	paramtype = "light", | ||||
| 	paramtype2 = "facedir", | ||||
| 	sunlight_propagates = true, | ||||
| 	is_ground_content = false, | ||||
| 	drop = 'mesecons_delayer:delayer_off_1', | ||||
| 	on_punch = function (pos, node) | ||||
| 		if node.name=="mesecons_delayer:delayer_off_1" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_2", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_off_2" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_3", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_off_3" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_4", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_off_4" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_1", param2=node.param2}) | ||||
| 	groups = off_groups, | ||||
| 	on_punch = function(pos, node, puncher) | ||||
| 		if minetest.is_protected(pos, puncher and puncher:get_player_name()) then | ||||
| 			return | ||||
| 		end | ||||
|  | ||||
| 		minetest.swap_node(pos, { | ||||
| 			name = "mesecons_delayer:delayer_off_"..tostring(i % 4 + 1), | ||||
| 			param2 = node.param2 | ||||
| 		}) | ||||
| 	end, | ||||
| 	delayer_time = delaytime, | ||||
| 	delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i), | ||||
| 	sounds = default.node_sound_stone_defaults(), | ||||
| 	mesecons = { | ||||
| 		receptor = | ||||
| 		{ | ||||
| @@ -115,13 +115,15 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { | ||||
| 			action_on = delayer_activate | ||||
| 		} | ||||
| 	}, | ||||
| 	on_blast = mesecon.on_blastnode, | ||||
| }) | ||||
| } | ||||
| for k, v in pairs(def) do | ||||
| 	off_state[k] = off_state[k] or v | ||||
| end | ||||
| minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), off_state) | ||||
|  | ||||
|  | ||||
| minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { | ||||
| -- Activated delayer definition defaults | ||||
| local on_state = { | ||||
| 	description = "You hacker you", | ||||
| 	drawtype = "nodebox", | ||||
| 	tiles = { | ||||
| 		"mesecons_delayer_on_"..tostring(i)..".png", | ||||
| 		"mesecons_delayer_bottom.png", | ||||
| @@ -130,35 +132,18 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { | ||||
| 		"mesecons_delayer_sides_on.png", | ||||
| 		"mesecons_delayer_sides_on.png" | ||||
| 	}, | ||||
| 	walkable = true, | ||||
| 	selection_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, | ||||
| 	}, | ||||
| 	node_box = { | ||||
| 		type = "fixed", | ||||
| 		fixed = boxes | ||||
| 	}, | ||||
| 	groups = {bendy = 2, snappy = 1, dig_immediate = 2, not_in_creative_inventory = 1}, | ||||
| 	paramtype = "light", | ||||
| 	paramtype2 = "facedir", | ||||
| 	sunlight_propagates = true, | ||||
| 	is_ground_content = false, | ||||
| 	drop = 'mesecons_delayer:delayer_off_1', | ||||
| 	on_punch = function (pos, node) | ||||
| 		if node.name=="mesecons_delayer:delayer_on_1" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_2", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_on_2" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_3", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_on_3" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_4", param2=node.param2}) | ||||
| 		elseif node.name=="mesecons_delayer:delayer_on_4" then | ||||
| 			minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_1", param2=node.param2}) | ||||
| 	on_punch = function(pos, node, puncher) | ||||
| 		if minetest.is_protected(pos, puncher and puncher:get_player_name()) then | ||||
| 			return | ||||
| 		end | ||||
|  | ||||
| 		minetest.swap_node(pos, { | ||||
| 			name = "mesecons_delayer:delayer_on_"..tostring(i % 4 + 1), | ||||
| 			param2 = node.param2 | ||||
| 		}) | ||||
| 	end, | ||||
| 	delayer_time = delaytime, | ||||
| 	delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i), | ||||
| 	sounds = default.node_sound_stone_defaults(), | ||||
| 	mesecons = { | ||||
| 		receptor = | ||||
| 		{ | ||||
| @@ -171,8 +156,12 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { | ||||
| 			action_off = delayer_deactivate | ||||
| 		} | ||||
| 	}, | ||||
| 	on_blast = mesecon.on_blastnode, | ||||
| }) | ||||
| } | ||||
| for k, v in pairs(def) do | ||||
| 	on_state[k] = on_state[k] or v | ||||
| end | ||||
| minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), on_state) | ||||
|  | ||||
| end | ||||
|  | ||||
| minetest.register_craft({ | ||||
|   | ||||
| Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 221 B | 
| Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 176 B | 
| Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 187 B | 
| Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 448 B | 
| Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 449 B | 
| Before Width: | Height: | Size: 561 B After Width: | Height: | Size: 448 B | 
| Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 446 B | 
| Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 541 B | 
| Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 541 B | 
| Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 541 B | 
| Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 538 B | 
| Before Width: | Height: | Size: 229 B After Width: | Height: | Size: 173 B | 
| Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 181 B | 
| @@ -1,8 +1,11 @@ | ||||
| The node detector is a receptor. It changes its state when either any node | ||||
| or a specific node is detected. Right-click it to set a nodename to scan for. | ||||
| It can also receive digiline signals. You can either send "GET" and it will | ||||
| respond with the detected nodename or you can send any other string and it will | ||||
| set this string as the node to scan for. | ||||
| It can also receive digiline signals. For example, you can send | ||||
| <code>{distance=4, scanname="default:dirt"}</code> | ||||
| to set distance to 4 and scan for dirt. You can omit either parameter. | ||||
| There is also a command parameter: <code>{command="get"}</code> will respond | ||||
| with the detected nodename and <code>{command="scan"}</code> will respond with | ||||
| a boolean using the distance and nodename of the detector. | ||||
| Nodenames must include the mod they reside in, so for instance default:dirt, not just dirt. | ||||
| The distance parameter specifies how many blocks are between the node detector and the node to detect. | ||||
| Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work. | ||||
|   | ||||
| Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 43 KiB | 
| Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.3 KiB | 
| Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 78 KiB | 
| Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 7.1 KiB | 
| @@ -189,29 +189,50 @@ local function node_detector_scan(pos) | ||||
| 		(frontname ~= "air" and frontname ~= "ignore" and scanname == "") | ||||
| end | ||||
|  | ||||
| local function node_detector_send_node_name(pos, node, channel, meta) | ||||
| 	local distance = meta:get_int("distance") | ||||
| 	local distance_max = mesecon.setting("node_detector_distance_max", 10) | ||||
| 	if distance < 0 then distance = 0 end | ||||
| 	if distance > distance_max then distance = distance_max end | ||||
| 	local nodename = minetest.get_node( | ||||
| 		vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1)) | ||||
| 	).name | ||||
|  | ||||
| 	digiline:receptor_send(pos, digiline.rules.default, channel, nodename) | ||||
| end | ||||
|  | ||||
| -- set player name when receiving a digiline signal on a specific channel | ||||
| local node_detector_digiline = { | ||||
| 	effector = { | ||||
| 		action = function(pos, node, channel, msg) | ||||
| 			local meta = minetest.get_meta(pos) | ||||
|  | ||||
| 			local distance = meta:get_int("distance") | ||||
| 			local distance_max = mesecon.setting("node_detector_distance_max", 10) | ||||
| 			if distance < 0 then distance = 0 end | ||||
| 			if distance > distance_max then distance = distance_max end | ||||
|  | ||||
| 			if channel ~= meta:get_string("digiline_channel") then return end | ||||
|  | ||||
| 			if type(msg) == "table" then | ||||
| 				if msg.distance or msg.scanname then | ||||
| 					if msg.distance then | ||||
| 						meta:set_string("distance", msg.distance) | ||||
| 					end | ||||
| 					if msg.scanname then | ||||
| 						meta:set_string("scanname", msg.scanname) | ||||
| 					end | ||||
| 					node_detector_make_formspec(pos) | ||||
| 				end | ||||
| 				if msg.command == "get" then | ||||
| 					node_detector_send_node_name(pos, node, channel, meta) | ||||
| 				elseif msg.command == "scan" then | ||||
| 					local result = node_detector_scan(pos) | ||||
| 					digiline:receptor_send(pos, digiline.rules.default, channel, result) | ||||
| 				end | ||||
| 			else | ||||
| 				if msg == GET_COMMAND then | ||||
| 				local nodename = minetest.get_node( | ||||
| 					vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1)) | ||||
| 				).name | ||||
|  | ||||
| 				digiline:receptor_send(pos, digiline.rules.default, channel, nodename) | ||||
| 					node_detector_send_node_name(pos, node, channel, meta) | ||||
| 				else | ||||
| 					meta:set_string("scanname", msg) | ||||
| 					node_detector_make_formspec(pos) | ||||
| 				end | ||||
| 			end | ||||
| 		end, | ||||
| 	}, | ||||
| 	receptor = {} | ||||
|   | ||||
| Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 640 B | 
| Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 687 B | 
| Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 655 B | 
| Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 693 B | 
| Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 36 KiB | 
| Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.9 KiB | 
| Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 28 KiB | 
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 2.5 KiB | 
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.9 KiB | 
| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.9 KiB | 
| Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB | 
| Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.9 KiB | 
| Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 519 B | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB | 
| @@ -1,10 +1,11 @@ | ||||
| local plg = {} | ||||
| plg.rules = {} | ||||
| -- per-player formspec positions | ||||
| plg.open_formspecs = {} | ||||
|  | ||||
| local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua") | ||||
| dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg) | ||||
|  | ||||
|  | ||||
| plg.register_nodes = function(template) | ||||
| 	-- each loop is for one of the 4 IO ports | ||||
| 	for a = 0, 1 do | ||||
| @@ -93,16 +94,20 @@ plg.register_nodes({ | ||||
|  | ||||
| 		meta:set_string("instr", lcore.serialize(is)) | ||||
| 		meta:set_int("valid", 0) | ||||
| 		meta:set_string("formspec", plg.to_formspec_string(is)) | ||||
| 		meta:set_string("infotext", "FPGA") | ||||
| 	end, | ||||
| 	on_receive_fields = function(pos, formname, fields, sender) | ||||
| 		if fields.program == nil then return end -- we only care when the user clicks "Program" | ||||
| 	on_rightclick = function(pos, node, clicker) | ||||
| 		if not minetest.is_player(clicker) then | ||||
| 			return | ||||
| 		end | ||||
| 		local meta = minetest.get_meta(pos) | ||||
| 		local is = plg.from_formspec_fields(fields) | ||||
| 		local name = clicker:get_player_name() | ||||
| 		-- Erase formspecs of old FPGAs | ||||
| 		meta:set_string("formspec", "") | ||||
|  | ||||
| 		meta:set_string("instr", lcore.serialize(is)) | ||||
| 		plg.update_formspec(pos, is) | ||||
| 		plg.open_formspecs[name] = pos | ||||
| 		local is = lcore.deserialize(meta:get_string("instr")) | ||||
| 		minetest.show_formspec(name, "mesecons:fpga", plg.to_formspec_string(is, nil)) | ||||
| 	end, | ||||
| 	sounds = default.node_sound_stone_defaults(), | ||||
| 	mesecons = { | ||||
| @@ -116,6 +121,12 @@ plg.register_nodes({ | ||||
| 	}, | ||||
| 	after_dig_node = function(pos, node) | ||||
| 		mesecon.receptor_off(pos, plg.rules[node.name]) | ||||
| 		for name, open_pos in pairs(plg.open_formspecs) do | ||||
| 			if vector.equals(pos, open_pos) then | ||||
| 				minetest.close_formspec(name, "mesecons:fpga") | ||||
| 				plg.open_formspecs[name] = nil | ||||
| 			end | ||||
| 		end | ||||
| 	end, | ||||
| 	on_blast = mesecon.on_blastnode, | ||||
| 	on_rotate = function(pos, node, user, mode) | ||||
| @@ -153,13 +164,12 @@ plg.register_nodes({ | ||||
| 		end | ||||
|  | ||||
| 		meta:set_string("instr", lcore.serialize(instr)) | ||||
| 		plg.update_formspec(pos, instr) | ||||
| 		plg.update_meta(pos, instr) | ||||
| 		return true | ||||
| 	end, | ||||
| }) | ||||
|  | ||||
|  | ||||
| plg.to_formspec_string = function(is) | ||||
| plg.to_formspec_string = function(is, err) | ||||
| 	local function dropdown_op(x, y, name, val) | ||||
| 		local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";" | ||||
| 				.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored? | ||||
| @@ -180,26 +190,20 @@ plg.to_formspec_string = function(is) | ||||
| 		return s .. "]" | ||||
| 	end | ||||
| 	local function dropdown_action(x, y, name, val) | ||||
| 		local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";" | ||||
| 				.. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored? | ||||
| 		s = s .. " , AND,  OR, NOT, XOR,NAND,   =,XNOR;" | ||||
| 		if val == nil then | ||||
| 			return s .. "0]" -- actually selects no field at all | ||||
| 		local selected = 0 | ||||
| 		local titles = { " " } | ||||
| 		for i, data in ipairs(lcore.get_operations()) do | ||||
| 			titles[i + 1] = data.fs_name | ||||
| 			if val == data.gate then | ||||
| 				selected = i + 1 | ||||
| 			end | ||||
| 		local mapping = { | ||||
| 			["and"] = 1, | ||||
| 			["or"] = 2, | ||||
| 			["not"] = 3, | ||||
| 			["xor"] = 4, | ||||
| 			["nand"] = 5, | ||||
| 			["buf"] = 6, | ||||
| 			["xnor"] = 7, | ||||
| 		} | ||||
| 		return s .. tostring(1 + mapping[val]) .. "]" | ||||
| 		end | ||||
| 		return ("dropdown[%f,%f;1.125,0.5;%s;%s;%i]"):format( | ||||
| 			x, y, name, table.concat(titles, ","), selected) | ||||
| 	end | ||||
| 	local s = "size[9,9]".. | ||||
| 		"label[3.4,-0.15;FPGA gate configuration]".. | ||||
| 		"button_exit[7,7.5;2,2.5;program;Program]".. | ||||
| 		"button[7,7.5;2,2.5;program;Program]".. | ||||
| 		"box[4.2,0.5;0.03,7;#ffffff]".. | ||||
| 		"label[0.25,0.25;op. 1]".. | ||||
| 		"label[1.0,0.25;gate type]".. | ||||
| @@ -225,6 +229,12 @@ plg.to_formspec_string = function(is) | ||||
| 			y = 1 - 0.25 | ||||
| 		end | ||||
| 	end | ||||
| 	if err then | ||||
| 		local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg)) | ||||
| 		s = s .. plg.red_box_around(err.i) .. | ||||
| 			"label[0.25,8.25;The gate configuration is erroneous in the marked area:]".. | ||||
| 			"label[0.25,8.5;" .. fmsg .. "]" | ||||
| 	end | ||||
| 	return s | ||||
| end | ||||
|  | ||||
| @@ -239,20 +249,11 @@ plg.from_formspec_fields = function(fields) | ||||
| 		end | ||||
| 	end | ||||
| 	local function read_action(s) | ||||
| 		if s == nil or s == " " then | ||||
| 			return nil | ||||
| 		for i, data in ipairs(lcore.get_operations()) do | ||||
| 			if data.fs_name == s then | ||||
| 				return data.gate | ||||
| 			end | ||||
| 		end | ||||
| 		local mapping = { | ||||
| 			["AND"] = "and", | ||||
| 			["OR"] = "or", | ||||
| 			["NOT"] = "not", | ||||
| 			["XOR"] = "xor", | ||||
| 			["NAND"] = "nand", | ||||
| 			["="] = "buf", | ||||
| 			["XNOR"] = "xnor", | ||||
| 		} | ||||
| 		s = s:gsub("^%s*", "") -- remove leading spaces | ||||
| 		return mapping[s] | ||||
| 	end | ||||
| 	local is = {} | ||||
| 	for i = 1, 14 do | ||||
| @@ -266,12 +267,11 @@ plg.from_formspec_fields = function(fields) | ||||
| 	return is | ||||
| end | ||||
|  | ||||
| plg.update_formspec = function(pos, is) | ||||
| plg.update_meta = function(pos, is) | ||||
| 	if type(is) == "string" then -- serialized string | ||||
| 		is = lcore.deserialize(is) | ||||
| 	end | ||||
| 	local meta = minetest.get_meta(pos) | ||||
| 	local form = plg.to_formspec_string(is) | ||||
|  | ||||
| 	local err = lcore.validate(is) | ||||
| 	if err == nil then | ||||
| @@ -280,17 +280,20 @@ plg.update_formspec = function(pos, is) | ||||
| 	else | ||||
| 		meta:set_int("valid", 0) | ||||
| 		meta:set_string("infotext", "FPGA") | ||||
| 		local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg)) | ||||
| 		form = form .. plg.red_box_around(err.i) .. | ||||
| 			"label[0.25,8.25;The gate configuration is erroneous in the marked area:]".. | ||||
| 			"label[0.25,8.5;" .. fmsg .. "]" | ||||
| 	end | ||||
|  | ||||
| 	meta:set_string("formspec", form) | ||||
|  | ||||
| 	-- reset ports and run programmed logic | ||||
| 	plg.setports(pos, false, false, false, false) | ||||
| 	plg.update(pos) | ||||
|  | ||||
| 	-- Refresh open formspecs | ||||
| 	local form = plg.to_formspec_string(is, err) | ||||
| 	for name, open_pos in pairs(plg.open_formspecs) do | ||||
| 		if vector.equals(pos, open_pos) then | ||||
| 			minetest.show_formspec(name, "mesecons:fpga", form) | ||||
| 		end | ||||
| 	end | ||||
| 	return err | ||||
| end | ||||
|  | ||||
| plg.red_box_around = function(i) | ||||
| @@ -409,6 +412,38 @@ plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT | ||||
| 	end | ||||
| end | ||||
|  | ||||
| minetest.register_on_player_receive_fields(function(player, formname, fields) | ||||
| 	local player_name = player:get_player_name() | ||||
|  | ||||
| 	if formname ~= "mesecons:fpga" or fields.quit then | ||||
| 		plg.open_formspecs[player_name] = nil -- potential garbage | ||||
| 		return | ||||
| 	end | ||||
| 	if not fields.program then | ||||
| 		return -- we only care when the user clicks "Program" | ||||
| 	end | ||||
| 	local pos = plg.open_formspecs[player_name] | ||||
| 	if minetest.is_protected(pos, player_name) then | ||||
| 		minetest.record_protection_violation(pos, player_name) | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	local meta = minetest.get_meta(pos) | ||||
| 	local is = plg.from_formspec_fields(fields) | ||||
|  | ||||
| 	meta:set_string("instr", lcore.serialize(is)) | ||||
| 	local err = plg.update_meta(pos, is) | ||||
|  | ||||
| 	if not err then | ||||
| 		plg.open_formspecs[player_name] = nil | ||||
| 		-- Close on success | ||||
| 		minetest.close_formspec(player_name, "mesecons:fpga") | ||||
| 	end | ||||
| end) | ||||
|  | ||||
| minetest.register_on_leaveplayer(function(player) | ||||
| 	plg.open_formspecs[player:get_player_name()] = nil | ||||
| end) | ||||
|  | ||||
| minetest.register_craft({ | ||||
| 	output = "mesecons_fpga:fpga0000 2", | ||||
|   | ||||
| @@ -1,5 +1,27 @@ | ||||
|  | ||||
| local lg = {} | ||||
|  | ||||
| local operations = { | ||||
| 	-- table index: Index in the formspec dropdown | ||||
| 	-- gate:    Internal name | ||||
| 	-- short:   Serialized form, single character | ||||
| 	-- fs_name: Display name, padded to 4 characters | ||||
| 	-- func:    Function that applies the operation | ||||
| 	-- unary:   Whether this gate only has one input | ||||
| 	{ gate = "and",  short = "&", fs_name = " AND", func = function(a, b) return a and b end }, | ||||
| 	{ gate = "or",   short = "|", fs_name = "  OR", func = function(a, b) return a or b end }, | ||||
| 	{ gate = "not",  short = "~", fs_name = " NOT", func = function(a, b) return not b end, unary = true }, | ||||
| 	{ gate = "xor",  short = "^", fs_name = " XOR", func = function(a, b) return a ~= b end }, | ||||
| 	{ gate = "nand", short = "?", fs_name = "NAND", func = function(a, b) return not (a and b) end }, | ||||
| 	{ gate = "buf",  short = "_", fs_name = "   =", func = function(a, b) return b end, unary = true }, | ||||
| 	{ gate = "xnor", short = "=", fs_name = "XNOR", func = function(a, b) return a == b end }, | ||||
| 	{ gate = "nor",  short = "!", fs_name = " NOR", func = function(a, b) return not (a or b) end }, | ||||
| } | ||||
|  | ||||
| lg.get_operations = function() | ||||
| 	return operations | ||||
| end | ||||
|  | ||||
| -- (de)serialize | ||||
| lg.serialize = function(t) | ||||
| 	local function _op(t) | ||||
| @@ -11,20 +33,14 @@ lg.serialize = function(t) | ||||
| 			return tostring(t.n) | ||||
| 		end | ||||
| 	end | ||||
| 	local function _action(s) | ||||
| 		if s == nil then | ||||
| 			return " " | ||||
| 	-- Serialize actions (gates) from eg. "and" to "&" | ||||
| 	local function _action(action) | ||||
| 		for i, data in ipairs(operations) do | ||||
| 			if data.gate == action then | ||||
| 				return data.short | ||||
| 			end | ||||
| 		local mapping = { | ||||
| 			["and"] = "&", | ||||
| 			["or"] = "|", | ||||
| 			["not"] = "~", | ||||
| 			["xor"] = "^", | ||||
| 			["nand"] = "?", --dunno | ||||
| 			["buf"] = "_", | ||||
| 			["xnor"] = "=", | ||||
| 		} | ||||
| 		return mapping[s] | ||||
| 		end | ||||
| 		return " " | ||||
| 	end | ||||
|  | ||||
| 	local s = "" | ||||
| @@ -48,18 +64,14 @@ lg.deserialize = function(s) | ||||
| 			return {type = "reg", n = tonumber(c)} | ||||
| 		end | ||||
| 	end | ||||
| 	local function _action(c) | ||||
| 		local mapping = { | ||||
| 			["&"] = "and", | ||||
| 			["|"] = "or", | ||||
| 			["~"] = "not", | ||||
| 			["^"] = "xor", | ||||
| 			["?"] = "nand", | ||||
| 			["_"] = "buf", | ||||
| 			["="] = "xnor", | ||||
| 			[" "] = nil, | ||||
| 		} | ||||
| 		return mapping[c] | ||||
| 	-- Deserialize actions (gates) from eg. "&" to "and" | ||||
| 	local function _action(action) | ||||
| 		for i, data in ipairs(operations) do | ||||
| 			if data.short == action then | ||||
| 				return data.gate | ||||
| 			end | ||||
| 		end | ||||
| 		-- nil | ||||
| 	end | ||||
|  | ||||
| 	local ret = {} | ||||
| @@ -109,16 +121,25 @@ lg.validate_single = function(t, i) | ||||
| 		return false | ||||
| 	end | ||||
| 	local elem = t[i] | ||||
|  | ||||
| 	local gate_data | ||||
| 	for j, data in ipairs(operations) do | ||||
| 		if data.gate == elem.action then | ||||
| 			gate_data = data | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	-- check for completeness | ||||
| 	if elem.action == nil then | ||||
| 		return {i = i, msg = "Gate type required"} | ||||
| 	elseif elem.action == "not" or elem.action == "buf" then | ||||
| 	if not gate_data then | ||||
| 		return {i = i, msg = "Gate type is required"} | ||||
| 	elseif gate_data.unary then | ||||
| 		if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then | ||||
| 			return {i = i, msg = "Second operand (only) and destination required"} | ||||
| 			return {i = i, msg = "Second operand (only) and destination are required"} | ||||
| 		end | ||||
| 	else | ||||
| 		if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then | ||||
| 			return {i = i, msg = "Operands and destination required"} | ||||
| 			return {i = i, msg = "Operands and destination are required"} | ||||
| 		end | ||||
| 	end | ||||
| 	-- check whether operands/destination are identical | ||||
| @@ -159,22 +180,13 @@ end | ||||
| -- interpreter | ||||
| lg.interpret = function(t, a, b, c, d) | ||||
| 	local function _action(s, v1, v2) | ||||
| 		if s == "and" then | ||||
| 			return v1 and v2 | ||||
| 		elseif s == "or" then | ||||
| 			return v1 or v2 | ||||
| 		elseif s == "not" then | ||||
| 			return not v2 | ||||
| 		elseif s == "xor" then | ||||
| 			return v1 ~= v2 | ||||
| 		elseif s == "nand" then | ||||
| 			return not (v1 and v2) | ||||
| 		elseif s == "buf" then | ||||
| 			return v2 | ||||
| 		else -- s == "xnor" | ||||
| 			return v1 == v2 | ||||
| 		for i, data in ipairs(operations) do | ||||
| 			if data.gate == s then | ||||
| 				return data.func(v1, v2) | ||||
| 			end | ||||
| 		end | ||||
| 		return false -- unknown gate | ||||
| 	end | ||||
| 	local function _op(t, regs, io_in) | ||||
| 		if t.type == "reg" then | ||||
| 			return regs[t.n] | ||||
|   | ||||
| Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 256 B | 
| Before Width: | Height: | Size: 536 B After Width: | Height: | Size: 480 B | 
| Before Width: | Height: | Size: 816 B After Width: | Height: | Size: 760 B | 
| @@ -34,17 +34,22 @@ minetest.register_tool("mesecons_fpga:programmer", { | ||||
| 		if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then | ||||
| 			return itemstack | ||||
| 		end | ||||
| 		local player_name = user:get_player_name() | ||||
| 		if minetest.is_protected(pos, player_name) then | ||||
| 			minetest.record_protection_violation(pos, player_name) | ||||
| 			return itemstack | ||||
| 		end | ||||
|  | ||||
| 		local imeta = itemstack:get_metadata() | ||||
| 		if imeta == "" then | ||||
| 			minetest.chat_send_player(user:get_player_name(), "Use shift+right-click to copy a gate configuration first.") | ||||
| 			minetest.chat_send_player(player_name, "Use shift+right-click to copy a gate configuration first.") | ||||
| 			return itemstack | ||||
| 		end | ||||
|  | ||||
| 		local meta = minetest.get_meta(pos) | ||||
| 		meta:set_string("instr", imeta) | ||||
| 		plg.update_formspec(pos, imeta) | ||||
| 		minetest.chat_send_player(user:get_player_name(), "Gate configuration was successfully written to FPGA!") | ||||
| 		plg.update_meta(pos, imeta) | ||||
| 		minetest.chat_send_player(player_name, "Gate configuration was successfully written to FPGA!") | ||||
|  | ||||
| 		return itemstack | ||||
| 	end | ||||
|   | ||||
| Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 52 KiB | 
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 54 KiB | 
| Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 53 KiB | 
| Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 53 KiB | 
| Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.6 KiB | 
| @@ -1,6 +1,14 @@ | ||||
| local selection_box = { | ||||
| 	type = "fixed", | ||||
| 	fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 } | ||||
| } | ||||
|  | ||||
| local nodebox = { | ||||
| 	type = "fixed", | ||||
| 	fixed = {{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }}, | ||||
| 	fixed = { | ||||
| 		{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab | ||||
| 		{ -6/16, -7/16, -6/16, 6/16, -6/16, 6/16 } | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| local function gate_rotate_rules(node, rules) | ||||
| @@ -68,7 +76,7 @@ local function register_gate(name, inputnumber, assess, recipe, description) | ||||
| 		is_ground_content = false, | ||||
| 		drawtype = "nodebox", | ||||
| 		drop = basename.."_off", | ||||
| 		selection_box = nodebox, | ||||
| 		selection_box = selection_box, | ||||
| 		node_box = nodebox, | ||||
| 		walkable = true, | ||||
| 		sounds = default.node_sound_stone_defaults(), | ||||
| @@ -78,8 +86,16 @@ local function register_gate(name, inputnumber, assess, recipe, description) | ||||
| 		inputnumber = inputnumber, | ||||
| 		after_dig_node = mesecon.do_cooldown, | ||||
| 	},{ | ||||
| 		tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^".. | ||||
| 			"jeija_gate_"..name..".png"}, | ||||
| 		tiles = { | ||||
| 			"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^".. | ||||
| 			"jeija_gate_output_off.png^".."jeija_gate_"..name..".png", | ||||
| 			"jeija_microcontroller_bottom.png^".."jeija_gate_output_off.png^".. | ||||
| 			"[transformFY", | ||||
| 			"jeija_gate_side.png^".."jeija_gate_side_output_off.png", | ||||
| 			"jeija_gate_side.png", | ||||
| 			"jeija_gate_side.png", | ||||
| 			"jeija_gate_side.png" | ||||
| 		}, | ||||
| 		groups = {dig_immediate = 2, overheat = 1}, | ||||
| 		mesecons = { receptor = { | ||||
| 			state = "off", | ||||
| @@ -89,8 +105,16 @@ local function register_gate(name, inputnumber, assess, recipe, description) | ||||
| 			action_change = update_gate | ||||
| 		}} | ||||
| 	},{ | ||||
| 		tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_on.png^".. | ||||
| 			"jeija_gate_"..name..".png"}, | ||||
| 		tiles = { | ||||
| 			"jeija_microcontroller_bottom.png^".."jeija_gate_on.png^".. | ||||
| 			"jeija_gate_output_on.png^".."jeija_gate_"..name..".png", | ||||
| 			"jeija_microcontroller_bottom.png^".."jeija_gate_output_on.png^".. | ||||
| 			"[transformFY", | ||||
| 			"jeija_gate_side.png^".."jeija_gate_side_output_on.png", | ||||
| 			"jeija_gate_side.png", | ||||
| 			"jeija_gate_side.png", | ||||
| 			"jeija_gate_side.png" | ||||
| 		}, | ||||
| 		groups = {dig_immediate = 2, not_in_creative_inventory = 1, overheat = 1}, | ||||
| 		mesecons = { receptor = { | ||||
| 			state = "on", | ||||
|   | ||||
| Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 123 B | 
| Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 128 B | 
| Before Width: | Height: | Size: 251 B After Width: | Height: | Size: 127 B | 
| Before Width: | Height: | Size: 251 B After Width: | Height: | Size: 127 B | 
| Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 128 B | 
| Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 84 B | 
| Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 84 B | 
| Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 127 B | 
							
								
								
									
										
											BIN
										
									
								
								mesecons_gates/textures/jeija_gate_output_off.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 98 B | 
							
								
								
									
										
											BIN
										
									
								
								mesecons_gates/textures/jeija_gate_output_on.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 99 B | 
							
								
								
									
										
											BIN
										
									
								
								mesecons_gates/textures/jeija_gate_side.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 136 B | 
							
								
								
									
										
											BIN
										
									
								
								mesecons_gates/textures/jeija_gate_side_output_off.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 109 B | 
							
								
								
									
										
											BIN
										
									
								
								mesecons_gates/textures/jeija_gate_side_output_on.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 110 B | 
| Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 128 B |