/***********************************************************/
// Atom variables and functions.
/***********************************************************/

atom

	var

		// This value must be set to specify which type of autojoining the object
		// uses. When an object is created or deleted, if this autojoin var is true,
		// it will attempt to match it with the appropriate type of autojoining
		// and align it with nearby atoms. Acceptable values are: 13, 16
		autojoin = 0
		autojoin_elevate=0 // Determines elevation; mostly for testing purposes.


	// Whenever an object is created or deleted, it will call the Autojoin function
	// which will tell all nearby objects to reconnect themselves, so if the new
	// or removed object changed the join-structure of the neighboring tiles, they
	// will readjust themselves.
/*	New()
		..()
		autojoining.Join(src)

	Del()
		autojoining.Join(src)
		..()*/


	// The JoinMatch proc is used to determine whether two tiles can join together
	// or not. You can override this function to provide your own requirements.
	proc/JoinMatch(direction)
		var/turf/T = get_step(src, direction)

		// Connect to the edges of the map.
		if(!T)
			return 1
		if(autojoin_elevate!=null)
			if(T.autojoin_elevate>autojoin_elevate) // If T's elevation is higher, we join with them.
				return 1
		if(istype(src,/turf/autojoin))
			if(istype(T,/turf/autojoin/walls)&&src:aj_walls) return 1
			if(istype(T,/turf/autojoin/paths)&&src:aj_paths) return 1
			if(istype(T,/turf/autojoin/building)&&src:aj_building) return 1
			if(istype(T,/turf/autojoin/roof)&&src:aj_roof) return 1
			if(istype(T,/turf/autojoin/cliffs)&&src:aj_cliffs) return 1
			if(istype(T,/turf/autojoin/terrain)&&src:aj_terrain) return 1

		// Connect to turfs of the same type.
		if(isturf(src))
			if(T.type == src.type)
				if(T.autojoin_elevate>=autojoin_elevate) return 1
				else return 0
				return 1

		// Connect to objects on the same type in that turf.
		else
			for(var/atom/movable/M in T)
				if(M.type == src.type)
					return 1

		return 0




/***********************************************************/
// Autojoining variables and functions.
/***********************************************************/

var/autojoining/autojoining = new()

autojoining

	var/const

		// These are used for bit-flag directions.
		N  = 1
		E  = 2
		S  = 4
		W  = 8
		NE = 16
		NW = 32
		SE = 64
		SW = 128

	var
		list/dir_to_bit = list(1, 16, 0, 4, 2, 8, 0, 64, 128, 32)


	// Telling an object to Autojoin will have the object and all objects within
	// a one-tile radius to reconnect themselves. The type of connection made
	// depends on each object's autojoin setting.
	proc/Join(atom/A)
		var/center = isturf(A) ? A : A.loc
		for(var/atom/N in range(center, 1))
			switch(N.autojoin)
				if(13)
					src.Autojoin13(N)
				if(16)
					src.Autojoin16(N)
				if(47)
					src.Autojoin47(N)
/*
				if(82)
					src.Autojoin82(N)
				if(161)
					src.Autojoin161(N)
*/
		return


	// This is use to assign adjust byte values by bit and mask. The object must
	// also be able to connect to the adjoining tile.
	proc/Match(atom/object, direction, byte, bit, mask=0)
		if((byte & mask) == mask && object.JoinMatch(direction))
			byte |= bit
		else
			byte &= ~bit
		return byte


	// *** 13-STATE AUTOJOINING *** //
	proc/Autojoin13(atom/A)
		var/byte = 0
		byte = src.Match(A, NORTH, byte, N)
		byte = src.Match(A, EAST, byte, E)
		byte = src.Match(A, SOUTH, byte, S)
		byte = src.Match(A, WEST, byte, W)
		byte = src.Match(A, NORTHEAST, byte, NE)
		byte = src.Match(A, SOUTHEAST, byte, SE)
		byte = src.Match(A, SOUTHWEST, byte, SW)
		byte = src.Match(A, NORTHWEST, byte, NW)
		A.icon_state = "[src.GetState13(byte)]"
		return


	// Check to see which directions are and aren't joinable. These are arranged in
	// order of most common to least common in order to use as few checks as possible.
	proc/GetState13(byte)

		// For for match combinations including diagonal directions.
		switch(byte)
			if(N|E|S|W|NE|SE|SW)
				return 12
			if(N|E|S|W|SE|SW|NW)
				return 11
			if(N|E|S|W|NE|SE|NW)
				return 10
			if(N|E|S|W|NE|SW|NW)
				return 9

		// If no diagonal matches were found, then check using only cardinals.
		switch(byte & (N|E|S|W))
			if(N|W)
				return 8
			if(N|E|W)
				return 7
			if(N|E)
				return 6
			if(N|S|W)
				return 5
			if(N|E|S)
				return 4
			if(S|W)
				return 3
			if(E|S|W)
				return 2
			if(E|S)
				return 1

		// Could not connect to anything.
		return 13


	// *** 16-STATE AUTOJOINING *** //
	proc/Autojoin16(atom/A)
		var/byte = 0
		byte = src.Match(A, NORTH, byte, 1)
		byte = src.Match(A, EAST, byte, 2)
		byte = src.Match(A, SOUTH, byte, 4)
		byte = src.Match(A, WEST, byte, 8)
		A.icon_state = "[byte]"
		return


	// *** 47-STATE AUTOJOINING *** //
	proc/Autojoin47(atom/A)
		var/byte = 0
		byte = src.Match(A, NORTH, byte, 1)
		byte = src.Match(A, EAST, byte, 4)
		byte = src.Match(A, SOUTH, byte, 16)
		byte = src.Match(A, WEST, byte, 64)
		byte = src.Match(A, NORTHEAST, byte, 2, 5)
		byte = src.Match(A, SOUTHEAST, byte, 8, 20)
		byte = src.Match(A, SOUTHWEST, byte, 32, 80)
		byte = src.Match(A, NORTHWEST, byte, 128, 65)
		A.icon_state = "[byte]"
		return

/*
	// *** 82-STATE AUTOJOINING *** //
	// Provided by Lummox JR
	proc/Autojoin82(atom/A)
		var/byte = 0
		for(var/direction in 1 to 10)
			var/bit = dir_to_bit[direction]
			if(bit && A.JoinMatch(direction))
				byte |= bit
		byte ^= (byte << 1) & ((byte << 7) | (byte >> 1)) & 170
		A.icon_state = "[byte]"
		return

	// *** 161-STATE AUTOJOINING *** //
	// Provided by Lummox JR
	proc/Autojoin161(atom/A)
		var/byte = 0
		for(var/direction in 1 to 10)
			var/bit = dir_to_bit[direction]
			if(bit && A.JoinMatch(direction))
				byte |= bit
		byte &= (byte << 1) | (byte << 7) | (byte >> 1) | 85
		A.icon_state = "[byte]"
		return
*/






turf
	autojoin
		var
			tmp
				y_end // The y value of where the cliff bottom is. Automatically determined on creation. Signifies what adjacent cliff tiles it should autojoin with based on its position.
				t_type // If set, this turf will treat adjacent turfs of this type as joining. This is useful if you want to make smooth walls.
				aj_paths // If 1, this turf will autojoin with any path-type turf adjacent to it.
				aj_walls // If 1, this turf will autojoin with any wall-type turf adjacent to it.
				aj_terrain // If 1, this turf will autojoin with any terrain-type turf adjacent to it.
				aj_cliffs // If 1, this turf will autojoin with any cliff-type turf adjacent to it.
				aj_building // If 1, this turf will autojoin with any building-type turf adjacent to it.
				aj_roof // If 1, this turf will autojoin with any roof-type turf adjacent to it.

		New()
			..()
			if(autojoin) autojoining.Join(src)

			BlockedEntry=setup_blocked_entry()
			BlockedExit=setup_blocked_exit()
			BlockedEntrySpec=setup_blocked_entry_spec()
			BlockedExitSpec=setup_blocked_exit_spec()
			setup_shifts()
			spawn(1) // For some reason this won't actually work if there's no spawn delay.
				setup_vari()

		Del()
			if(autojoin) autojoining.Join(src)
			..()
		proc
			setup_blocked_entry() .=0 // Setup directional density based on icon state.
			setup_blocked_exit() .=0 // Setup directional density based on icon state.
			setup_blocked_entry_spec() .=0 // Setup directional density based on icon state.
			setup_blocked_exit_spec() .=0 // Setup directional density based on icon state.
			setup_shifts()
			setup_vari()
			setup_y_end()
				var/turf/T=src
				while(!y_end) // First we loop downward until we hit the bottom of the map or a different tile type.
					if(T.y!=1) // If it's not already the bottom of the map, then continue downwards until there's no more of this cliff wall.
						var/turf/NT=get_step(T,SOUTH)
						if(NT.type==type)
							T=NT
						else
							y_end=T.y
					else
						y_end=1
				var/loops=1
				while(loops) // Now we continue moving upwards until we hit the edge of the map or a different tile type and set y_end along the way.
					if(T.y!=world.maxy)
						var/turf/autojoin/cliffs/NT=get_step(T,NORTH)
						if(NT.type==type)
							T=NT
							NT.y_end=y_end
						else loops=0
					else loops=0


		paths
			icon_state="255"
			autojoin=47
			icon='icons/autojoin/paths_convert.dmi'

			dirt_trans // Transparent dirt tile.
				icon='icons/autojoin/autojoin_path_dirt.dmi'
				icon_state="0" // State 0 is used because it has transparency, letting it use the tile beneath it as an underlay.
							// Note that tiles that are applied as underlays will not autojoin.

			stone
				icon='icons/autojoin/autojoin_path_stone.dmi'
			atlas // This one is transparent.
				icon='icons/autojoin/autojoin_path_atlas.dmi'
				icon_state="0"
			snow
				icon='icons/autojoin/autojoin_path_snow.dmi'
			farmland // Transparent farmland with custom icon state for mapping.
				icon='icons/autojoin/autojoin_path_farmland.dmi'
				icon_state="trans"

		walls
			icon_state="0"
			autojoin=47
			density=1
			opacity=1
			autojoin_elevate=1 // Set to 1 by default so that they play nice with cliffs.
			purple
				icon='icons/autojoin/autojoin_wall_purple.dmi'
			argentyle
				icon='icons/autojoin/autojoin_wall_argentyle.dmi'
			wood
				icon='icons/autojoin/autojoin_wall_wood.dmi'
			mage
				icon='icons/autojoin/autojoin_wall_mage.dmi'
				opacity=0
			castle
				icon='icons/autojoin/autojoin_wall_castle.dmi'
				opacity=0
			cabin
				icon='icons/autojoin/autojoin_wall_cabin.dmi'
			fort
				icon='icons/autojoin/autojoin_wall_fort.dmi'

		terrain
			autojoin=47
			aj_walls=1
			aj_paths=1
			aj_building=1
			aj_roof=1
			Click()
				setup_vari()
				..()
			JoinMatch(direction) // By default terrain follows the normal rules but will autojoin with paths, walls, and buildings so they do not interrupt it.
				.=..()
				var/turf/T=get_step(src,direction)
				if(istype(T,/turf/autojoin))
					var/cliff=istype(T,/turf/autojoin/cliffs)
					if(cliff)
						//if(T.x==x&&T.y>y) return 1 // If the cliff is directly above this tile then we should autojoin with it.
						if(T:y>y)
							if(T.autojoin_elevate!=null&&autojoin_elevate!=null&&T.autojoin_elevate+1>autojoin_elevate)
								return 1
						if(T:y<y&&T.x==x) return 0 // If this tile is directly above the cliff, it shouldn't autojoin.
					if(T.autojoin_elevate!=null&&autojoin_elevate!=null&&autojoin_elevate>T.autojoin_elevate) return 0
					else if(cliff)
						if(T.autojoin_elevate+1>autojoin_elevate) return 1 // Assuming the cliff is 1 elevation higher than it actually is, if it's greater than this tile's elevation, join with it.
//					if(istype(T,/turf/autojoin/walls)||istype(T,/turf/autojoin/paths)||istype(T,/turf/autojoin/building)||istype(T,/turf/autojoin/roof)) return 1
			dark_forest
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_darkforest.dmi'
				ele_1
					icon_state="e1"
					autojoin_elevate=1
				ele_2
					icon_state="e2"
					autojoin_elevate=2
				ele_3
					icon_state="e3"
					autojoin_elevate=3
				setup_vari()
					if(icon_state=="255")
						icon_state="[(y<=1||y%2!=0) ? "0" : "1"],[(x<=1||x%2!=0) ? "0" : "1"]"
/*						name="State Should Be: [(y<=1||y%2!=0) ? "0" : "1"],[(x<=1||x%2!=0) ? "0" : "1"], icon_state: [icon_state]"
					else
						name=icon_state*/
				setup_shifts()
					switch(icon_state)
						if("5")
							Shift_X=8
							Shift_Y=8
						if("7")
							Shift_X=8
							Shift_Y=8
						if("65")
							Shift_Y=8
							Shift_X=-8
						if("68")
							Shift_Y=8
						if("193")
							Shift_X=-8
							Shift_Y=8
						if("199")
							Shift_Y=8


				setup_blocked_entry()
					switch(icon_state)
						if("5")
							.=5
							ComplexEntry=DD_SW
							ComplexExit=130
						if("7") .=5
						if("17") .=12
						if("20")
							.=6
							ComplexEntry=DD_NW
							ComplexExit=136
						if("21")
							.=4
							ComplexExit=10
							ComplexEntry=160
						if("23")
							ComplexEntry=DD_E+DD_NW
							ComplexExit=DD_W+DD_SE
						if("28") .=6
						if("29")
							ComplexEntry=DD_E+DD_SW
							ComplexExit=DD_W+DD_NE
						if("31") .=4
						if("65")
							.=9
							ComplexEntry=DD_SE
							ComplexExit=136
						if("68") .=3
						if("69")
							.=1
							ComplexEntry=40
							ComplexExit=130
						if("71")
							.=1
							ComplexEntry=DD_SE
							ComplexExit=DD_NW
						if("80")
							.=10
							ComplexEntry=DD_NE
							ComplexExit=34
						if("81")
							.=8
							ComplexExit=160
							ComplexEntry=10
						if("84")
							.=2
							ComplexEntry=130
							ComplexExit=40
						if("85")
							ComplexEntry=170
							ComplexExit=170
						if("92") .=2
						if("95")
							ComplexEntry=10
							ComplexExit=DD_NW+DD_SW
						if("112") .=10
						if("113")
							//.=8
							ComplexEntry=DD_W+DD_SE
							ComplexExit=DD_NW+DD_E
						if("116") .=2
						if("124") .=2
						if("125")
							ComplexEntry=DD_SW+DD_SE
							ComplexExit=DD_NW+DD_NE
						if("193") .=9
						if("197")
							.=1
							ComplexEntry=DD_SW
							ComplexExit=DD_NE
						if("199") .=1
						if("209")
							ComplexEntry=DD_W+DD_NE
							ComplexExit=DD_E+DD_SW
						if("215")
							ComplexEntry=DD_NW+DD_NE
							ComplexExit=DD_SW+DD_SE
						if("241") .=8
						if("245")
							ComplexEntry=160
							ComplexExit=10
						if("127") .=0
						if("223") .=0
						if("247") .=0
						if("253") .=0
						if("255") .=0
						else density=1 // If it's not an icon we listed then we can assume it is probably dense.



				setup_blocked_exit()
					switch(icon_state)
						if("5") .=10
						if("7") .=10
						if("17") .=12
						if("20") .=9
						if("21") .=8
						if("28") .=9
						if("31") .=8
						if("65") .=6
						if("68") .=3
						if("69") .=2
						if("71") .=2
						if("80") .=5
						if("81") .=4
						if("84") .=1
						if("92") .=1
						if("112") .=5
						if("116") .=1
						if("124") .=1
						if("193") .=6
						if("197") .=2
						if("199") .=2
						if("241") .=4



				setup_blocked_entry_spec()
					switch(icon_state)
						if("92") .=5
						if("116") .=9
						if("127") .=6
						if("223") .=5
						if("253") .=10
						if("247") .=9

				setup_blocked_exit_spec()
					switch(icon_state)
						if("92") .=10
						if("116") .=6
						if("127") .=9
						if("223") .=10
						if("247") .=6
						if("253") .=5

			port
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_port.dmi'
			water
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_water.dmi'
				density=1
				JoinMatch(direction) // By default terrain follows the normal rules but will autojoin with paths, walls, and buildings so they do not interrupt it.
					.=..()
					var/turf/T=get_step(src,direction)
					if(T)
						if(T.type==/turf/autojoin/cliffs/port) return 1
			karaten
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_karaten.dmi'
				aj_cliffs=1
			black_marble
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_blackmarble.dmi'
				aj_cliffs=1
			white_marble
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_whitemarble.dmi'
				aj_cliffs=1
			mixed_marble_whitetoblack
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_mixedmarble.dmi'
				aj_cliffs=1
			mixed_marble_blacktowhite
				icon_state="255"
				icon='icons/autojoin/autojoin_terrain_mixedmarble2.dmi'
				aj_cliffs=1

		cliffs // Cliffs are essentially front-facing 'walls' that are meant to combo with other terrain to show depth. For example, the main wall might have a cliff leading down to the 'ground'.
			density=1
//			Click()
//				..()
//				usr << "y_end: [y_end], [icon_state]"
			JoinMatch(direction)
				.=..()
				var/turf/autojoin/cliffs/T=get_step(src,direction)
				if(istype(T,/turf/autojoin))
					if(T.type==type)
						if(T.y_end<=y_end)
							return 1
						else return 0
					else if(T.type==t_type)
						if(T.autojoin_elevate!=null&&autojoin_elevate!=null&&autojoin_elevate+1>T.autojoin_elevate) return 0
						else return 1


			New()
				..()
				if(!y_end)
					setup_y_end()
				if(autojoin) autojoining.Join(src)


			autojoin=47
			dark_forest
				icon='icons/autojoin/autojoin_cliff_darkforest.dmi'
				icon_state="map"
				t_type=/turf/autojoin/terrain/dark_forest
				ele_2
					icon_state="ele2"
					autojoin_elevate=2
			purple
				icon='icons/autojoin/autojoin_cliff_purple.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/purple
			argentyle
				icon='icons/autojoin/autojoin_cliff_argentyle.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/argentyle
			port
				icon='icons/autojoin/autojoin_cliff_port.dmi'
				icon_state="255"
				t_type=/turf/autojoin/terrain/port
			castle
				icon='icons/autojoin/autojoin_cliff_castle.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/castle
			cabin
				icon='icons/autojoin/autojoin_cliff_cabin.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/cabin
			fort
				icon='icons/autojoin/autojoin_cliff_fort.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/fort
			black_marble
				icon='icons/autojoin/autojoin_cliff_blackmarble.dmi'
				icon_state="255"
			white_marble
				icon='icons/autojoin/autojoin_cliff_whitemarble.dmi'
				icon_state="255"

		building
			autojoin=47
			autojoin_elevate=null
			density=1
			New()
				..()
				if(!y_end)
					setup_y_end()
				if(autojoin) autojoining.Join(src)


			JoinMatch(direction)
				.=..()
				var/turf/autojoin/T=get_step(src,direction)
				if(istype(T,/turf/autojoin))
					if(T.type==type)
						if(T.y_end<=y_end)
							return 1
						else return 0
					else if(T.type==t_type)
						return 1


			als_build // NOTE: It may be best to make buildings a separate definition type so that terrain type turfs can treat them as auto-join targets.
				// Otherwise, autojoin turfs will put edges around the building. This may be desirable in some circumstances but for the most part
				// you wouldn't want, for example, the cliff edges for dark forest terrain to circle a building.
				icon='icons/autojoin/autojoin_cliff_alsbuild.dmi'
				icon_state="255"
			mage_build
				icon='icons/autojoin/autojoin_cliff_mage.dmi'
				icon_state="255"
				t_type=/turf/autojoin/walls/mage
			color_roof
				icon='icons/autojoin/autojoin_cliff_colorroof.dmi'
				icon_state="255"


		roof
			autojoin=47
			autojoin_elevate=null
			density=1

/*			JoinMatch(direction)
				.=..()
				var/turf/autojoin/roof/T=get_step(src,direction)
				if(T.type==type)
					if(T.y_end<=y_end)
						return 1
					else return 0
				else if(T.type==t_type)
					return 1*/

			als_build
				icon='icons/autojoin/autojoin_roof_alsbuild.dmi'
				icon_state="255"
			color_roof
				icon='icons/autojoin/autojoin_roof_colorroof_light.dmi'
				icon_state="255"
				color="#ff0000" // You can set the color to a hex value or color matrix to color the roof.
				blend_mode=BLEND_ADD


