Compare commits
	
		
			33 Commits
		
	
	
		
			302a28934d
			...
			mvps-ignor
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a38f37d1ee | ||
|  | d356f901a3 | ||
|  | 6921909100 | ||
|  | 3202bf6786 | ||
|  | fedbf49372 | ||
|  | 9fda51b650 | ||
|  | 4750925eab | ||
|  | 16836b16d6 | ||
|  | c1eccba247 | ||
|  | 01332a7ba1 | ||
|  | de4eeebd3b | ||
|  | 75bd1e8be6 | ||
|  | 95e6e2e4a5 | ||
|  | 168a923665 | ||
|  | 8dec053996 | ||
|  | 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), | ||||
| 				func=func, | ||||
| 				params=mesecon.tablecopy(params or {}), | ||||
| 				time=time, | ||||
| 				owcheck=(overwritecheck and mesecon.tablecopy(overwritecheck)) or nil, | ||||
| 				priority=priority} | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	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 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 = {} | ||||
|  | ||||
| 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 actions_now={} | ||||
|  | ||||
| 	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 | ||||
| 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) | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| default | ||||
							
								
								
									
										2
									
								
								mesecons/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons | ||||
| depends = default | ||||
| 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,29 +186,30 @@ 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 | ||||
| 		end | ||||
| function mesecon.tablecopy(obj) -- deep copy | ||||
| 	if type(obj) == "table" then | ||||
| 		return table.copy(obj) | ||||
| 	end | ||||
|  | ||||
| 	return newtable | ||||
| 	return obj | ||||
| end | ||||
|  | ||||
| -- Returns whether two values are equal. | ||||
| -- In tables, keys are compared for identity but values are compared recursively. | ||||
| -- There is no protection from infinite recursion. | ||||
| function mesecon.cmpAny(t1, t2) | ||||
| 	if type(t1) ~= type(t2) then return false end | ||||
| 	if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end | ||||
| 	if type(t1) ~= "table" then return t1 == t2 end | ||||
|  | ||||
| 	-- Check that for each key of `t1` both tables have the same value | ||||
| 	for i, e in pairs(t1) do | ||||
| 		if not mesecon.cmpAny(e, t2[i]) then return false end | ||||
| 	end | ||||
|  | ||||
| 	-- Check that all keys of `t2` are also keys of `t1` so were checked in the previous loop | ||||
| 	for i, _ in pairs(t2) do | ||||
| 		if t1[i] == nil then return false end | ||||
| 	end | ||||
|  | ||||
| 	return true | ||||
| end | ||||
|  | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| mesecons | ||||
							
								
								
									
										2
									
								
								mesecons_alias/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_alias | ||||
| depends = mesecons | ||||
| @@ -1 +0,0 @@ | ||||
| mesecons | ||||
							
								
								
									
										
											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 | 
							
								
								
									
										2
									
								
								mesecons_blinkyplant/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_blinkyplant | ||||
| depends = mesecons | ||||
| Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 367 B | 
| Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 394 B | 
| @@ -1,2 +0,0 @@ | ||||
| mesecons | ||||
| mesecons_receiver | ||||
| 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 | 
| @@ -8,7 +8,7 @@ mesecon.button_turnoff = function (pos) | ||||
| 		return | ||||
| 	end | ||||
| 	minetest.swap_node(pos, {name = "mesecons_button:button_off", param2 = node.param2}) | ||||
| 	minetest.sound_play("mesecons_button_pop", {pos = pos}) | ||||
| 	minetest.sound_play("mesecons_button_pop", { pos = pos }, true) | ||||
| 	local rules = mesecon.rules.buttonlike_get(node) | ||||
| 	mesecon.receptor_off(pos, rules) | ||||
| end | ||||
| @@ -46,7 +46,7 @@ minetest.register_node("mesecons_button:button_off", { | ||||
| 	on_rightclick = function (pos, node) | ||||
| 		minetest.swap_node(pos, {name = "mesecons_button:button_on", param2=node.param2}) | ||||
| 		mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node)) | ||||
| 		minetest.sound_play("mesecons_button_push", {pos=pos}) | ||||
| 		minetest.sound_play("mesecons_button_push", { pos = pos }, true) | ||||
| 		minetest.get_node_timer(pos):start(1) | ||||
| 	end, | ||||
| 	sounds = default.node_sound_stone_defaults(), | ||||
|   | ||||
							
								
								
									
										2
									
								
								mesecons_button/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_button | ||||
| depends = mesecons, mesecons_receiver | ||||
| 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 | 
| @@ -1 +0,0 @@ | ||||
| mesecons | ||||
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 33 KiB | 
| @@ -110,7 +110,7 @@ local function resolve_commands(commands, pos) | ||||
| 	local nearest, farthest = nil, nil | ||||
| 	local min_distance, max_distance = math.huge, -1 | ||||
| 	for index, player in pairs(players) do | ||||
| 		local distance = vector.distance(pos, player:getpos()) | ||||
| 		local distance = vector.distance(pos, player:get_pos()) | ||||
| 		if distance < min_distance then | ||||
| 			min_distance = distance | ||||
| 			nearest = player:get_player_name() | ||||
| @@ -174,7 +174,8 @@ end | ||||
| local function can_dig(pos, player) | ||||
| 	local meta = minetest.get_meta(pos) | ||||
| 	local owner = meta:get_string("owner") | ||||
| 	return owner == "" or owner == player:get_player_name() | ||||
| 	return owner == "" or owner == player:get_player_name() or | ||||
| 		minetest.check_player_privs(player, "protection_bypass") | ||||
| end | ||||
|  | ||||
| minetest.register_node("mesecons_commandblock:commandblock_off", { | ||||
|   | ||||
							
								
								
									
										2
									
								
								mesecons_commandblock/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_commandblock | ||||
| depends = mesecons | ||||
| Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 183 B | 
| Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 183 B | 
| @@ -1 +0,0 @@ | ||||
| mesecons | ||||
| 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,19 +51,9 @@ 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", | ||||
| 	tiles = { | ||||
| 		"mesecons_delayer_off_"..tostring(i)..".png", | ||||
| 		"mesecons_delayer_bottom.png", | ||||
| 		"mesecons_delayer_ends_off.png", | ||||
| 		"mesecons_delayer_ends_off.png", | ||||
| 		"mesecons_delayer_sides_off.png", | ||||
| 		"mesecons_delayer_sides_off.png" | ||||
| 		}, | ||||
| 	inventory_image = "mesecons_delayer_off_1.png", | ||||
| 	wield_image = "mesecons_delayer_off_1.png", | ||||
| 	walkable = true, | ||||
| 	selection_box = { | ||||
| 		type = "fixed", | ||||
| @@ -83,26 +63,46 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { | ||||
| 		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}) | ||||
| 		end | ||||
| 	end, | ||||
| 	delayer_time = delaytime, | ||||
| 	delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i), | ||||
| 	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", | ||||
| 		"mesecons_delayer_ends_off.png", | ||||
| 		"mesecons_delayer_ends_off.png", | ||||
| 		"mesecons_delayer_sides_off.png", | ||||
| 		"mesecons_delayer_sides_off.png" | ||||
| 	}, | ||||
| 	inventory_image = "mesecons_delayer_off_1.png", | ||||
| 	wield_image = "mesecons_delayer_off_1.png", | ||||
| 	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_onstate = "mesecons_delayer:delayer_on_"..tostring(i), | ||||
| 	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", | ||||
| @@ -129,36 +131,19 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { | ||||
| 		"mesecons_delayer_ends_on.png", | ||||
| 		"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({ | ||||
|   | ||||
							
								
								
									
										2
									
								
								mesecons_delayer/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_delayer | ||||
| depends = mesecons | ||||
| 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,2 +0,0 @@ | ||||
| mesecons | ||||
| mesecons_materials | ||||
| @@ -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,28 +189,49 @@ 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 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) | ||||
| 			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 | ||||
| 				meta:set_string("scanname", msg) | ||||
| 				node_detector_make_formspec(pos) | ||||
| 				if msg == GET_COMMAND then | ||||
| 					node_detector_send_node_name(pos, node, channel, meta) | ||||
| 				else | ||||
| 					meta:set_string("scanname", msg) | ||||
| 					node_detector_make_formspec(pos) | ||||
| 				end | ||||
| 			end | ||||
| 		end, | ||||
| 	}, | ||||
| @@ -218,7 +239,7 @@ local node_detector_digiline = { | ||||
| } | ||||
|  | ||||
| local function after_place_node_detector(pos, placer) | ||||
| 	local placer_pos = placer:getpos() | ||||
| 	local placer_pos = placer:get_pos() | ||||
| 	if not placer_pos then | ||||
| 		return | ||||
| 	end | ||||
|   | ||||
							
								
								
									
										2
									
								
								mesecons_detector/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| name = mesecons_detector | ||||
| depends = mesecons, mesecons_materials | ||||
| 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 | 
| @@ -1,2 +0,0 @@ | ||||
| mesecons | ||||
| doors | ||||
| @@ -13,9 +13,9 @@ local function on_rightclick(pos, dir, check_name, replace, replace_dir, params) | ||||
| 	minetest.swap_node(pos, {name = replace, param2 = p2}) | ||||
|  | ||||
| 	if (minetest.get_meta(pos):get_int("right") ~= 0) == (params[1] ~= 3) then | ||||
| 		minetest.sound_play("doors_door_close", {pos = pos, gain = 0.3, max_hear_distance = 10}) | ||||
| 		minetest.sound_play("doors_door_close", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true) | ||||
| 	else | ||||
| 		minetest.sound_play("doors_door_open", {pos = pos, gain = 0.3, max_hear_distance = 10}) | ||||
| 		minetest.sound_play("doors_door_open", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| @@ -73,16 +73,17 @@ meseconify_door("doors:door_wood") | ||||
| meseconify_door("doors:door_steel") | ||||
| meseconify_door("doors:door_glass") | ||||
| meseconify_door("doors:door_obsidian_glass") | ||||
| meseconify_door("xpanes:door_steel_bar") | ||||
|  | ||||
| -- Trapdoor | ||||
| local function trapdoor_switch(pos, node) | ||||
| 	local state = minetest.get_meta(pos):get_int("state") | ||||
|  | ||||
| 	if state == 1 then | ||||
| 		minetest.sound_play("doors_door_close", {pos = pos, gain = 0.3, max_hear_distance = 10}) | ||||
| 		minetest.sound_play("doors_door_close", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true) | ||||
| 		minetest.set_node(pos, {name="doors:trapdoor", param2 = node.param2}) | ||||
| 	else | ||||
| 		minetest.sound_play("doors_door_open", {pos = pos, gain = 0.3, max_hear_distance = 10}) | ||||
| 		minetest.sound_play("doors_door_open", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true) | ||||
| 		minetest.set_node(pos, {name="doors:trapdoor_open", param2 = node.param2}) | ||||
| 	end | ||||
|  | ||||
| @@ -110,6 +111,12 @@ if doors and doors.get then | ||||
| 	minetest.override_item("doors:trapdoor_open", override) | ||||
| 	minetest.override_item("doors:trapdoor_steel", override) | ||||
| 	minetest.override_item("doors:trapdoor_steel_open", override) | ||||
|  | ||||
| 	if minetest.registered_items["xpanes:trapdoor_steel_bar"] then | ||||
| 		minetest.override_item("xpanes:trapdoor_steel_bar", override) | ||||
| 		minetest.override_item("xpanes:trapdoor_steel_bar_open", override) | ||||
| 	end | ||||
|  | ||||
| else | ||||
| 	if minetest.registered_nodes["doors:trapdoor"] then | ||||
| 		minetest.override_item("doors:trapdoor", { | ||||
|   | ||||
							
								
								
									
										3
									
								
								mesecons_doors/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| name = mesecons_doors | ||||
| depends = mesecons, doors | ||||
| optional_depends = xpanes | ||||
| @@ -1,12 +1,5 @@ | ||||
| local screwdriver_exists = minetest.global_exists("screwdriver") | ||||
|  | ||||
| local corner_nodebox = { | ||||
| 	type = "fixed", | ||||
| 	-- ±0.001 is to prevent z-fighting | ||||
| 	fixed = {{ -16/32-0.001, -17/32, -3/32, 0, -13/32, 3/32 }, | ||||
| 		   { -3/32, -17/32, -16/32+0.001, 3/32, -13/32, 3/32}} | ||||
| } | ||||
|  | ||||
| local corner_selectionbox = { | ||||
| 		type = "fixed", | ||||
| 		fixed = { -16/32, -16/32, -16/32, 5/32, -12/32, 5/32 }, | ||||
| @@ -25,14 +18,11 @@ local corner_get_rules = function (node) | ||||
| end | ||||
|  | ||||
| minetest.register_node("mesecons_extrawires:corner_on", { | ||||
| 	drawtype = "nodebox", | ||||
| 	drawtype = "mesh", | ||||
| 	mesh = "mesecons_extrawires_corner.obj", | ||||
| 	tiles = { | ||||
| 		"jeija_insulated_wire_curved_tb_on.png", | ||||
| 		"jeija_insulated_wire_curved_tb_on.png^[transformR270", | ||||
| 		"jeija_insulated_wire_sides_on.png", | ||||
| 		"jeija_insulated_wire_ends_on.png", | ||||
| 		"jeija_insulated_wire_sides_on.png", | ||||
| 		"jeija_insulated_wire_ends_on.png" | ||||
| 		{ name = "jeija_insulated_wire_sides_on.png", backface_culling = true }, | ||||
| 		{ name = "jeija_insulated_wire_ends_on.png", backface_culling = true }, | ||||
| 	}, | ||||
| 	paramtype = "light", | ||||
| 	paramtype2 = "facedir", | ||||
| @@ -55,15 +45,12 @@ minetest.register_node("mesecons_extrawires:corner_on", { | ||||
| }) | ||||
|  | ||||
| minetest.register_node("mesecons_extrawires:corner_off", { | ||||
| 	drawtype = "nodebox", | ||||
| 	drawtype = "mesh", | ||||
| 	description = "Insulated Mesecon Corner", | ||||
| 	mesh = "mesecons_extrawires_corner.obj", | ||||
| 	tiles = { | ||||
| 		"jeija_insulated_wire_curved_tb_off.png", | ||||
| 		"jeija_insulated_wire_curved_tb_off.png^[transformR270", | ||||
| 		"jeija_insulated_wire_sides_off.png", | ||||
| 		"jeija_insulated_wire_ends_off.png", | ||||
| 		"jeija_insulated_wire_sides_off.png", | ||||
| 		"jeija_insulated_wire_ends_off.png" | ||||
| 		{ name = "jeija_insulated_wire_sides_off.png", backface_culling = true }, | ||||
| 		{ name = "jeija_insulated_wire_ends_off.png", backface_culling = true }, | ||||
| 	}, | ||||
| 	paramtype = "light", | ||||
| 	paramtype2 = "facedir", | ||||
| @@ -87,8 +74,7 @@ minetest.register_node("mesecons_extrawires:corner_off", { | ||||
| minetest.register_craft({ | ||||
| 	output = "mesecons_extrawires:corner_off 3", | ||||
| 	recipe = { | ||||
| 		{"", "", ""}, | ||||
| 		{"mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off", ""}, | ||||
| 		{"", "mesecons_insulated:insulated_off", ""}, | ||||
| 		{"mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off"}, | ||||
| 		{"", "mesecons_insulated:insulated_off"}, | ||||
| 	} | ||||
| }) | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| default | ||||
| mesecons | ||||
| screwdriver? | ||||
| 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 | 
							
								
								
									
										91
									
								
								mesecons_extrawires/doublecorner.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| local rotate | ||||
| if minetest.global_exists("screwdriver") then rotate = screwdriver.rotate_simple end | ||||
|  | ||||
| local doublecorner_selectionbox = { | ||||
| 	type = "fixed", | ||||
| 	fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, | ||||
| } | ||||
|  | ||||
| local rules = { | ||||
| 	{ | ||||
| 		{ x = 1, y = 0, z = 0 }, | ||||
| 		{ x = 0, y = 0, z = 1 }, | ||||
| 	}, | ||||
| 	{ | ||||
| 		{ x = -1, y = 0, z = 0 }, | ||||
| 		{ x = 0, y = 0, z = -1 }, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| local doublecorner_rules = {} | ||||
| for k = 1, 4 do | ||||
| 	doublecorner_rules[k] = table.copy(rules) | ||||
| 	for i, r in ipairs(rules) do | ||||
| 		rules[i] = mesecon.rotate_rules_left(r) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local function doublecorner_get_rules(node) | ||||
| 	return doublecorner_rules[node.param2 % 4 + 1] | ||||
| end | ||||
|  | ||||
| local doublecorner_states = { | ||||
| 	"mesecons_extrawires:doublecorner_00", | ||||
| 	"mesecons_extrawires:doublecorner_01", | ||||
| 	"mesecons_extrawires:doublecorner_10", | ||||
| 	"mesecons_extrawires:doublecorner_11", | ||||
| } | ||||
| local wire1_states = { "off", "off", "on", "on" } | ||||
| local wire2_states = { "off", "on", "off", "on" } | ||||
|  | ||||
| for k, state in ipairs(doublecorner_states) do | ||||
| 	local w1 = wire1_states[k] | ||||
| 	local w2 = wire2_states[k] | ||||
| 	local groups =  { dig_immediate = 3 } | ||||
| 	if k ~= 1 then groups.not_in_creative_inventory = 1 end | ||||
| 	minetest.register_node(state, { | ||||
| 		drawtype = "mesh", | ||||
| 		mesh = "mesecons_extrawires_doublecorner.obj", | ||||
| 		description = "Insulated Mesecon Double Corner", | ||||
| 		tiles = { | ||||
| 			{ name = "jeija_insulated_wire_sides_" .. w1 .. ".png", backface_culling = true }, | ||||
| 			{ name = "jeija_insulated_wire_ends_" .. w1 .. ".png", backface_culling = true }, | ||||
| 			{ name = "jeija_insulated_wire_sides_" .. w2 .. ".png", backface_culling = true }, | ||||
| 			{ name = "jeija_insulated_wire_ends_" .. w2 .. ".png", backface_culling = true }, | ||||
| 		}, | ||||
| 		paramtype = "light", | ||||
| 		paramtype2 = "facedir", | ||||
| 		is_ground_content = false, | ||||
| 		walkable = false, | ||||
| 		sunlight_propagates = true, | ||||
| 		selection_box = doublecorner_selectionbox, | ||||
| 		groups = groups, | ||||
| 		drop = doublecorner_states[1], | ||||
| 		sounds = default.node_sound_defaults(), | ||||
| 		mesecons = { | ||||
| 			conductor = { | ||||
| 				states = doublecorner_states, | ||||
| 				rules = doublecorner_get_rules, | ||||
| 			}, | ||||
| 		}, | ||||
| 		on_blast = mesecon.on_blastnode, | ||||
| 		on_rotate = rotate, | ||||
| 	}) | ||||
| end | ||||
|  | ||||
| minetest.register_craft({ | ||||
| 	type = "shapeless", | ||||
| 	output = "mesecons_extrawires:doublecorner_00", | ||||
| 	recipe = { | ||||
| 		"mesecons_extrawires:corner_off", | ||||
| 		"mesecons_extrawires:corner_off", | ||||
| 	}, | ||||
| }) | ||||
|  | ||||
| minetest.register_craft({ | ||||
| 	type = "shapeless", | ||||
| 	output = "mesecons_extrawires:corner_off 2", | ||||
| 	recipe = { | ||||
| 		"mesecons_extrawires:doublecorner_00", | ||||
| 	}, | ||||
| }) | ||||
| @@ -1,5 +1,6 @@ | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/crossover.lua"); | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/tjunction.lua"); | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/corner.lua"); | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/doublecorner.lua"); | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/vertical.lua"); | ||||
| dofile(minetest.get_modpath("mesecons_extrawires").."/mesewire.lua"); | ||||
|   | ||||
							
								
								
									
										3
									
								
								mesecons_extrawires/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| name = mesecons_extrawires | ||||
| depends = default, mesecons | ||||
| optional_depends = screwdriver | ||||
							
								
								
									
										125
									
								
								mesecons_extrawires/models/mesecons_extrawires_corner.obj
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,125 @@ | ||||
| # Вершины | ||||
| # Провод 1 | ||||
| # 1 (ниж. внутр.) | ||||
| v 0.093750 -0.531250 -0.501000 | ||||
| v 0.093750 -0.531250 -0.331726 | ||||
| v 0.331726 -0.531250 -0.093750 | ||||
| v 0.501000 -0.531250 -0.093750 | ||||
| # 5 (ниж. наруж.) | ||||
| v -0.093750 -0.531250 -0.501000 | ||||
| v -0.093750 -0.531250 -0.254061 | ||||
| v 0.254061 -0.531250 0.093750 | ||||
| v 0.501000 -0.531250 0.093750 | ||||
| # 9 (верх. внутр.) | ||||
| v 0.093750 -0.406250 -0.501000 | ||||
| v 0.093750 -0.406250 -0.331726 | ||||
| v 0.331726 -0.406250 -0.093750 | ||||
| v 0.501000 -0.406250 -0.093750 | ||||
| # 13 (верх. наруж.) | ||||
| v -0.093750 -0.406250 -0.501000 | ||||
| v -0.093750 -0.406250 -0.254061 | ||||
| v 0.254061 -0.406250 0.093750 | ||||
| v 0.501000 -0.406250 0.093750 | ||||
| # Текстурные координаты | ||||
| # 1 (ниж.) | ||||
| vt 0.000000 0.406250 | ||||
| vt 0.168274 0.406250 | ||||
| vt 0.331726 0.406250 | ||||
| vt 0.668274 0.406250 | ||||
| vt 0.831726 0.406250 | ||||
| vt 1.000000 0.406250 | ||||
| vt 0.000000 0.593750 | ||||
| vt 0.245939 0.593750 | ||||
| vt 0.254061 0.593750 | ||||
| vt 0.745939 0.593750 | ||||
| vt 0.754061 0.593750 | ||||
| vt 1.000000 0.593750 | ||||
| # 13 (верх.) | ||||
| vt 0.000000 0.406250 | ||||
| vt 0.245939 0.406250 | ||||
| vt 0.254061 0.406250 | ||||
| vt 0.745939 0.406250 | ||||
| vt 0.754061 0.406250 | ||||
| vt 1.000000 0.406250 | ||||
| vt 0.000000 0.593750 | ||||
| vt 0.168274 0.593750 | ||||
| vt 0.331726 0.593750 | ||||
| vt 0.668274 0.593750 | ||||
| vt 0.831726 0.593750 | ||||
| vt 1.000000 0.593750 | ||||
| # 25 (внутр.) | ||||
| vt 1.000000 0.093750 | ||||
| vt 0.831726 0.093750 | ||||
| vt 0.668274 0.093750 | ||||
| vt 0.331726 0.093750 | ||||
| vt 0.168274 0.093750 | ||||
| vt 0.000000 0.093750 | ||||
| vt 1.000000 -0.031250 | ||||
| vt 0.831726 -0.031250 | ||||
| vt 0.668274 -0.031250 | ||||
| vt 0.331726 -0.031250 | ||||
| vt 0.168274 -0.031250 | ||||
| vt 0.000000 -0.031250 | ||||
| # 37 (внеш.) | ||||
| vt 0.000000 -0.031250 | ||||
| vt 0.245939 -0.031250 | ||||
| vt 0.254061 -0.031250 | ||||
| vt 0.745939 -0.031250 | ||||
| vt 0.754061 -0.031250 | ||||
| vt 1.000000 -0.031250 | ||||
| vt 0.000000 0.093750 | ||||
| vt 0.245939 0.093750 | ||||
| vt 0.254061 0.093750 | ||||
| vt 0.745939 0.093750 | ||||
| vt 0.754061 0.093750 | ||||
| vt 1.000000 0.093750 | ||||
| # 49 (торец) | ||||
| vt 0.406250 -0.031250 | ||||
| vt 0.406250 0.093750 | ||||
| vt 0.593750 -0.031250 | ||||
| vt 0.593750 0.093750 | ||||
| # Нормали | ||||
| # 1 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.707107 0.000000 0.707107 | ||||
| # 5 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn -0.707107 0.000000 -0.707107 | ||||
| # Грани | ||||
| g Sides1 | ||||
| # Часть 1 | ||||
| f 5/37/1 6/38/1 14/44/1 | ||||
| f 5/37/1 14/44/1 13/43/1 | ||||
| f 13/13/2 14/14/2 10/20/2 | ||||
| f 13/13/2 10/20/2 9/19/2 | ||||
| f 9/25/5 10/26/5 2/32/5 | ||||
| f 9/25/5 2/32/5 1/31/5 | ||||
| f 1/1/6 2/2/6 6/8/6 | ||||
| f 1/1/6 6/8/6 5/7/6 | ||||
| # Часть 2 | ||||
| f 6/39/4 7/40/4 15/46/4 | ||||
| f 6/39/4 15/46/4 14/45/4 | ||||
| f 14/15/2 15/16/2 11/22/2 | ||||
| f 14/15/2 11/22/2 10/21/2 | ||||
| f 10/27/8 11/28/8 3/34/8 | ||||
| f 10/27/8 3/34/8 2/33/8 | ||||
| f 2/3/6 3/4/6 7/10/6 | ||||
| f 2/3/6 7/10/6 6/9/6 | ||||
| # Часть 3 | ||||
| f 7/41/3 8/42/3 16/48/3 | ||||
| f 7/41/3 16/48/3 15/47/3 | ||||
| f 15/17/2 16/18/2 12/24/2 | ||||
| f 15/17/2 12/24/2 11/23/2 | ||||
| f 11/29/7 12/30/7 4/36/7 | ||||
| f 11/29/7 4/36/7 3/35/7 | ||||
| f 3/5/6 4/6/6 8/12/6 | ||||
| f 3/5/6 8/12/6 7/11/6 | ||||
| g Ends1 | ||||
| f 1/49/3 5/51/3 13/52/3 | ||||
| f 1/49/3 13/52/3 9/50/3 | ||||
| f 4/49/1 12/50/1 16/52/1 | ||||
| f 4/49/1 16/52/1 8/51/1 | ||||
							
								
								
									
										180
									
								
								mesecons_extrawires/models/mesecons_extrawires_doublecorner.obj
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,180 @@ | ||||
| # Вершины | ||||
| # Провод 1 | ||||
| # 1 (ниж. внутр.) | ||||
| v 0.093750 -0.531250 -0.501000 | ||||
| v 0.093750 -0.531250 -0.331726 | ||||
| v 0.331726 -0.531250 -0.093750 | ||||
| v 0.501000 -0.531250 -0.093750 | ||||
| # 5 (ниж. наруж.) | ||||
| v -0.093750 -0.531250 -0.501000 | ||||
| v -0.093750 -0.531250 -0.254061 | ||||
| v 0.254061 -0.531250 0.093750 | ||||
| v 0.501000 -0.531250 0.093750 | ||||
| # 9 (верх. внутр.) | ||||
| v 0.093750 -0.406250 -0.501000 | ||||
| v 0.093750 -0.406250 -0.331726 | ||||
| v 0.331726 -0.406250 -0.093750 | ||||
| v 0.501000 -0.406250 -0.093750 | ||||
| # 13 (верх. наруж.) | ||||
| v -0.093750 -0.406250 -0.501000 | ||||
| v -0.093750 -0.406250 -0.254061 | ||||
| v 0.254061 -0.406250 0.093750 | ||||
| v 0.501000 -0.406250 0.093750 | ||||
| # Провод 2 | ||||
| # 17 (ниж. внутр.) | ||||
| v -0.093750 -0.531250 0.501000 | ||||
| v -0.093750 -0.531250 0.331726 | ||||
| v -0.331726 -0.531250 0.093750 | ||||
| v -0.501000 -0.531250 0.093750 | ||||
| # 21 (ниж. наруж.) | ||||
| v 0.093750 -0.531250 0.501000 | ||||
| v 0.093750 -0.531250 0.254061 | ||||
| v -0.254061 -0.531250 -0.093750 | ||||
| v -0.501000 -0.531250 -0.093750 | ||||
| # 25 (верх. внутр.) | ||||
| v -0.093750 -0.406250 0.501000 | ||||
| v -0.093750 -0.406250 0.331726 | ||||
| v -0.331726 -0.406250 0.093750 | ||||
| v -0.501000 -0.406250 0.093750 | ||||
| # 29 (верх. наруж.) | ||||
| v 0.093750 -0.406250 0.501000 | ||||
| v 0.093750 -0.406250 0.254061 | ||||
| v -0.254061 -0.406250 -0.093750 | ||||
| v -0.501000 -0.406250 -0.093750 | ||||
| # Текстурные координаты | ||||
| # 1 (ниж.) | ||||
| vt 0.000000 0.406250 | ||||
| vt 0.168274 0.406250 | ||||
| vt 0.331726 0.406250 | ||||
| vt 0.668274 0.406250 | ||||
| vt 0.831726 0.406250 | ||||
| vt 1.000000 0.406250 | ||||
| vt 0.000000 0.593750 | ||||
| vt 0.245939 0.593750 | ||||
| vt 0.254061 0.593750 | ||||
| vt 0.745939 0.593750 | ||||
| vt 0.754061 0.593750 | ||||
| vt 1.000000 0.593750 | ||||
| # 13 (верх.) | ||||
| vt 0.000000 0.406250 | ||||
| vt 0.245939 0.406250 | ||||
| vt 0.254061 0.406250 | ||||
| vt 0.745939 0.406250 | ||||
| vt 0.754061 0.406250 | ||||
| vt 1.000000 0.406250 | ||||
| vt 0.000000 0.593750 | ||||
| vt 0.168274 0.593750 | ||||
| vt 0.331726 0.593750 | ||||
| vt 0.668274 0.593750 | ||||
| vt 0.831726 0.593750 | ||||
| vt 1.000000 0.593750 | ||||
| # 25 (внутр.) | ||||
| vt 1.000000 0.093750 | ||||
| vt 0.831726 0.093750 | ||||
| vt 0.668274 0.093750 | ||||
| vt 0.331726 0.093750 | ||||
| vt 0.168274 0.093750 | ||||
| vt 0.000000 0.093750 | ||||
| vt 1.000000 -0.031250 | ||||
| vt 0.831726 -0.031250 | ||||
| vt 0.668274 -0.031250 | ||||
| vt 0.331726 -0.031250 | ||||
| vt 0.168274 -0.031250 | ||||
| vt 0.000000 -0.031250 | ||||
| # 37 (внеш.) | ||||
| vt 0.000000 -0.031250 | ||||
| vt 0.245939 -0.031250 | ||||
| vt 0.254061 -0.031250 | ||||
| vt 0.745939 -0.031250 | ||||
| vt 0.754061 -0.031250 | ||||
| vt 1.000000 -0.031250 | ||||
| vt 0.000000 0.093750 | ||||
| vt 0.245939 0.093750 | ||||
| vt 0.254061 0.093750 | ||||
| vt 0.745939 0.093750 | ||||
| vt 0.754061 0.093750 | ||||
| vt 1.000000 0.093750 | ||||
| # 49 (торец) | ||||
| vt 0.406250 -0.031250 | ||||
| vt 0.406250 0.093750 | ||||
| vt 0.593750 -0.031250 | ||||
| vt 0.593750 0.093750 | ||||
| # Нормали | ||||
| # 1 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.707107 0.000000 0.707107 | ||||
| # 5 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn -0.707107 0.000000 -0.707107 | ||||
| # Грани | ||||
| # Грани | ||||
| g Sides1 | ||||
| # Часть 1 | ||||
| f 5/37/1 6/38/1 14/44/1 | ||||
| f 5/37/1 14/44/1 13/43/1 | ||||
| f 13/13/2 14/14/2 10/20/2 | ||||
| f 13/13/2 10/20/2 9/19/2 | ||||
| f 9/25/5 10/26/5 2/32/5 | ||||
| f 9/25/5 2/32/5 1/31/5 | ||||
| f 1/1/6 2/2/6 6/8/6 | ||||
| f 1/1/6 6/8/6 5/7/6 | ||||
| # Часть 2 | ||||
| f 6/39/4 7/40/4 15/46/4 | ||||
| f 6/39/4 15/46/4 14/45/4 | ||||
| f 14/15/2 15/16/2 11/22/2 | ||||
| f 14/15/2 11/22/2 10/21/2 | ||||
| f 10/27/8 11/28/8 3/34/8 | ||||
| f 10/27/8 3/34/8 2/33/8 | ||||
| f 2/3/6 3/4/6 7/10/6 | ||||
| f 2/3/6 7/10/6 6/9/6 | ||||
| # Часть 3 | ||||
| f 7/41/3 8/42/3 16/48/3 | ||||
| f 7/41/3 16/48/3 15/47/3 | ||||
| f 15/17/2 16/18/2 12/24/2 | ||||
| f 15/17/2 12/24/2 11/23/2 | ||||
| f 11/29/7 12/30/7 4/36/7 | ||||
| f 11/29/7 4/36/7 3/35/7 | ||||
| f 3/5/6 4/6/6 8/12/6 | ||||
| f 3/5/6 8/12/6 7/11/6 | ||||
| g Ends1 | ||||
| f 1/49/3 5/51/3 13/52/3 | ||||
| f 1/49/3 13/52/3 9/50/3 | ||||
| f 4/49/1 12/50/1 16/52/1 | ||||
| f 4/49/1 16/52/1 8/51/1 | ||||
| g Sides2 | ||||
| # Часть 1 | ||||
| f 21/37/1 22/38/1 30/44/1 | ||||
| f 21/37/1 30/44/1 29/43/1 | ||||
| f 29/13/2 30/14/2 26/20/2 | ||||
| f 29/13/2 26/20/2 25/19/2 | ||||
| f 25/25/5 26/26/5 18/32/5 | ||||
| f 25/25/5 18/32/5 17/31/5 | ||||
| f 17/1/6 18/2/6 22/8/6 | ||||
| f 17/1/6 22/8/6 21/7/6 | ||||
| # Часть 2 | ||||
| f 22/39/4 23/40/4 31/46/4 | ||||
| f 22/39/4 31/46/4 30/45/4 | ||||
| f 30/15/2 31/16/2 27/22/2 | ||||
| f 30/15/2 27/22/2 26/21/2 | ||||
| f 26/27/8 27/28/8 19/34/8 | ||||
| f 26/27/8 19/34/8 18/33/8 | ||||
| f 18/3/6 19/4/6 23/10/6 | ||||
| f 18/3/6 23/10/6 22/9/6 | ||||
| # Часть 3 | ||||
| f 23/41/3 24/42/3 32/48/3 | ||||
| f 23/41/3 32/48/3 31/47/3 | ||||
| f 31/17/2 32/18/2 28/24/2 | ||||
| f 31/17/2 28/24/2 27/23/2 | ||||
| f 27/29/7 28/30/7 20/36/7 | ||||
| f 27/29/7 20/36/7 19/35/7 | ||||
| f 19/5/6 20/6/6 24/12/6 | ||||
| f 19/5/6 24/12/6 23/11/6 | ||||
| g Ends2 | ||||
| f 17/49/3 21/51/3 29/52/3 | ||||
| f 17/49/3 29/52/3 25/50/3 | ||||
| f 20/49/1 28/50/1 32/52/1 | ||||
| f 20/49/1 32/52/1 24/51/1 | ||||
| @@ -88,7 +88,6 @@ minetest.register_node("mesecons_extrawires:tjunction_off", { | ||||
| minetest.register_craft({ | ||||
| 	output = "mesecons_extrawires:tjunction_off 3", | ||||
| 	recipe = { | ||||
| 		{"", "", ""}, | ||||
| 		{"mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off"}, | ||||
| 		{"", "mesecons_insulated:insulated_off", ""}, | ||||
| 	} | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| mesecons | ||||
| screwdriver? | ||||
| 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 | ||||
| 		end | ||||
| 		local mapping = { | ||||
| 			["and"] = 1, | ||||
| 			["or"] = 2, | ||||
| 			["not"] = 3, | ||||
| 			["xor"] = 4, | ||||
| 			["nand"] = 5, | ||||
| 			["buf"] = 6, | ||||
| 			["xnor"] = 7, | ||||
| 		} | ||||
| 		return s .. tostring(1 + mapping[val]) .. "]" | ||||
| 		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 | ||||
| 		end | ||||
| 		local mapping = { | ||||
| 			["and"] = "&", | ||||
| 			["or"] = "|", | ||||
| 			["not"] = "~", | ||||
| 			["xor"] = "^", | ||||
| 			["nand"] = "?", --dunno | ||||
| 			["buf"] = "_", | ||||
| 			["xnor"] = "=", | ||||
| 		} | ||||
| 		return mapping[s] | ||||
| 		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,21 +180,12 @@ 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 | ||||
|   | ||||
							
								
								
									
										3
									
								
								mesecons_fpga/mod.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| name = mesecons_fpga | ||||
| depends = mesecons | ||||
| optional_depends = screwdriver | ||||
							
								
								
									
										
											BIN
										
									
								
								mesecons_fpga/sounds/mesecons_fpga_copy.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								mesecons_fpga/sounds/mesecons_fpga_fail.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								mesecons_fpga/sounds/mesecons_fpga_write.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 256 B |