aboutsummaryrefslogtreecommitdiff
path: root/advtrains/textures/advtrains_dtrack_load_placer.png
blob: 427c0117040433c78543d748b7c82646fe897fc3 (plain)
ofshex dumpascii
0000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 38 00 00 00 39 08 06 00 00 00 63 da e8 .PNG........IHDR...8...9.....c..
0020 bb 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 93 00 00 00 09 70 48 59 73 00 00 0b 13 00 .....bKGD..............pHYs.....
0040 00 0b 13 01 00 9a 9c 18 00 00 00 07 74 49 4d 45 07 e1 05 1f 12 38 03 40 62 30 61 00 00 04 6d 49 ............tIME.....8.@b0a...mI
0060 44 41 54 68 de ed da 5b 68 db 55 1c 07 f0 ef 49 9a 0b 49 53 4d 93 76 8b 62 93 b5 dd 14 5d 42 57 DATh...[h.U....I..ISM.v.b....]BW
0080 e7 60 36 8d 81 89 c2 82 eb aa 6e 0f b6 63 0f 65 94 76 55 86 1b a8 14 a5 fa 30 27 be 4c c2 d4 31 .`6.......n..c.e.vU......0'.L..1
00a0 e6 54 f4 c1 31 46 14 ea e8 5a 47 5c 2f ca c0 15 bc 2c c2 50 9b 3e 98 2e c9 d2 ae 69 4b fd 97 e5 .T..1F...ZG\/....,.P.>.....iK...
00c0 f8 90 fd ff e4 f2 cf 3d 4d d6 72 ce 5b 48 38 39 9f 9c ef f9 9d 73 92 10 83 c1 80 f5 dc 24 58 e7 .......=M.r.[H89.....s.......$X.
00e0 8d 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 19 90 01 ................................
0100 57 b1 ed 78 6a 3b 2d 35 b0 a2 14 6f f2 da ab 7d b4 b5 b5 05 00 30 37 3b 47 0f 75 f7 90 35 09 3c W..xj;-5...o...}.....07;G.u..5.<
0120 df 6f a2 00 30 fa db 1d 00 00 31 76 80 87 2d 84 17 70 79 78 04 4b 4b 4b 6b 33 a2 ed 3b d5 74 3a .o..0.....1v..-..pyx.KKKk3..;.t:
0140 10 01 21 04 c4 d8 01 5b e7 c7 68 6d 6d c1 42 78 01 17 2f ba 10 bc 1d c4 93 cd db 10 0e 87 d7 26 ..!....[..hmm.Bx../............&
0160 70 a3 56 82 69 c5 1e c0 72 5c 14 e6 f7 07 51 55 55 85 9e 9e 6e 1c e8 ec 28 d9 5a 24 f9 fc 7c c6 p.V.i...r\....QUU...n...(.Z$..|.
0180 47 91 8f 63 62 14 9f 77 1c c6 d7 5f be 0b 02 82 5b fe 00 36 37 36 40 5b ad 05 c7 71 f8 f9 fc 11 G..cb..w..._....[..676@[...q....
01a0 38 bf 9b 25 f7 2d b0 7d a7 9a b6 98 75 30 d6 4a f1 e1 90 0e c7 8e be 0e 22 21 02 2c b1 8d 5f fd 8..%.-.}....u0.J........"!.,.._.
01c0 02 1c c7 41 fe e7 00 28 a5 88 5d a3 a5 80 e6 0c ec d9 ad a1 b2 86 83 49 33 86 c8 54 4c f0 4d 00 ...A...(..]............I3..TL.M.
01e0 00 0f e5 70 a1 4b 03 5f e8 2e 00 c0 6a d1 c3 58 2b 2d 29 34 2d 30 9b 28 26 b5 c8 14 3c e4 21 e1 ...p.K._....j..X+-)4-0.(&...<.!.
0200 e1 2e 03 08 ff c1 94 03 9a 12 18 1b c5 d1 3b 8e f4 b0 c8 14 20 31 c1 43 b9 a4 7e 5c dd da b8 81 ..............;......1.C..~\....
0220 97 1a 9a 76 06 63 37 68 7e 1f 6b 6e 6e 82 f2 c0 31 00 c0 4b 44 2d 44 31 b1 85 4e 6d 12 06 ce 0f ...v.c7h~.knn...1..KD-D1..Nm....
0240 be 1c 50 62 30 18 32 46 91 87 f1 55 b1 ee e8 09 a1 83 c7 21 13 ed d8 d9 ae 4a 1a 78 2e d0 e9 20 ..Pb0.2F...U.......!.....J.x....
0260 e0 f5 15 0e 25 bd 2f 36 52 59 05 c1 be 67 6a a2 03 31 1f 17 aa 62 22 2c b6 dc 87 76 bd 12 07 74 ....%./6RY...gj..1...b",...v...t
0280 75 6b b1 f7 f4 ac d0 f1 85 2e 0d 00 c0 17 ba 5b 10 34 9b 19 3d 77 f6 0c 55 a9 55 70 bb af e2 93 uk.............[.4..=w..U.Up....
02a0 4f 4f 93 a4 19 e4 3b 97 cb 24 90 3e ec 80 4c 26 83 d9 fc 84 28 ac a3 f3 20 e1 5f 9f ed c0 57 0b OO....;..$.>..L&....(....._...W.
02c0 7a ee ec 19 5a a9 a9 8c 3e 37 3a 0e bd ae 1a ef 0c bc 47 52 ae c1 b7 de 7c 83 8e 8d 8d a1 6d cf z...Z...>7:.......GR....|.....m.
02e0 0b 19 37 68 a7 6f 85 de e8 aa 46 a1 d0 e9 60 f4 79 af 2f 33 d4 1b a0 a8 d3 03 8b f5 fd 48 84 71 ..7h.o....F...`.y./3.........H.q
0300 2b 1c 26 af 4f 62 f0 d2 10 49 5b 64 f6 ef 7f 99 da 6d 36 d4 d4 d6 24 6d d0 62 03 8f 1d 4c 31 67 +.&.Ob...I[d.....m6...$m.b...L1g
0320 f4 df 19 79 dc 71 ae 77 6f 05 da 0e 7d 24 0a 53 28 14 98 99 f1 c3 66 b3 e2 76 30 80 de be 23 24 ...y.q.wo...}$.S(.....f..v0...#$
0340 ab 2a 1a 1b 5d 61 8d ae 32 d4 7a 78 22 7a 50 b8 b7 f5 00 c0 d0 e0 a9 b4 30 bb dd 06 02 82 5f ae .*..]a..2.zx"zP.........0....._.
0360 4f e2 fd 13 1f 90 9c 4f 32 c5 82 f2 95 51 0c fa f6 67 ff c5 a1 10 99 c2 d0 a5 c1 ac 61 db a4 df O......O2....Q...g..........a...
0380 88 16 a3 9c 8e 6a ab 31 a3 71 51 bc 07 e4 67 ec ca 15 37 94 4a 25 b4 0f 3e 90 12 e6 71 3b 45 8b .....j.1.qQ...g...7.J%..>...q;E.
03a0 91 54 42 70 d2 15 22 79 dd 26 8a 09 bd d5 77 53 38 30 88 45 71 7c e2 27 58 ad 4f a7 8c 62 52 31 .TBp.."y.&....wS80.Eq|.'X.O..bR1
03c0 da 20 c3 3f 33 2b e0 56 22 18 b9 f6 77 7e d7 a5 4c 50 b1 28 8a 41 bd 01 0a c5 c0 4d 54 7e ff 79 ...?3+.V"...w~..LP.(.A.....MT~.y
03e0 ca aa 68 da 54 9f 14 c5 74 db cb 5c 78 19 e6 06 2d 1e d1 4b f1 eb 5f f3 85 01 0b 9d d1 4c 55 d1 ..h.T...t..\x...-..K.._......LU.
0400 6e b7 61 78 f8 07 e8 74 3a d1 28 02 c0 c4 1f f3 38 e9 0a 09 fd ef b3 6a 28 c7 cd e3 db 6b 84 e4 n.ax...t:.(.....8......j(....k..
0420 7d e1 2d 14 2a b6 41 67 aa 8a 49 55 37 21 8a ee 1b 72 52 b4 1b 7d ae 50 6f 20 0a d5 ed e8 cf a9 }.-.*.Ag..IU7!...rR..}.Po.......
0440 2a a6 db 5e 3c de c5 b8 28 7e f5 e3 72 e9 80 a9 a0 23 fe 67 a1 52 a9 72 ae 8a d9 44 b1 a8 df c9 *..^<...(~..r....#.g.R.r...D....
0460 e4 0b 55 d7 b7 a1 a9 c9 82 e5 e5 e5 b4 1b 74 5c 31 ca 32 8a 65 05 02 80 c9 58 47 1b 1a 37 e3 b1 ..U...........t\1.2.e....XG..7..
0480 47 b7 64 15 45 1e ea f1 2e a2 56 ab c4 f6 2d 95 69 a3 58 76 20 df 2c 96 ad d4 b1 db 01 8f db 29 G.d.E.....V...-.i.Xv..,........)
04a0 5a 8c 12 a3 08 00 cf 6d e5 e8 e5 df e5 79 dd 09 49 39 ff 75 1f b7 46 ed 1b 0a 8a e2 7d 09 2c 66 Z......m.....y..I9.u..F.....}.,f
04c0 14 53 02 f5 ba 8d eb fa e7 b3 ff 01 49 56 06 20 c0 6a 55 fd 00 00 00 00 49 45 4e 44 ae 42 60 82 .S..........IV...jU.....IEND.B`.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
-- Track Circuit Breaks and Track Sections - Player interaction

local players_assign_tcb = {}
local players_assign_signal = {}
local players_link_ts = {}

local ildb = advtrains.interlocking.db
local ilrs = advtrains.interlocking.route

local sigd_equal = advtrains.interlocking.sigd_equal

local lntrans = { "A", "B" }

local function sigd_to_string(sigd)
	return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
end
advtrains.interlocking.sigd_to_string = sigd_to_string

minetest.register_node("advtrains_interlocking:tcb_node", {
	drawtype = "mesh",
	paramtype="light",
	paramtype2="facedir",
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-1/6, -1/2, -1/6, 1/6, 1/4, 1/6},
	},
	mesh = "at_il_tcb_node.obj",
	tiles = {"at_il_tcb_node.png"},
	description="Track Circuit Break",
	sunlight_propagates=true,
	groups = {
		cracky=3,
		not_blocking_trains=1,
		--save_in_at_nodedb=2,
		at_il_track_circuit_break = 1,
	},
	after_place_node = function(pos, node, player)
		local meta = minetest.get_meta(pos)
		meta:set_string("infotext", "Unconfigured Track Circuit Break, right-click to assign.")
	end,
	on_rightclick = function(pos, node, player)
		local pname = player:get_player_name()
		if not minetest.check_player_privs(pname, "interlocking") then
			minetest.chat_send_player(pname, "Insufficient privileges to use this!")
			return
		end
		
		local meta = minetest.get_meta(pos)
		local tcbpts = meta:get_string("tcb_pos")
		if tcbpts ~= "" then
			local tcbpos = minetest.string_to_pos(tcbpts)
			local tcb = ildb.get_tcb(tcbpos)
			if tcb then
				advtrains.interlocking.show_tcb_form(tcbpos, pname)
			else
				minetest.chat_send_player(pname, "This TCB has been removed. Please dig marker.")
			end
		else
			--unconfigured
			minetest.chat_send_player(pname, "Configuring TCB: Please punch the rail you want to assign this TCB to.")
			
			players_assign_tcb[pname] = pos
		end	
	end,
	--on_punch = function(pos, node, player)
	--	local meta = minetest.get_meta(pos)
	--	local tcbpts = meta:get_string("tcb_pos")
	--	if tcbpts ~= "" then
	--		local tcbpos = minetest.string_to_pos(tcbpts)
	--		advtrains.interlocking.show_tcb_marker(tcbpos)
	--	end	
	--end,
	can_dig = function(pos, player)
		if player == nil then return false end

		local pname = player:get_player_name()

		-- Those markers can only be dug when all adjacent TS's are set
		-- as EOI.
		local meta = minetest.get_meta(pos)
		local tcbpts = meta:get_string("tcb_pos")
		if tcbpts ~= "" then
			if not minetest.check_player_privs(pname, "interlocking") then
				minetest.chat_send_player(pname, "Insufficient privileges to use this!")
				return
			end			
			local tcbpos = minetest.string_to_pos(tcbpts)
			local tcb = ildb.get_tcb(tcbpos)
			if not tcb then return true end
			for connid=1,2 do
				if tcb[connid].ts_id or tcb[connid].signal then
					minetest.chat_send_player(pname, "Can't remove TCB: Both sides must have no track section and no signal assigned!")
					return false
				end
				if not ildb.may_modify_tcbs(tcb[connid]) then
					minetest.chat_send_player(pname, "Can't remove TCB: Side "..connid.." forbids modification (shouldn't happen).")
					return false
				end
			end
		end	
		return true
	end,
	after_dig_node = function(pos, oldnode, oldmetadata, player)
		if not oldmetadata or not oldmetadata.fields then return end
		local tcbpts = oldmetadata.fields.tcb_pos
		if tcbpts and tcbpts ~= "" then
			local tcbpos = minetest.string_to_pos(tcbpts)
			local success = ildb.remove_tcb(tcbpos)
			if success and player then
				minetest.chat_send_player(player:get_player_name(), "TCB has been removed.")
			else
				minetest.chat_send_player(player:get_player_name(), "Failed to remove TCB!")
				minetest.set_node(pos, oldnode)
				local meta = minetest.get_meta(pos)
				meta:set_string("tcb_pos", minetest.pos_to_string(tcbpos))
			end
		end
	end,
})


-- Crafting

-- set some fallbacks
local tcb_core = "default:mese_crystal"
local tcb_secondary = "default:mese_crystal_fragment"

--alternative recipe items
--core
if minetest.get_modpath("basic_materials") then
	tcb_core = "basic_materials:ic"
elseif minetest.get_modpath("technic") then
	tcb_core = "technic:control_logic_unit"
end
--print("TCB Core: "..tcb_core)
--secondary
if minetest.get_modpath("mesecons") then
	tcb_secondary = 'mesecons:wire_00000000_off'
end
--print("TCB Secondary: "..tcb_secondary)

minetest.register_craft({
	output = 'advtrains_interlocking:tcb_node 4',
	recipe = {
		{tcb_secondary,tcb_core,tcb_secondary},
		{'advtrains:dtrack_placer','','advtrains:dtrack_placer'}
	},
	--actually use track in the tcb recipe
	replacements = {
		{"advtrains:dtrack_placer","advtrains:dtrack_placer"},
		{"advtrains:dtrack_placer","advtrains:dtrack_placer"},
	}
})

--nil the temp crafting variables
tcb_core= nil
tcb_secondary = nil

minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
	local pname = player:get_player_name()
	if not minetest.check_player_privs(pname, "interlocking") then
		return
	end
	-- TCB assignment
	local tcbnpos = players_assign_tcb[pname]
	if tcbnpos then
		if vector.distance(pos, tcbnpos)<=20 then
			local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
			if node_ok and #conns == 2 then
				local ok = ildb.create_tcb(pos)
				
				if not ok then
					minetest.chat_send_player(pname, "Configuring TCB: TCB already exists at this position! It has now been re-assigned.")
				end
				
				ildb.sync_tcb_neighbors(pos, 1)
				ildb.sync_tcb_neighbors(pos, 2)
				
				local meta = minetest.get_meta(tcbnpos)
				meta:set_string("tcb_pos", minetest.pos_to_string(pos))
				meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
				minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB")
			else
				minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.")
			end
		else
			minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.")
		end
		players_assign_tcb[pname] = nil
	end
	
	-- Signal assignment
	local sigd = players_assign_signal[pname]
	if sigd then
		if vector.distance(pos, sigd.p)<=50 then
			local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2
			if is_signal then
				local ndef = minetest.registered_nodes[node.name]
				if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
					if ndef.advtrains.supported_aspects and not ndef.advtrains.supported_aspects.dst_shift then
						local tcbs = ildb.get_tcbs(sigd)
						if tcbs then
							tcbs.signal = pos
							if not tcbs.signal_name then
								tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p)
							end
							if not tcbs.routes then
								tcbs.routes = {}
							end
							ildb.set_sigd_for_signal(pos, sigd)
							minetest.chat_send_player(pname, "Configuring TCB: Successfully assigned signal.")
							advtrains.interlocking.show_ip_form(pos, pname, true)
						else
							minetest.chat_send_player(pname, "Configuring TCB: Internal error, TCBS doesn't exist. Aborted.")
						end
					else
						minetest.chat_send_player(pname, "Configuring TCB: Cannot use distant signal. Aborted.")
					end
				else
					minetest.chat_send_player(pname, "Configuring TCB: Cannot use static signals for routesetting. Aborted.")
				end
			else
				minetest.chat_send_player(pname, "Configuring TCB: Not a compatible signal. Aborted.")
			end
		else
			minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.")
		end
		players_assign_signal[pname] = nil
	end
end)

-- TCB Form

local function mktcbformspec(tcbs, btnpref, offset, pname)
	local form = ""
	local ts
	if tcbs.ts_id then
		ts = ildb.get_ts(tcbs.ts_id)
	end
	if ts then
		form = form.."label[0.5,"..offset..";Side "..btnpref..": "..minetest.formspec_escape(ts.name).."]"
		form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_gotots;Show track section]"
		if ildb.may_modify_tcbs(tcbs) then
			-- Note: the security check to prohibit those actions is located in database.lua in the corresponding functions.
			form = form.."button[0.5,"..(offset+1.5)..";2.5,1;"..btnpref.."_update;Update near TCBs]"
			form = form.."button[3  ,"..(offset+1.5)..";2.5,1;"..btnpref.."_remove;Remove from section]"
		end
	else
		tcbs.ts_id = nil
		form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]"
		form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]"
		--if tcbs.section_free then
			--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setlocked;Section is free]"
		--else
			--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setfree;Section is blocked]"		
		--end
	end
	if tcbs.signal then
		form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]"	
	else
		form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_asnsig;Assign a signal]"
	end
	return form
end


function advtrains.interlocking.show_tcb_form(pos, pname)
	if not minetest.check_player_privs(pname, "interlocking") then
		minetest.chat_send_player(pname, "Insufficient privileges to use this!")
		return
	end
	local tcb = ildb.get_tcb(pos)
	if not tcb then return end
	
	local form = "size[6,9] label[0.5,0.5;Track Circuit Break Configuration]"
	form = form .. mktcbformspec(tcb[1], "A", 1, pname)
	form = form .. mktcbformspec(tcb[2], "B", 5, pname)
	
	minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form)
	advtrains.interlocking.show_tcb_marker(pos)
end

--helper: length of nil table is 0
local function nlen(t)
	if not t then return 0 end
	return #t
end


minetest.register_on_player_receive_fields(function(player, formname, fields)
	local pname = player:get_player_name()
	if not minetest.check_player_privs(pname, "interlocking") then
		return
	end
	local pts = string.match(formname, "^at_il_tcbconfig_(.+)$")
	local pos
	if pts then
		pos = minetest.string_to_pos(pts)
	end
	if pos and not fields.quit then
		local tcb = ildb.get_tcb(pos)
		if not tcb then return end
		local f_gotots = {fields.A_gotots, fields.B_gotots}
		local f_update = {fields.A_update, fields.B_update}
		local f_remove = {fields.A_remove, fields.B_remove}
		local f_makeil = {fields.A_makeil, fields.B_makeil}
		local f_setlocked = {fields.A_setlocked, fields.B_setlocked}
		local f_setfree = {fields.A_setfree, fields.B_setfree}
		local f_asnsig = {fields.A_asnsig, fields.B_asnsig}
		local f_sigdia = {fields.A_sigdia, fields.B_sigdia}
		
		for connid=1,2 do
			local tcbs = tcb[connid]
			if tcbs.ts_id then
				if f_gotots[connid] then
					advtrains.interlocking.show_ts_form(tcbs.ts_id, pname)
					return
				end
				if f_update[connid] then
					ildb.sync_tcb_neighbors(pos, connid)
				end
				if f_remove[connid] then
					ildb.remove_from_interlocking({p=pos, s=connid})
				end
			else
				if f_makeil[connid] then
					-- try sinc_tcb_neighbors first
					ildb.sync_tcb_neighbors(pos, connid)
					-- if that didn't work, create new section
					if not tcbs.ts_id then
						ildb.create_ts({p=pos, s=connid})
						ildb.sync_tcb_neighbors(pos, connid)
					end
				end
				-- non-interlocked
				if f_setfree[connid] then
					tcbs.section_free = true
				end
				if f_setlocked[connid] then
					tcbs.section_free = nil
				end
			end
			if f_asnsig[connid] and not tcbs.signal then
				minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.")
				players_assign_signal[pname] = {p=pos, s=connid}
				minetest.close_formspec(pname, formname)
				return
			end
			if f_sigdia[connid] and tcbs.signal then
				advtrains.interlocking.show_signalling_form({p=pos, s=connid}, pname)
				return
			end

		end
		advtrains.interlocking.show_tcb_form(pos, pname)
	end

end)



-- TS Formspec

-- textlist selection temporary storage
local ts_pselidx = {}

function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
	if not minetest.check_player_privs(pname, "interlocking") then
		minetest.chat_send_player(pname, "Insufficient privileges to use this!")
		return
	end
	local ts = ildb.get_ts(ts_id)
	if not ts_id then return end
	
	local form = "size[10,10]label[0.5,0.5;Track Section Detail - "..ts_id.."]"
	form = form.."field[0.8,2;5.2,1;name;Section name;"..minetest.formspec_escape(ts.name).."]"
	form = form.."button[5.5,1.7;1,1;setname;Set]"
	local hint
	
	local strtab = {}
	for idx, sigd in ipairs(ts.tc_breaks) do
		strtab[#strtab+1] = minetest.formspec_escape(sigd_to_string(sigd))
		advtrains.interlocking.show_tcb_marker(sigd.p)
	end
	
	form = form.."textlist[0.5,3;5,3;tcblist;"..table.concat(strtab, ",").."]"
	
	if ildb.may_modify_ts(ts) then
		
		if players_link_ts[pname] then
			local other_id = players_link_ts[pname]
			local other_ts = ildb.get_ts(other_id)
			if other_ts then
				if ildb.may_modify_ts(other_ts) then
					form = form.."button[5.5,3;3.5,1;mklink;Join with "..minetest.formspec_escape(other_ts.name).."]"
					form = form.."button[9  ,3;0.5,1;cancellink;X]"
				end
			end
		else
			form = form.."button[5.5,3;4,1;link;Join into other section]"
			hint = 1
		end
		form = form.."button[5.5,4;4,1;dissolve;Dissolve Section]"
		form = form.."tooltip[dissolve;This will remove the track section and set all its end points to End Of Interlocking]"
		if sel_tcb then
			form = form.."button[5.5,5;4,1;del_tcb;Unlink selected TCB]"
			hint = 2
		end
	else
		hint=3
	end
	
	if ts.route then
		form = form.."label[0.5,6.1;Route is set: "..ts.route.rsn.."]"
	elseif ts.route_post then
		form = form.."label[0.5,6.1;Section holds "..#(ts.route_post.lcks or {}).." route locks.]"
	end
	-- occupying trains
	if ts.trains and #ts.trains>0 then
		form = form.."label[0.5,7.1;Trains on this section:]"
		form = form.."textlist[0.5,7.7;3,2;trnlist;"..table.concat(ts.trains, ",").."]"
	else
		form = form.."label[0.5,7.1;No trains on this section.]"
	end
	
	form = form.."button[5.5,7;4,1;reset;Reset section state]"
	
	if hint == 1 then
		form = form.."label[0.5,0.75;Use the 'Join' button to designate rail crosses and link not listed far-away TCBs]"
	elseif hint == 2 then
		form = form.."label[0.5,0.75;Unlinking a TCB will set it to non-interlocked mode.]"
	elseif hint == 3 then
		form = form.."label[0.5,0.75;You cannot modify track sections when a route is set or a train is on the section.]"
		--form = form.."label[0.5,1;Trying to unlink a TCB directly connected to this track will not work.]"
	end
	
	ts_pselidx[pname]=sel_tcb
	minetest.show_formspec(pname, "at_il_tsconfig_"..ts_id, form)
	
end


minetest.register_on_player_receive_fields(function(player, formname, fields)
	local pname = player:get_player_name()
	if not minetest.check_player_privs(pname, "interlocking") then
		return
	end
	-- independent of the formspec, clear this whenever some formspec event happens
	local tpsi = ts_pselidx[pname]
	ts_pselidx[pname] = nil
	
	local ts_id = string.match(formname, "^at_il_tsconfig_(.+)$")
	if ts_id and not fields.quit then
		local ts = ildb.get_ts(ts_id)
		if not ts then return end
		
		local sel_tcb
		if fields.tcblist then
			local tev = minetest.explode_textlist_event(fields.tcblist)
			sel_tcb = tev.index
			ts_pselidx[pname] = sel_tcb
		elseif tpsi then
			sel_tcb = tpsi
		end
		
		if ildb.may_modify_ts(ts) then
			if players_link_ts[pname] then
				if fields.cancellink then
					players_link_ts[pname] = nil
				elseif fields.mklink then
					ildb.link_track_sections(players_link_ts[pname], ts_id)
					players_link_ts[pname] = nil
				end
			end
			
			if fields.del_tcb and sel_tcb and sel_tcb > 0 and sel_tcb <= #ts.tc_breaks then
				if not ildb.remove_from_interlocking(ts.tc_breaks[sel_tcb]) then
					minetest.chat_send_player(pname, "Please unassign signal first!")
				end
				sel_tcb = nil
			end
			
			if fields.link then
				players_link_ts[pname] = ts_id
			end
			if fields.dissolve then
				ildb.dissolve_ts(ts_id)
				minetest.close_formspec(pname, formname)
				return
			end
		end
		
		if fields.setname then
			ts.name = fields.name
			if ts.name == "" then
				ts.name = "Section "..ts_id
			end
		end
		
		if fields.reset then
			-- User requested resetting the section
			-- Show him what this means...
			local form = "size[7,5]label[0.5,0.5;Reset track section]"
			form = form.."label[0.5,1;This will clear the list of trains\nand the routesetting status of this section.\nAre you sure?]"
			form = form.."button_exit[0.5,2.5;  5,1;reset;Yes]"
			form = form.."button_exit[0.5,3.5;  5,1;cancel;Cancel]"
			minetest.show_formspec(pname, "at_il_tsreset_"..ts_id, form)
			return
		end
		
		advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
		return
	end
	
	ts_id = string.match(formname, "^at_il_tsreset_(.+)$")
	if ts_id and fields.reset then
		local ts = ildb.get_ts(ts_id)
		if not ts then return end
		ts.trains = {}
		if ts.route_post then
			advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks)
		end
		ts.route_post = nil
		ts.route = nil
		for _, sigd in ipairs(ts.tc_breaks) do
			local tcbs = ildb.get_tcbs(sigd)
			advtrains.interlocking.update_signal_aspect(tcbs)
		end
		minetest.chat_send_player(pname, "Reset track section "..ts_id.."!")
	end
end)

-- TCB marker entities

-- table with objectRefs
local markerent = {}

minetest.register_entity("advtrains_interlocking:tcbmarker", {
	visual = "mesh",
	mesh = "trackplane.b3d",
	textures = {"at_il_tcb_marker.png"},
	collisionbox = {-1,-0.5,-1, 1,-0.4,1},
	visual_size = {x=10, y=10},
	on_punch = function(self)
		self.object:remove()
	end,
	on_rightclick = function(self, player)
		if self.tcbpos and player then
			advtrains.interlocking.show_tcb_form(self.tcbpos, player:get_player_name())
		end
	end,
	get_staticdata = function() return "STATIC" end,
	on_activate = function(self, sdata) if sdata=="STATIC" then self.object:remove() end end,
	static_save = false,
})

function advtrains.interlocking.show_tcb_marker(pos)
	--atdebug("showing tcb marker",pos)
	local tcb = ildb.get_tcb(pos)
	if not tcb then return end
	local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
	if not node_ok then return end
	local yaw = advtrains.conn_angle_median(conns[2].c, conns[1].c)
	
	local itex = {}
	for connid=1,2 do
		local tcbs = tcb[connid]
		local ts
		if tcbs.ts_id then
			ts = ildb.get_ts(tcbs.ts_id)
		end
		if ts then
			itex[connid] = ts.name
		else
			itex[connid] = "--EOI--"
		end
	end
	
	local pts = advtrains.roundfloorpts(pos)
	if markerent[pts] then
		markerent[pts]:remove()
	end
	
	local obj = minetest.add_entity(pos, "advtrains_interlocking:tcbmarker")
	if not obj then return end
	obj:set_yaw(yaw)
	obj:set_properties({
		infotext = "A = "..itex[1].."\nB = "..itex[2]
	})
	local le = obj:get_luaentity()
	if le then le.tcbpos = pos end
	
	markerent[pts] = obj
end

-- Signalling formspec - set routes a.s.o

-- textlist selection temporary storage
local sig_pselidx = {}
-- Players having a signalling form open
local p_open_sig_form = {}

function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, called_from_form_update)
	if not minetest.check_player_privs(pname, "train_operator") then
		minetest.chat_send_player(pname, "Insufficient privileges to use this!")
		return
	end
	local hasprivs = minetest.check_player_privs(pname, "interlocking")
	local tcbs = ildb.get_tcbs(sigd)
	
	if not tcbs.signal then return end
	if not tcbs.signal_name then tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p) end
	if not tcbs.routes then tcbs.routes = {} end
	
	local form = "size[7,10.25]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]"
	form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..minetest.formspec_escape(tcbs.signal_name).."]"
	form = form.."button[5.5,1.2;1,1;setname;Set]"
	
	if tcbs.routeset then
		local rte = tcbs.routes[tcbs.routeset]
		if not rte then
			atwarn("Unknown route set from signal!")
			tcbs.routeset = nil
			return
		end
		form = form.."label[0.5,2.5;A route is requested from this signal:]"
		form = form.."label[0.5,3.0;"..minetest.formspec_escape(rte.name).."]"
		if tcbs.route_committed then
			form = form.."label[0.5,3.5;Route has been set.]"
		else
			form = form.."label[0.5,3.5;Waiting for route to be set...]"
			if tcbs.route_rsn then
				form = form.."label[0.5,4;"..minetest.formspec_escape(tcbs.route_rsn).."]"
			end
		end
		if not tcbs.route_auto then
			form = form.."button[0.5,7;  5,1;auto;Enable Automatic Working]"
		else
			form = form.."label[0.5,7  ;Automatic Working is active.]"
			form = form.."label[0.5,7.3;Route is re-set when a train passed.]"
			form = form.."button[0.5,7.7;  5,1;noauto;Disable Automatic Working]"
		end
		
		form = form.."button[0.5,6;  5,1;cancelroute;Cancel Route]"
	else
		if not tcbs.route_origin then
			local strtab = {}
			for idx, route in ipairs(tcbs.routes) do
				local clr = ""
				if route.ars then
					clr = "#FF5555"
					if route.ars.default then
						clr = "#55FF55"
					end
				end
				strtab[#strtab+1] = clr .. minetest.formspec_escape(route.name)
			end
			form = form.."label[0.5,2.5;Routes:]"
			form = form.."textlist[0.5,3;5,3;rtelist;"..table.concat(strtab, ",").."]"
			if sel_rte then
				form = form.."button[0.5,6;  5,1;setroute;Set Route]"
				form = form.."button[0.5,7;2,1;dsproute;Show]"
				if hasprivs then
					form = form.."button[3.5,7;2,1;editroute;Edit]"
				end
			else
				if tcbs.ars_disabled then
					form = form.."label[0.5,6  ;NOTE: ARS is disabled.]"
					form = form.."label[0.5,6.5;Routes are not automatically set.]"
				end
			end
			if hasprivs then
				form = form.."button[0.5,8;2.5,1;newroute;New Route]"
				form = form.."button[  3,8;2.5,1;unassign;Unassign Signal]"
				form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled)
				form = form..string.format("checkbox[0.5,9.25;dst;Distant signalling;%s]", not tcbs.nodst)
			end
		elseif sigd_equal(tcbs.route_origin, sigd) then
			-- something has gone wrong: tcbs.routeset should have been set...
			form = form.."label[0.5,2.5;Inconsistent state: route_origin is same TCBS but no route set. Try again.]"
			ilrs.cancel_route_from(sigd)
		else
			form = form.."label[0.5,2.5;Route is set over this signal by:\n"..sigd_to_string(tcbs.route_origin).."]"
			form = form.."label[0.5,4;Wait for this route to be cancelled in order to do anything here.]"
		end
	end	
	sig_pselidx[pname] = sel_rte
	minetest.show_formspec(pname, "at_il_signalling_"..minetest.pos_to_string(sigd.p).."_"..sigd.s, form)
	p_open_sig_form[pname] = sigd
	
	-- always a good idea to update the signal aspect
	if not called_from_form_update then
	-- FIX prevent a callback loop
		advtrains.interlocking.update_signal_aspect(tcbs)
	end
end

function advtrains.interlocking.update_player_forms(sigd)
	for pname, tsigd in pairs(p_open_sig_form) do
		if advtrains.interlocking.sigd_equal(sigd, tsigd) then
			advtrains.interlocking.show_signalling_form(sigd, pname, nil)
		end
	end
end


minetest.register_on_player_receive_fields(function(player, formname, fields)
	local pname = player:get_player_name()
	if not minetest.check_player_privs(pname, "train_operator") then
		return
	end
	local hasprivs = minetest.check_player_privs(pname, "interlocking")
	
	-- independent of the formspec, clear this whenever some formspec event happens
	local tpsi = sig_pselidx[pname]
	sig_pselidx[pname] = nil
	p_open_sig_form[pname] = nil
	
	local pts, connids = string.match(formname, "^at_il_signalling_([^_]+)_(%d)$")
	local pos, connid
	if pts then
		pos = minetest.string_to_pos(pts)
		connid = tonumber(connids)
		if not connid or connid<1 or connid>2 then return end
	end
	if pos and connid then
		local sigd = {p=pos, s=connid}
		local tcbs = ildb.get_tcbs(sigd)
		if not tcbs then return end

		if fields.quit then
			-- form quit: disable temporary ARS ignore
			tcbs.ars_ignore_next = nil
			return
		end

		local sel_rte
		if fields.rtelist then
			local tev = minetest.explode_textlist_event(fields.rtelist)
			sel_rte = tev.index
		elseif tpsi then
			sel_rte = tpsi
		end
		if fields.setname and fields.name and hasprivs then
			tcbs.signal_name = fields.name
		end
		if tcbs.routeset and fields.cancelroute then
			if tcbs.routes[tcbs.routeset] and tcbs.routes[tcbs.routeset].ars then
				tcbs.ars_ignore_next = true
			end
			-- if route committed, cancel route ts info
			ilrs.update_route(sigd, tcbs, nil, true)
		end
		if not tcbs.routeset then
			if fields.newroute and hasprivs then
				advtrains.interlocking.init_route_prog(pname, sigd)
				minetest.close_formspec(pname, formname)
				tcbs.ars_ignore_next = nil
				return
			end
			if sel_rte and tcbs.routes[sel_rte] then
				if fields.setroute then
					ilrs.update_route(sigd, tcbs, sel_rte)
				end
				if fields.dsproute then