New features via Lua script

Can you help improve your favourite game? Hardcore C mages, talented artists, and players with any level of experience are welcome!
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

PersistentMapLabels Re-applies persistent map labels at start of each turn with some considerations. Incidentally, in the labels use \n for newline, \t for tab, max width per line appears to be about 25 characters, multiple lines work (example "\t1\n2\n\t3\n4\n\t\5" and so on.)

Code: Select all

--##############################################################################
function Lua_PersistentMapLabels(turn, year)

	-- If needed global vars not set, set them
	if ( PersistentMapLabels == nil ) then PersistentMapLabels=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( PersistentMapLabels ) then return end

	if ( debugLuaScripts ) then log.error("%s","PersistentMapLabels BEGIN") end

	-- Map labels -MUST- be placed here for availability via Lua script in Fc 2.6.4
	-- They may also be placed in the [map] section of the Scenario file, but it is not required.
	-- \t for tab and \n for newline works in the label text.
	labels={"\tHard\n Rock\n \tCandy\n Mountain", "Stinkin' SSpot\n\tP.U.!", "  Do NOT \nGo There!"}
	labelsX={20,30,43}
	labelsY={10,15,29}

-- ToDo: Optional terrain type dependency per label (so that "Highest Peak" stops being applied if the Mountains tile is transformed to Hills.)

	for i=1,#labels,1 do
		tile=find.tile(labelsX[i],labelsY[i])
		-- Check for unhomed units on tile
		if ( (tile):num_units() > 0 ) then
			for unit in ( (tile):units_iterate() ) do
				-- If there is still any unhomed unit on the tile, then skip this tile
				if ( (unit).homecity == 0 ) then goto continue end
			end
		end
		-- If we are here, then there are no unhomed units on tile
		-- Reapply the persistent map label
		-- Skip re-applying the label if tile is a city
		if not ( tile:city() ) then
			edit.tile_set_label(tile,labels[i])
		end
	end
	::continue::

	if ( debugLuaScripts ) then log.error("%s","PersistentMapLabels END") end
end
signal.connect("turn_started", "Lua_PersistentMapLabels")
Image Image
^ Before unhomed unit moves onto labelled tile, then on the labelled tile.

Image
^ Then next turn, off the labelled tile again and label has been re-applied.

The routine is included in the "FairTest" scenario file attached to my latest post in this topic.

Attached file: "RandoMap" with self-contained PersistentMapLabels routine only (no other Lua scripts) for testing purposes.
^ To test, start the scenario game, go to the editor and change any existing tile label, then "turn done" and the persistent tile label will be re-applied.
Attachments
RandoMap PersistentMapLabels only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(6.88 KiB) Downloaded 233 times
Last edited by Molo_Parko on Fri Nov 10, 2023 5:39 pm, edited 6 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

LateArrivalStartunits makes it possible for a scenario to give only non-Settler start units at turn 0, and then have Settlers and/or other units appear later. For example, to give human players an edge, provide a couple of Workers as start units without any settlers (and without the "start with a city" option.) Then use LateArrivalStartunits to supply Settlers on a later turn, such as turn 10. AI controlled tribes will not have their Workers make improvements to tiles since they have no city yet, but human players would have 10 turns to make tile improvements prior to their Settlers arriving. The script also allows any unit type to be included (by name, such as "Phalanx") among the late arrivals -- and every tribe gets the same units (as is the case with start units specified in scenario files in the usual way.) Another interesting use would be to supply only Explorers initially, and have Workers and Settlers arrive later, which may induce players that might otherwise not scout the area before building cities to do so. It is also possible within the script to randomize the turn on which units arrive for each tribe -- some tribes would get Settlers earlier than other tribes, at random but within a specified number of turns. Also there is no limit on the number of LateArrivalStartunits unlike startunits specified within a scenario file in the usual way (limit 15 units.)

Code: Select all

--##############################################################################
function Lua_LateArrivalStartunits(turn, year)

	if ( LateArrivalStartunits == nil ) then LateArrivalStartunits=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( LateArrivalStartunits ) then return end

	if ( debugLuaScripts ) then log.error("%s","LateArrivalStartunits BEGIN") end

	-- Derives start position from location of an actual startunit on turn #0
	-- LateArrivalStartunits will appear there

	-- Declare global variables if needed
	if ( _G.playerID == nil ) then _G.playerID={} end
	if ( _G.startX == nil ) then _G.startX={} end
	if ( _G.startY == nil ) then _G.startY={} end
	if ( _G.arrivalTurnPerTribe == nil ) then _G.arrivalTurnPerTribe={} end

	-- declare local variables
	local i, j, player, lateArrivalStartUnits, unit, tile
	local arrivalTurn, randomizeTurn, minimumTurn, maximumTurn, useDispersion

	lateArrivalStartUnits={"Settlers","Settlers"}
-- ToDo: Change this so that all unit names predefined in array, and user sets qty per unit type in separate array?
-- That is problematic due to varying customizations?
-- Hmm, boats appear where? nearest water within 10 tiles or less, or not at all?
	arrivalTurn=5 -- Change this to desired turn # at which units will arrive
	randomizeTurn=false
	minimumTurn=5
	maximumTurn=10
	useDispersion=false
-- ToDo: Apply dispersion to randomize tile where each lateArrivalStartunit appears

	-- Self-disable after maximumTurn
	if ( turn > maximumTurn ) then
		LateArrivalStartunits=false
		return
	end

	i=0
	if ( turn == 0 ) then 
		for player in ( players_iterate() ) do 
			i=(i+1)
			_G.playerID[i]=(player).id
			for unit in (player):units_iterate() do
				tile=(unit).tile
				_G.startX[i]=(tile).x 
				_G.startY[i]=(tile).y 
				-- Only need the x,y of one unit per player, so break
				break
			end
			if ( randomizeTurn ) then
				_G.arrivalTurnPerTribe[i]=( random(minimumTurn,maximumTurn) )
			else
				_G.arrivalTurnPerTribe[i]=( arrivalTurn )
			end
		end
	elseif ( ( turn > 0 ) and ( turn <= maximumTurn ) ) then
			for i = 1,#_G.playerID,1 do
				if turn == ( _G.arrivalTurnPerTribe[i] ) then
					player=find.player(_G.playerID[i])
					tile=find.tile( _G.startX[i],_G.startY[i] )
					for j = 1,#lateArrivalStartUnits,1 do
							unitType=( find.unit_type(lateArrivalStartUnits[j]) )
							if ( lateArrivalStartUnits[j] ~= "none" ) then
								if ( ( string.sub(fc_version(),17,19) ) <= "2.3" ) then 
								-- ^ not sure exactly which versions prior to 2.3 had this?
									create_unit(player, tile, unitType, 0, nil, 3)
								elseif ( ( string.sub(fc_version(),17,19) ) >= "2.4" ) then
									edit.create_unit(player, tile, unitType, 0, nil, 3)
									-- ^ moves left (the last # within the parens) should be type dependent?
								end
							end
					end
				end
			end
	end

	if ( debugLuaScripts ) then log.error("%s","LateArrivalStartunits END") end
end
signal.connect("turn_started", "Lua_LateArrivalStartunits")
The attached test scenario file "RandoMap with Lua LateArrivalStartunits only" has only this script. The scenario file FairTEST has all the Lua scripts together in one scenario for testing, and that scenario file is attached to my latest post in this topic.
Attachments
RandoMap with Lua LateArrivalStartunits only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(7.71 KiB) Downloaded 247 times
Last edited by Molo_Parko on Sat Nov 11, 2023 3:31 pm, edited 6 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

SearchForWaterCisterns is a modified version of the routine as used in the Hillzapoppin' scenario. Allows Workers (only -- Settlers and Engineers are too high-up in their wagons and bulldozers) to search for (and sometimes find) hidden underground water cisterns. They are only found on Grassland tiles without adjacent (including diagonally) water.

Code: Select all

--##############################################################################
function Lua_SearchForWaterCisterns(unit, src_tile, dst_tile)

	-- This routine was originally used in the Hillzapoppin scenario and is slightly modified from that version

	-- Only workers are skilled at finding undergound water cisterns
	-- Engineers and Settlers are too high-up in their wagons and bulldozers.

	-- Hidden, underground water cisterns are only found on Grassland tiles
	-- and never on tiles next to lake, ocean, deep ocean, irrigation, or river

	-- If needed global vars not set, set them, part 1 of 2
	if ( SearchForWaterCisterns == nil ) then SearchForWaterCisterns=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( SearchForWaterCisterns ) then return end

	if ( debugLuaScripts ) then log.error("%s","SearchForWaterCisterns BEGIN") end

	-- declare local variables
	local nearWater, i, j, checkX, checkY

	-- If needed global vars aren't set, set them, part 2 of 2
	if ( _G.cisternsCount == nil ) then _G.cisternsCount=0 end
	if ( _G.cisternsDifficulty == nil ) then _G.cisternsDifficulty=80 end
	if ( topology == nil ) or ( _G.wrapX == nil ) or ( _G.wrapY == nil )
	or ( _G.xSize == nil ) or ( _G.ySize == nil ) or ( _G.topo == nil ) then 

		topology=(server.setting.get("topology"))
		_G.xSize=tonumber(server.setting.get("xsize"))
		_G.ySize=tonumber(server.setting.get("ysize"))
		_G.dispersion=(server.setting.get("dispersion"))
		_G.topo="classic"
		_G.wrapX=false
		_G.wrapY=false
		topoFields={}

		-- Split topology field (from scenario settings) to a table
		count=1
		for field in string.gmatch(topology, "[^|]+") do
			topoFields[count]=field
			count=(count+1)
		end

		-- Evaluate topology settings
		if ( #topoFields > 0 ) then
			for i=1,#topoFields,1 do
				if ( topoFields[i] == "WRAPX" ) then _G.wrapX=true 
				elseif ( topoFields[i] == "WRAPY" ) then _G.wrapY=true 
				elseif ( topoFields[i] == "ISO" ) then _G.topo="ISO" 
				elseif ( topoFields[i] == "HEX" ) then _G.topo="HEX" end
			end
		end
	end

	-- Uses the following global variables
	-- SearchForWaterCisterns _G.cisternsCount _G.cisternsDifficulty _G.wrapX _G.wrapY ySize xSize

	-- declare global function
	function _G.is_XY_map_location_valid(x,y)
		if ( _G.wrapX and _G.wrapY ) 
		or ( _G.wrapX and ( y >= 0 ) and ( y < _G.ySize ) ) 
		or ( _G.wrapY and ( x >= 0 ) and ( x < _G.xSize ) ) 
		or ( y >= 0 and y < _G.ySize and x >= 0  and x < _G.xSize ) then
			return true
		else
			return false
		end
	end

	if      ( unit.utype.id == find.unit_type("Workers").id ) 
	and     ( dst_tile.terrain.id == find.terrain("Grassland").id ) 
	and not ( dst_tile:has_extra("River") ) 
	and not ( dst_tile:has_extra("Irrigation") ) 
	and not ( (dst_tile):city() ) then 

		-- Check for neighboring tiles with water
		nearWater=false
		for i=-1, 1, 1 do
			local checkY=( ( (dst_tile).y ) + i )
			for j=-1, 1, 1 do
				local checkX=( ( (dst_tile).x ) + j )

				-- Skip tiles which are past the edges of a map when wrapping is disabled
				if ( _G.is_XY_map_location_valid( ( checkX ),( checkY ) ) ) then
					if ( ( find.tile(checkX,checkY) ):has_extra("River") )
					or ( ( find.tile(checkX,checkY) ):has_extra("Irrigation") ) 
					or ( ( find.tile(checkX,checkY) ).terrain.id == find.terrain("Lake").id ) 
					or ( ( find.tile(checkX,checkY) ).terrain.id == find.terrain("Ocean").id ) 
					or ( ( find.tile(checkX,checkY) ).terrain.id == find.terrain("Deep Ocean").id ) then
						nearWater=true
					end
				end
			end
		end

		if not nearWater and ( random(1, 100) > _G.cisternsDifficulty ) then
			if ( ( string.sub(fc_version(),17,19) ) >= "2.6" ) then
				edit.create_extra( dst_tile,"River" )
				_G.cisternsCount=( _G.cisternsCount + 1 )
				_G.cisternsDifficulty=( _G.cisternsDifficulty+1 )
				if ( _G.cisternsDifficulty > 99 ) then _G.cisternsDifficulty=90 end
				-- Uncomment the line below (remove the two hyphens -- at start) to get a message window entry each time
				notify.event_msg(nil,nil,0,"A worker has found an underground water cistern. Total found so far:".._G.cisternsCount)
			end
		end
	end
	if ( debugLuaScripts ) then log.error("%s","SearchForWaterCisterns END") end
end
signal.connect("unit_moved", "Lua_SearchForWaterCisterns")
The attached test scenario file "RandoMap" includes the SearchForWaterCisterns Lua script as the only Lua script in the file, for testing. To test, create some workers and have them move around on grassland tiles which aren't next to water, river, or irrigation tiles. It may take several turns for the workers to find a cistern.

The "FairTest" scenario (for Freeciv 2.6 and later) file includes all the Lua scripts that I've posted in this topic, and that file is attached to the latest of my posts in this topic.
Attachments
RandoMap SearchForWaterCisterns 48x48=2k, 80% land, Classic, Flat, v1 copy.sav.zip
(7.66 KiB) Downloaded 241 times
Last edited by Molo_Parko on Fri Nov 10, 2023 9:55 pm, edited 3 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

OnTheBeatenPathToRoad is intended to benefit AI tribes who build road near their cities for tile trade value rather than as connection between cities. Usually they leave gaps in the road (between cities), which results in their city defender units not being able to quickly move from one city to another. It also slows down their workers as they keep walking back and forth between towns and over some tiles without road. This script tracks the # of turns each tile is occupied at turn_begin, and once a tile has been occupied 10 turns (consecutive or not), Road is added to the tile, because it has become a "Beaten Path" -- a defacto road. Counting occupied tiles begins at the turn specified in the "function turn_callback(turn, year)" section (it is pre-set to 10) to avoid creating road merely because the AI lamely has startunit Workers standing still until Settlers arrive later via LateArrivalStartunits.

A human player isn't likely to get much advantage from this, because it would take less time for a Workers/Settlers to build the road (Mountains tiles take longest at 6 turns.) Units visiting, or "parked" on a tile in enemy territory will contribute to a tile gaining a road to the potential benefit of the enemy. A side effect of this method is that it is possible to "get" a road on a river tile without Bridge Building technology.

Code: Select all

--##############################################################################
function Lua_OnTheBeatenPathToRoad(turn, year)

	-- If needed global vars not set, set them
	if ( OnTheBeatenPathToRoad == nil ) then OnTheBeatenPathToRoad=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( OnTheBeatenPathToRoad ) then return end

	if ( debugLuaScripts ) then log.error("%s","OnTheBeatenPathToRoad BEGIN") end

	if ( turn < 10 ) and ( OnTheBeatenPathToRoad ) then return end
		-- ^ Ideally the turn number should be maximumTurn from LateArrivalStartunits
		-- so that AI tribes' Workers standing still won't help with "free" road.

	-- Declare local variables
	local tile

	-- Uses the following global variables
	-- _G.tilesOccupied[tile]

	-- Create the global tilesOccupied table if needed
	if ( tilesOccupied == nil ) then tilesOccupied={} end

	for tile in ( whole_map_iterate() ) do
		if not ( _G.tilesOccupied[tile] ) then 
			_G.tilesOccupied[tile]=0
		end
		if ( (tile):num_units() > 0 ) 
		and ( tile.terrain:class_name() == "Land" ) 
		and not ( (tile):has_extra("Road") ) 
		and not ( (tile):city() ) then
			_G.tilesOccupied[tile]=( _G.tilesOccupied[tile] + 1 )
			if ( _G.tilesOccupied[tile] >= 10 ) then
				if ( ( string.sub(fc_version(),17,19) ) >= "2.6" ) then
					edit.create_extra( tile,"Road" )
					_G.tilesOccupied[tile]=0
					-- Uncomment the next line to see a message every time a Beaten Path becomes Road
					--notify.event_msg(nil,nil,0,"\tA beaten path has become Road.")
				end
			end
		end
	end
	if ( debugLuaScripts ) then log.error("%s","OnTheBeatenPathToRoad END") end
end
signal.connect("turn_started", "Lua_OnTheBeatenPathToRoad")
Attached file for testing has only the OnTheBeatenPathToRoad script with no other Lua scripts. To test, leave any land units on the same land tile fo 20 turns. Occupied tiles counting begins at turn 10, and 10 turns (continuous or not) are required to declare the tile a "Beaten Path" at which point road is added to the tile.
Attachments
RandoMap with Lua OnTheBeatenPathToRoad only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(6.71 KiB) Downloaded 249 times
Last edited by Molo_Parko on Fri Nov 10, 2023 6:05 pm, edited 6 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

SmallpoxSymptoms shows the devastating consequences of Smallpox strategy. Problems with area over-development, resource depletion, and the inevitable barbarians who prey on and in densely developed areas are all evident when using this script. Devastating to A.I. controlled tribes (not to mention the land they occupy.) The gist is that building cities at exactly the 'citymindist' minimum distance (which is 3 in the attached scenario) has consequences. Humans can easily escape the problem by building cities further apart, and by scouting a few tiles beyond the city build tile when building near another tribes' border to ensure that they aren't building a city just three tiles away from any of the neighboring tribes' cities.

Code: Select all

--##############################################################################
function Lua_SmallpoxSymptoms(city)

	if ( SmallpoxSymptoms == nil ) then SmallpoxSymptoms=true end

	-- If this routine is disabled, just return without doing anything else
	if not ( SmallpoxSymptoms ) then return end

	-- Barbarians scenario setting must be at least "HUTS_ONLY" to unleash barbarians
	-- If Barbarians are disabled in scenario settings, just return
	if ( server.setting.get("barbarians") == "DISABLED" ) then return end

	-- If needed global vars aren't set, set them
	if ( _G.BarbariansExistAlready == nil ) then _G.BarbariansExistAlready=false end
	if ( topology == nil ) or ( _G.wrapX == nil ) or ( _G.wrapY == nil )
	or ( _G.xSize == nil ) or ( _G.ySize == nil ) or ( _G.topo == nil )
	or ( _G.citymindist == nil ) then 

		topology=(server.setting.get("topology"))
		_G.xSize=tonumber(server.setting.get("xsize"))
		_G.ySize=tonumber(server.setting.get("ysize"))
		_G.dispersion=(server.setting.get("dispersion"))
		_G.citymindist=(server.setting.get("citymindist"))
		_G.topo="classic"
		_G.wrapX=false
		_G.wrapY=false
		topoFields={}

		-- Split topology field (from scenario settings) to a table
		count=1
		for field in string.gmatch(topology, "[^|]+") do
			topoFields[count]=field
			count=(count+1)
		end

		-- Evaluate topology settings
		if ( #topoFields > 0 ) then
			for i=1,#topoFields,1 do
				if ( topoFields[i] == "WRAPX" ) then _G.wrapX=true 
				elseif ( topoFields[i] == "WRAPY" ) then _G.wrapY=true 
				elseif ( topoFields[i] == "ISO" ) then _G.topo="ISO" 
				elseif ( topoFields[i] == "HEX" ) then _G.topo="HEX" end
			end
		end
	end

	if ( _G.BarbariansExistAlready == true ) and ( _G.BarbarianPlayer == nil ) then
		for player in ( players_iterate() ) do
			if ( ( (player).nation ) == ( find.nation_type("Barbarian") ) ) then
				_G.BarbarianPlayer=player
			end
		end
	end

	if ( debugLuaScripts ) then log.error("%s","SmallpoxSymptoms BEGIN") end

	-- Declare local variables
	local tile, x, y, nearbyCities, v, h, checkTile, i, extra, someExtras={}

	-- Declare global function
	function _G.is_XY_map_location_valid(x,y)
		if ( _G.wrapX and _G.wrapY ) 
		or ( _G.wrapX and ( y >= 0 ) and ( y < _G.ySize ) ) 
		or ( _G.wrapY and ( x >= 0 ) and ( x < _G.xSize ) ) 
		or ( y >= 0 and y < _G.ySize and x >= 0  and x < _G.xSize ) then
			return true
		else
			return false
		end
	end

	-- Uses the following global variables
	-- _G.BarbariansExistAlready, _G.wrapX, _G.wrapY

	someExtras={"Irrigation","Mine","Oil Well","Hut","Farmland","Fortress","Airbase","Buoy","Road","Railroad","River"}
	tile=(city).tile
	x=(tile).x
	y=(tile).y
	nearbyCities=0

	-- Check left and right sides of area
	for h=(x-_G.citymindist),(x+_G.citymindist),( _G.citymindist * 2 ) do
		for v=( y - _G.citymindist ),( y + _G.citymindist ),1 do
			if ( _G.is_XY_map_location_valid(h,v) ) then
				checkTile=find.tile((h),v)
				if ( (checkTile):city() ) then
					nearbyCities=( nearbyCities + 1 )
				end
			end
		end
	end

	-- Check top and bottom sides of area but not the leftmost and rightmost which are already checked
	for v=(y-_G.citymindist),(y+_G.citymindist),( _G.citymindist * 2 ) do
		for h=( x - ( _G.citymindist - 1 ) ),( x + ( _G.citymindist - 1 ) ),1 do
			if ( _G.is_XY_map_location_valid(h,v) ) then
				checkTile=find.tile((h),v)
				if ( (checkTile):city() ) then
					nearbyCities=( nearbyCities + 1 )
				end
			end
		end
	end

	if ( nearbyCities == 1 ) then
		-- Cities too close together creates pollution problems
		for v=( y-_G.citymindist ),( y+_G.citymindist ),1 do
			for h=( x-_G.citymindist ),( x+_G.citymindist ),1 do
				if ( _G.is_XY_map_location_valid(h,v) ) then
					-- Pollution on every tile in city radius may slow down Smallpox city development
					checkTile=find.tile(h,v)
					edit.create_extra(checkTile,"Pollution")
				end
			end
		end
		if ( random(1,1000) >= 990 ) then -- 1% chance of barbarians when city built close to only 1 other city
			-- Barbarians tend to target densely developed areas for plunder
			edit.unleash_barbarians(tile) -- at city tile
			_G.BarbariansExistAlready=true
		end

	elseif ( nearbyCities >= 2 ) then
		-- Too many cities close together increases environmental problems and accidents
		for v=( y-_G.citymindist ),( y+_G.citymindist ),1 do
			for h=( x-_G.citymindist ),( x+_G.citymindist ),1 do
				if ( _G.is_XY_map_location_valid(h,v) ) then
					checkTile=find.tile(h,v)
					for i=1,#someExtras,1 do
						if ( (checkTile):has_extra(someExtras[i]) ) then
							edit.remove_extra(checkTile,someExtras[i])
						end
					end
				end
			end
		end

	elseif ( nearbyCities >= 3 ) then
		-- Barbarians tend to target densely developed areas for plunder
		edit.unleash_barbarians(tile)
		_G.BarbariansExistAlready=true
	end

	if ( debugLuaScripts ) then log.error("%s","SmallpoxSymptoms END") end
end --
signal.connect("city_built", "Lua_SmallpoxSymptoms")
Image
^ A.I. controlled tribes suffer horribly from SmallpoxSymptoms.

Attached is "RandoMap Lua SmallpoxSymptoms only" scenario file for testing purposes. The FairTEST scenario file has all the Lua scripts together in one scenario for Freeciv 2.6 or later and is attached to my latest post in this topic.

EDIT: A couple of other ideas to implement... decrease computer-controlled player's "Expansionist" trait by some amount per incidence of triggering SmallpoxSymptoms, and 50% chance of civil war each time.
Attachments
RandoMap Lua SmallpoxSymptoms only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(7.53 KiB) Downloaded 237 times
Last edited by Molo_Parko on Thu Nov 30, 2023 4:24 pm, edited 5 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

CursedTile and CursedTileUnitEffect: Woe to those tribes which build cities near the cursed land! Another good reason to scout the area prior to building a city. Units may move onto the cursed tile, but they won't be able to leave it (until death or disbanded.)

Image

Code: Select all

--##############################################################################
function Lua_CursedTileUnitEffect(unit, src_tile, dst_tile)
	-- If this routine is disabled, just return without doing anything else
	if not ( CursedTile ) then return end

	if ( debugLuaScripts ) then log.error("%s","CursedTileUnitEffect BEGIN") end

	-- if a unit moves from the _G.cursedTile, then move it back
	-- and attempt to use all the unit's movement points
	-- Except on turn 0 (gives initial startunits a chance to get off the cursed tile)

	if ( _G.cursedTile ) then
		if ( src_tile == _G.cursedTile ) then
			if ( (unit).homecity == 0 ) then
				edit.tile_set_label(dst_tile,"")
			end
			if ( game.turn() > 0 ) then
				edit.unit_move(unit,_G.cursedTile,6)
			end
		end
	end

	if ( debugLuaScripts ) then log.error("%s","CursedTileUnitEffect END") end
end
signal.connect("unit_moved", "Lua_CursedTileUnitEffect")


--##############################################################################
function Lua_CursedTile(turn, year)
	-- If needed global vars not set, set them
	if ( CursedTile == nil ) then CursedTile=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( CursedTile ) then return end

	if ( debugLuaScripts ) then log.error("%s","CursedTile BEGIN") end

	-- If needed global vars aren't set, set them
	if ( _G.BarbariansExistAlready == nil ) then _G.BarbariansExistAlready=false end
	if ( topology == nil ) or ( _G.wrapX == nil ) or ( _G.wrapY == nil )
	or ( _G.xSize == nil ) or ( _G.ySize == nil ) or ( _G.topo == nil )
	or ( _G.citymindist == nil ) then 

		topology=(server.setting.get("topology"))
		_G.xSize=tonumber(server.setting.get("xsize"))
		_G.ySize=tonumber(server.setting.get("ysize"))
		_G.dispersion=(server.setting.get("dispersion"))
		_G.citymindist=(server.setting.get("citymindist"))
		_G.topo="classic"
		_G.wrapX=false
		_G.wrapY=false
		topoFields={}

		-- Split topology field (from scenario settings) to a table
		count=1
		for field in string.gmatch(topology, "[^|]+") do
			topoFields[count]=field
			count=(count+1)
		end

		-- Evaluate topology settings
		if ( #topoFields > 0 ) then
			for i=1,#topoFields,1 do
				if ( topoFields[i] == "WRAPX" ) then _G.wrapX=true 
				elseif ( topoFields[i] == "WRAPY" ) then _G.wrapY=true 
				elseif ( topoFields[i] == "ISO" ) then _G.topo="ISO" 
				elseif ( topoFields[i] == "HEX" ) then _G.topo="HEX" end
			end
		end
	end

	if ( _G.BarbariansExistAlready == true ) and ( _G.BarbarianPlayer == nil ) then
		for player in ( players_iterate() ) do
			if ( ( (player).nation ) == ( find.nation_type("Barbarian") ) ) then
				_G.BarbarianPlayer=player
			end
		end
	end

	-- Declare local variables
	local x, y, tile, cursedLabel, consequences=false, unleashTile

	-- Declare global function
	function _G.is_XY_map_location_valid(x,y)
		if ( _G.wrapX and _G.wrapY ) 
		or ( _G.wrapX and ( y >= 0 ) and ( y < _G.ySize ) ) 
		or ( _G.wrapY and ( x >= 0 ) and ( x < _G.xSize ) ) 
		or ( y >= 0 and y < _G.ySize and x >= 0  and x < _G.xSize ) then
			return true
		else
			return false
		end
	end

	cursedLabel="~ Ancient ~\n\t~ Graves ~" -- The warning label for the cursed tile

	-- Uses the following global variables
	-- _G.cursedTile, _G.BarbariansExistAlready, _G.BarbarianPlayer

	-- Barbarians scenario setting must be at least "HUTS_ONLY" to unleash barbarians
	-- If Barbarians are disabled in scenario settings, just return
	if ( server.setting.get("barbarians") == "DISABLED" ) then log.error("%s","CursedTile: Barbarians disabled in scenario file") return end

	-- Curse a tile if not already done
	if ( _G.cursedTile == nil ) then 
		repeat
			x=random(1,_G.xSize) y=random(1,_G.ySize)
			if ( _G.is_XY_map_location_valid(x,y) ) then
				_G.cursedTile=find.tile(x,y)
			end
		until ( ( (_G.cursedTile).terrain:class_name() == "Land" ) and  ( (_G.cursedTile):num_units() == 0 ) )
	end

	-- Apply and re-apply the label every time
	edit.tile_set_label(_G.cursedTile,cursedLabel)

	-- Check whether the cursed tile is within a city's radius
	-- If citymindist=3, then this checks a 7x7 tile area centered on cursedTile
	x=((_G.cursedTile).x) y=((_G.cursedTile).y) unleashTile=nil
	for v=( y-_G.citymindist ),( y+_G.citymindist ),1 do
		for h=( x-_G.citymindist ),( x+_G.citymindist ),1 do
			if ( _G.is_XY_map_location_valid(h,v) ) then
				tile=find.tile(h,v)
				if ( (tile).terrain:class_name() == "Land" ) then
					if ( (tile):city() ) then 
						consequences=true
					else
						unleashTile=tile
					end
				end
			end
		end
	end

	-- If it were possible to induce a disaster in a city, or to apply an area effect, it would add some flexibility
	-- but it does not appear to be so, and therefore MORE BARBARIANS

	if ( consequences == true ) then
		if ( unleashTile ~= nil ) then
			if ( _G.BarbariansExistAlready == true ) then
				-- Using edit.create rather than unleash_barbarians
				unitType=find.unit_type("Horsemen")
				for i=1,3,1 do
					-- ^ This can trigger 1 turn after "unleash" if offending city not destroyed
					edit.create_unit(_G.BarbarianPlayer, unleashTile, unitType, 0, nil, 3)
				end
			else
				-- If Barbarians don't already exist, then unleash is the only method
				-- that does not add them to the Nations list visible to players?
				edit.unleash_barbarians(unleashTile)
				_G.BarbariansExistAlready=true
			end
		end
	end

	if ( debugLuaScripts ) then log.error("%s","CursedTile END") end
end
signal.connect("turn_started", "Lua_CursedTile")
Attached RandoMap file has only the CursedTile and CursedTileUnitEffect Lua scripts for testing. The FairTEST scenario posted in my latest post of this topic has -all- the Lua scripts in one scenario.
Attachments
RandoMap Lua_CursedTile and UnitEffect only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(8.04 KiB) Downloaded 266 times
Last edited by Molo_Parko on Fri Nov 10, 2023 7:05 pm, edited 6 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

CursedTileUnitEffect is now merged into the prior post with CursedTile.
Last edited by Molo_Parko on Fri Nov 10, 2023 7:07 pm, edited 3 times in total.
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

FadingFootprints leaves a short trail of footprints on tiles as units move around. Very helpful in tracking enemy units, especially in fog. :lol: Various other updates, corrections, and minor changes to prior routines are all included in the attached, playable scenario file for Freeciv 2.6.4 or later. As an example, minor change to CursedTileUnitEffect now allows initial startunits to move off the cursed tile on turn 0, if they happen to start on the cursed tile.

Code: Select all

--##############################################################################
function Lua_FadingFootprintsUnits(unit, src_tile, dst_tile)
	-- If this routine is disabled, just return without doing anything else
	if not ( FadingFootprints ) then return end

	if ( debugLuaScripts ) then log.error("%s","FadingFootprintsUnits BEGIN") end

	-- Create global footprint1 table if needed
	if not ( _G.footprints1 ) then _G.footprints1={} end

	-- Add src_tile to table footprints1
	_G.footprints1[#_G.footprints1 + 1]=(src_tile)

	-- Set footprints label on src_tile
	if ( game.turn() % 2 == 0 ) then
		edit.tile_set_label(src_tile," `. ")
	else
		edit.tile_set_label(src_tile," .` ")
	end

	if ( debugLuaScripts ) then log.error("%s","FadingFootprintsUnits END") end

end
signal.connect("unit_moved", "Lua_FadingFootprintsUnits")


--##############################################################################
function Lua_FadingFootprints(turn, year)

	-- If needed global vars not set, set them
	if ( FadingFootprints == nil ) then FadingFootprints=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( FadingFootprints ) then return end

	if ( debugLuaScripts ) then log.error("%s","FadingFootprints BEGIN") end

	-- Declare local variables
	local i

	-- Create global footprint tables if needed
	if not ( _G.footprints4 ) then _G.footprints4={} end
	if not ( _G.footprints3 ) then _G.footprints3={} end
	if not ( _G.footprints2 ) then _G.footprints2={} end
	if not ( _G.footprints1 ) then _G.footprints1={} end

	-- Clear labels from tiles in table 4
	for i=1,#_G.footprints4,1 do
		if ( _G.footprints4[i] ) then
			edit.tile_set_label(_G.footprints4[i],"")
		end
	end

	-- Shift tables contents
	_G.footprints4=( table.pack( table.unpack( _G.footprints3 ) ) )
	_G.footprints3=( table.pack( table.unpack( _G.footprints2 ) ) )
	_G.footprints2=( table.pack( table.unpack( _G.footprints1 ) ) )

	-- Clear table 1
	_G.footprints1=nil _G.footprints1={}

	if ( debugLuaScripts ) then log.error("%s","FadingFootprints END") end

end
signal.connect("turn_started", "Lua_FadingFootprints")
RandoMap scenario includes only the FadingFootprints Lua script. The FairTest scenario has all the Lua scripts.
Attachments
FairTEST 55x50=3k, 96% land, Classic, Flat, v2.11.15.sav.zip
(18.52 KiB) Downloaded 251 times
RandoMap with Lua FadingFootprints only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(6.7 KiB) Downloaded 226 times
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

Embark allows land units to embark for sea travel. Position a land unit next to an Ocean or Deep Ocean tile, disband the unit, and a temporary boat will be created with a new land unit in the boat (the replacement of the disbanded unit.) After the replacement unit leaves the boat, the empty boat will be disbanded at the beginning of the next turn. The boat is an unhomed unit, and the replacement Horseman keeps the same homecity as the original. Veteran status and current hit points of the original Horseman are lost since Freeciv 2.6.4 Lua has no way of determining those values from the original unit. Movement points are deliberately reduced to zero so that the "Embark" action uses one turn without further movement being possible in the same turn.

Code: Select all

--##############################################################################
function Lua_EmbarkBoatsCleanup(turn, year)

	if ( Embark == nil ) then Embark=true end

	-- If this routine is disabled, just return without doing anything else
	if not ( Embark ) then return end

	if ( debugLuaScripts ) then log.error("%s","EmbarkBoatsCleanup BEGIN") end

	-- Declare local variables
	local i, owner, killBoat

	-- Declare global vars if needed
	if ( _G.EmbarkBoats == nil ) then _G.EmbarkBoats={} end

	-- If Embark boat is empty, destroy it
	for i=1,#_G.EmbarkBoats,1 do
		if ( _G.EmbarkBoats[i] ~= nil ) then
			owner=(_G.EmbarkBoats[i]).owner
			killBoat=true
			for cargo in (_G.EmbarkBoats[i]):cargo_iterate() do
				-- If there are units in the boat, don't kill the boat
				killBoat=false
			end
			if ( killBoat == true) then
				-- Killing the unit will trigger unit_lost ?
				edit.unit_kill(_G.EmbarkBoats[i],"disbanded",owner)
				_G.EmbarkBoats[i]=nil
			end
		end
	end

	if ( debugLuaScripts ) then log.error("%s","EmbarkBoatsCleanup END") end

end
signal.connect("turn_started", "Lua_EmbarkBoatsCleanup")


--##############################################################################
function Lua_Embark(unit, player, reason)

	-- Flaky behavior in Freeciv 2.6.4, the disbanded unit is sometimes left viisble on the map, and in the city units list, but not addressable
	-- That persists across turns, and it is even possible for the unit to disappear after fog-of-war, but still be visible in city unit list
	-- switch to Editing mode > Global Observer, and then back to your tribe and -poof- the disbanded unit is gone completely

	-- If needed global vars are not set, set them, part 1 of 2
	if ( Embark == nil ) then Embark=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( Embark ) then return end

	if ( debugLuaScripts ) then log.error("%s","Embark BEGIN") end

	-- This prevents processing of disbanded/killed temp unit from ExtendVision
	if ( extendVisionInProgress ) then return end

	-- If needed global vars aren't set, set them
	if ( topology == nil ) or ( _G.wrapX == nil ) or ( _G.wrapY == nil )
	or ( _G.xSize == nil ) or ( _G.ySize == nil ) or ( _G.topo == nil ) then 

		topology=(server.setting.get("topology"))
		_G.xSize=tonumber(server.setting.get("xsize"))
		_G.ySize=tonumber(server.setting.get("ysize"))
		_G.topo="classic"
		_G.wrapX=false
		_G.wrapY=false
		topoFields={}

		-- Split topology field (from scenario settings) to a table
		count=1
		for field in string.gmatch(topology, "[^|]+") do
			topoFields[count]=field
			count=(count+1)
		end

		-- Evaluate topology settings
		if ( #topoFields > 0 ) then
			for i=1,#topoFields,1 do
				if ( topoFields[i] == "WRAPX" ) then _G.wrapX=true 
				elseif ( topoFields[i] == "WRAPY" ) then _G.wrapY=true 
				elseif ( topoFields[i] == "ISO" ) then _G.topo="ISO" 
				elseif ( topoFields[i] == "HEX" ) then _G.topo="HEX" end
			end
		end
	end

	-- Declare global function
	function _G.is_XY_map_location_valid(x,y)
		if ( _G.wrapX and _G.wrapY ) 
		or ( _G.wrapX and ( y >= 0 ) and ( y < _G.ySize ) ) 
		or ( _G.wrapY and ( x >= 0 ) and ( x < _G.xSize ) ) 
		or ( y >= 0 and y < _G.ySize and x >= 0  and x < _G.xSize ) then
			return true
		else
			return false
		end
	end

	-- To Embark, the unit must be disbanded
	if not ( reason == "disbanded" ) then
		if ( debugLuaScripts ) then log.error("%s","Embark: not a disbanded unit") end
		return
	end

	-- Declare local variables
	local tile, x, y, i, h, v, homecity, unitVet, unitMovesLeft, unitHPLeft, unitType, randomNum, embarkTile, checkTile

	-- Gather info about the original unit being disbanded
	tile=(unit).tile
	x=(tile).x
	y=(tile).y
	homecity=(unit).homecity
	unitType=(unit).utype

	-- Declare global vars if needed
	if ( _G.EmbarkBoats == nil ) then _G.EmbarkBoats={} end

	-- Unit must NOT be in a city, or on water or "Inaccessible" tiles
	if ( tile.terrain.id == find.terrain("Lake").id ) 
	or ( tile.terrain.id == find.terrain("Ocean").id ) 
	or ( tile.terrain.id == find.terrain("Deep Ocean").id )  
	or ( tile.terrain.id == find.terrain("Inaccessible").id ) 
	or ( (tile):city() ) then
		return
	end

	-- Check 1st ring of tiles around unit for adjacent water tile, get tile ID
	h={-1,-1,-1, 0, 0, 1, 1, 1}
	v={-1, 0, 1,-1, 1,-1, 0, 1}
	for i=1,#h,1 do
		if _G.is_XY_map_location_valid( (h[i]+x),(v[i]+y) ) then
			checkTile=find.tile((h[i]+x),(v[i]+y))
			if ( checkTile.terrain.id == find.terrain("Ocean").id )
			or ( checkTile.terrain.id == find.terrain("Deep Ocean").id ) 
			and ( checkTile:num_untis() == 0  ) then
				embarkTile=checkTile
				goto continue
			end
		end
	end

	-- ToDo: check 2nd ring of tiles if no embarkTile was found in 1st ring?

	::continue::
	
	-- if a valid embarkTile was found, then create the boat on that tile
	transportUnitType=find.unit_type("Trireme")
	unitVet=0
	unitMovesLeft=0
	unitHPLeft=-1
	homecity=nil
	edit.create_unit_full(player, embarkTile, transportUnitType, unitVet, homecity, unitMovesLeft, unitHPLeft, nil)

	-- Get the new transport unit's id and record in global array
	if ( (embarkTile):num_units() == 1 ) then
		for tileUnit in ( (embarkTile):units_iterate() ) do
			if ( (tileUnit).homecity == 0 ) then embarkBoatID=tileUnit end
		end
	end
	_G.EmbarkBoats[#_G.EmbarkBoats+1]=(embarkBoatID)

	-- Finally, create the replacement for the disbanded unit
	-- Replacement unit is created -in- the transport unit, and presumably -on- the same tile as the transport unit
	homecity=(unit):get_homecity()
	edit.create_unit_full(player, embarkTile, unitType, unitVet, homecity, unitMovesLeft, unitHPLeft, embarkBoatID)

	-- Uncomment the next line (remove the two hyphens) to receive a messgae list entry every time a unit Embarks
	--notify.event_msg(player,tile,0,"\tEmbark: A unit has embarked on a sea voyage." )

	if ( debugLuaScripts ) then log.error("%s","Embark END") end
end
signal.connect("unit_lost", "Lua_Embark")
Image
^ Horseman next to Ocean tile is ready to Embark

Image
^ Horseman next to an Ocean tile is disbanded, and replaced by a Horseman in a boat.

Image
^ Horseman exits the boat which will then be disbanded at the start of the next turn.

Image
^ Boat has been disbanded now that the Horseman is again on land.

I haven't yet added this into the FairTEST scenario file with all the other Lua scripts, and it is likely to interact poorly with the Military-to-Work Program Lua script since both are triggered by disbanding units.
Attachments
RandoMap with Lua Embark only 48x48=2k, 80% land, Classic, Flat, v1.sav.zip
(10.67 KiB) Downloaded 256 times
Molo_Parko
Hardened
Posts: 158
Joined: Fri Jul 02, 2021 4:00 pm

Re: New features via Lua script

Post by Molo_Parko »

AiTribesCityDefender Forces computer-controlled tribes to keep a unit in every city. If the only unit in a city moves out of the city, it is moved back into the city. If a city has no defender, one is created in the city, and payment in gold is extracted from the computer-controlled tribe -- even in small payments over time if needed. This is intended to benefit the computer-controlled tribes, but it may also be a relief for human players that ally with computer-controlled players in that the ally will be far less likely to leave their cities completely empty. The only cases in which a computer-controlled tribe's city may not have a unit present is if the unit is disbanded or attacked and killed -- a new defender unit will be created at the start of the next turn, which may have some impact on attacks against cities.

Code: Select all

--##############################################################################
function Lua_AiTribesCityDefenderCheck(turn, year)

	-- Computer-controlled players have a habit of leaving their cities without any defender
	-- This script forces computer-controlled player cities to have at least one unit present
	-- If city is empty, a unit is created regardless of computer-controlled player gold
	-- Tracks unit cost debt and extracts payments over time if needed
	-- Defender unit is always Warriors even after they can no longer be built normally
	-- This should result in every computer-controlled tribe's cities always having a unit

	if ( AiTribesCityDefender == nil ) then AiTribesCityDefender=true end
	if ( debugLuaScripts == nil ) then debugLuaScripts=false end

	-- If this routine is disabled, just return without doing anything else
	if not ( AiTribesCityDefender ) then return end

	if ( debugLuaScripts ) then log.error("%s","AiTribesCityDefenderCheck BEGIN") end

	-- Declare local variables
	local player, city, tile
	local unitType=find.unit_type("Warriors")

	-- Declare global variables if needed
	if ( _G.Debt == nil ) then _G.Debt={} end
	if ( _G.shieldbox == nil ) then _G.shieldbox=(server.setting.get("shieldbox")) end
	_G.CityDefenderCost=( ( (unitType).build_cost * ( _G.shieldbox / 100 ) ) * 2.55 )

	for player in players_iterate() do
		if ( (player).ai_controlled ) then 
			if ( _G.Debt[player] == nil ) then _G.Debt[player]=0 end
			for city in (player):cities_iterate() do
				if ( (city).exists ) then 
					tile=(city).tile
					-- If the city does not have any units in it...
					if ( (tile):num_units() == 0 ) then 
						-- Then create a city defender unit in the city, with no movement points
						edit.create_unit_full(player, tile, unitType, 0, city, 0, -1, mil)

						-- Uncomment the line below to receive a message list entry when an AICityDefender is created
						notify.event_msg(nil,nil,0,"\tA City Defender unit has been created for " .. tostring(player) .. ".")

						-- Add the cost to the AI player's account
						_G.Debt[player]=( _G.CityDefenderCost + _G.Debt[player] )
					end
				end
			end

			-- If AI player has gold and debt, deduct gold for debt payment
			if ( (player):gold() > 0 ) and ( _G.Debt[player] > 0 ) then
				if ( (player):gold() > _G.Debt[player] ) then
					payment=_G.Debt[player]
				else
					payment=( (player):gold() )
				end
							
				_G.Debt[player]=( _G.Debt[player] - payment )
				-- the next command uses "change by" amount rather than "change to" amount
				edit.change_gold( player, -payment )
			end
		end
	end

	if ( debugLuaScripts ) then log.error("%s","AiTribesCityDefenderCheck END") end

end
signal.connect("turn_started", "Lua_AiTribesCityDefenderCheck")


--##############################################################################
function Lua_AiTribesCityDefender(unit, src_tile, dst_tile)

	-- If a computer-controlled player moves the last unit in a city out,
	-- then put it back and remove all unit movement points.
	-- The last unit in the city can't leave so that the city always has at
	-- least one defender.
	-- This allows the computer to choose a Phalanx as a defender for the city,
	-- and then move an existing Warriors unit out, or disband it without
	-- triggering a replacement Warriors unit since city is not empty.

	-- Declare global variables if needed
	if ( AiTribesCityDefender == nil ) then AiTribesCityDefender=true end

	-- If this routine is disabled, just return without doing anything else
	if not ( AiTribesCityDefender ) then return end

	if ( debugLuaScripts ) then log.error("%s","AiTribesCityDefender BEGIN") end

	local player=(unit).owner

	if ( (player).ai_controlled )
	and ( (src_tile):city() ) 
	and ( (src_tile):num_units() == 0 ) then 

		-- Move the unit back into the city and expend all movement points
		edit.unit_move(unit,src_tile,99)

		-- To accomodate the separate Lua_LabelUnhomedUnits routine
		-- Remove Unhomed tile label from dst_tile
		if ( (unit).homecity == 0 ) then
			edit.tile_set_label(dst_tile,"")
		end
	end

	if ( debugLuaScripts ) then log.error("%s","AiTribesCityDefender END") end
	
end
signal.connect("unit_moved", "Lua_AiTribesCityDefender")
The attached playable scenario file "RandoMap" for Freeciv 2.6.4 and later has only the "AITribesCityDefender" script. The other scenario: "FairTEST", includes all the scripts from this topic (and currently both Embark and Military-to-Work run successfully when certain military units disband next to Ocean tiles -- resulting in a free unit (Workers, Diplomat, or Explorer depending on known technologies), and the Embarked military unit.)
Attachments
FairTEST 55x50=3k, 96% land, Classic, Flat, v2.13.0.sav.zip
(21.27 KiB) Downloaded 234 times
RandoMap with Lua_AiTribesCityDefender.sav.zip
(9.61 KiB) Downloaded 235 times
Post Reply