[INCLUDE] CustomUtilitiesPack.lua v0.00

ColBashar

Chieftain
Joined
Nov 3, 2010
Messages
32
CustomUtilitiesPack.lua is an include file for map scripts, providing scripters with a set of tools to help make developing advanced maps simpler and easier. At present, the file is in its beta stage. This means that changes to it may not support backward compatibility. As such, I do not recommend anybody using it in a map script intended for publication until the release of version 1.00. Upon version 1.00, backward compatibility will be an important feature. This is why I wanted to first run the beta by the forum community to collect feedback. Any major changes to be made can be addressed now before it becomes an issue.

The main tool that this file makes available is the AreaMap object. This allows lua tables to be manipulated as if they were a two-dimensional array. It also provides tools to manipulate this data, allowing for cropping, stretching, copying, masking, and other filters.

Documentation is provided in the include file itself, explaining the various functions; however, I will provide some examples here.

Code:
-- To designate a variable as an area map object, call the function:
local yourMap = AreaMap.Create(iWidth, iHeight, iDefault_Value, bWraps_X-axis, bWraps_y-axis)

-- Now that the area map has been created, it can be manipulated in
--   a number of ways.
-- This will generate fractal noise on the map:
yourMap:FiraxisFractal(iGrain_of_Fractal, fBlend_amount)

-- The fractal grain determines roughness of the noise, and accepts
--   positive integers
-- The blend amount is a float ranged 0-1 that will determine how much
--   the fractal will be blended with any previous data in the area map.
--   For example, if we ran a second Fractal() function...

yourMap:FiraxisFractal(1, 0.5)

-- ... after the first one, the results of the second fractal would be
--   mixed equally with that generated by the first.

-- We might want numbers of a range other than 0-1, though, so next we
--   will scale the map's values:

yourMap:Scale(floor, ceiling, mid_point)

-- The floor indicates the lowest number features on the area map while
--   the ceiling will set the highest value.  The mid_point attempts to
--   scale the average value of all the tiles to a particular value;
--   however, you can just supply "nil" for a straight scale.

-- Civ5 doesn't have a lot of use for float variables (i.e. non-integers)
--   so let's now see what we can do about that.  To convert a map's value
--   to integer form, you can use the following function:

yourMap:Integer()

-- or if we just wanted to abstract the map into binary values (0 or 1)
--   we could do the following:

yourMap:Boolean(threshold)

-- THis function will set the map's tiles to either 1 or 0 depending on
--   whether an individual tile is equal or greater than the threshold
--   value (assigned to 1) or below the threshold value (assigned to 0).
-- Note: This will probably be renamed Binary() in the next update.

-- Okay, so we have this binary map... let's turn that into something
--   useful!  Watch closely:

local iW, iH = Map.GetGridSize()
local mapPlots = AreaMap.Create(iW, iH, PlotTypes.PLOT_LAND, true, false)
mapPlots:Stamp(yourMap, PlotTypes.PLOT_OCEAN, 0, 0, false)

-- That might look confusing at first, but let me explain what happened.
--   First, we declared the variables iW and iH and assigned them the
--   width and height of our game map.  Then we created a new Area Map
--   called mapPlots.  We made mapPlots the same size as our game map,
--   and filled it with the default value Civ5 uses as ocean.
-- The interesting parts comes with the Stamp() function.  What that did
--   was took the area map we had just made binary above and used it to
--   apply ocean to mapPlots.  The way it works is that it looks at each
--   tile in yourMap.  If a tile was 0, then nothing happened.  Otherwise,
--   if the tile was -not- 0, then the value PlotTypes.PLOT_OCEAN was
--   assigned to the comparable tile on mapPlots.
-- Here's a more detailed look at what the arguments do:

Stamp(map_to_apply, value_to_multiply, x-axis_offset, y-axis_offset, bTileApplicationMap)

-- The first passed variable is the AreaMap to use as the stamp.  It
--   should first be run through a binary filter to function properly.
--   The second argument is the value to apply to the map when a non-zero
--   tile is found.  Actually, it's a multiplier, but since all non-zero
--   tiles should be 1, if you ran it through a binary filter, then the
--   argument is passed directly without modification.
-- The x- and y-axis offsets determine the positioning of the lower-left
--   tile (index 0) will be relative the two maps.  Positive values will
--   mean that the stamp will occur upwards and rightward while negative
--   values mean that the stamp will be positioned downwards and to the left.
--   Depending on plotMap's wrapping flags, the stamp may or may not
--   continue to be applied on the far boundaries of the map.
-- If the function is assigned to tile the stamp, then, if the map being
--   stamped is smaller than the map to which it is being stamped, the
--   stamping effect will repeat horizontally and vertically.

So now let's put these all together and write a function that will generate actual plots for a map script:

Code:
function GeneratePlotTypes(args)
	-- Here we're acquiring data from the game about our map
	local iW, iH = Map.GetGridSize();
	local flags = Map.GetFractalFlags();

	-- Using the map data, we're going to create an Area Map to house our plot information.
	local plotMap = AreaMap.Create(iW, iH, PlotTypes.PLOT_LAND, flags.WrapX, flags.WrapY);

	-- Now we're creating a new Area Map that is half in dimension of the game map.  We have arbitrarily told it to wrap horizontally and vertically.
	local heightMap = AreaMap.Create(iW * .5, iH * .5, 0, true, true)
	
	heightMap:FiraxisFractal(1,1);	-- Creating fractal noise
	heightMap:Boolean(.5);		-- Running the binary filter.  Since we know that the fractal generates results between 0 and 1, we can assume that a threshold value of 0.5 will cover roughly half the map.

	plotMap:Stamp(heightMap, PlotTypes.PLOT_HILLS, 0, 0, true);	-- We've now assigned hills to our plotMap.  Because we told the Stamp() to tile and because the heightMap is currently smaller than the map to which it is being applied, the hills will be stamped four times on the plot map.  Because we previously told the heightMap to toroidally wrap on both the x- and y-axis, the transition of the tiling will be perfectly smooth.

	-- Let's now rebuild the heightMap at normal dimensions
	heightMap = AreaMap.Create(iW, iH, 0, flags.WrapX, flags.WrapY)

	heightMap:FiraxisFractal(2,1);	-- We're running a new fractal, this one will be "rougher" than the first.
	heightMap:Boolean(.5);

	plotMap:Stamp(heightMap, PlotTypes.PLOT_OCEAN, 0, 0, false);  -- We've now stamped the plotMap with ocean tiles

	-- For the sake of simplicity, we'll ignore mountains for this example and move on right to getting our data to the game map itself.  I plan to write a function that will handle this but in the meantime we'll do it manually.

	-- The following two lines will run a nested loop that will cover each x- and y- coordinate on the map
	for x = 0, iW - 1, 1 do
		for y = 0, iH - 1, 1 do

			local plot = Map.GetPlot(x,y);	-- We're acquiring plot data from the game map
			local tile = plotMap:GetHeight(x,y);	-- Now we're acquiring tile data from the plotMap
			plot:SetPlotType(tile, false, false);	-- We've just assigned the data from our plotMap to the game map.  Yay!

		end
	end
end

This is just one example of how the AreaMap object can help make map generation easier. Other function, detailed in the include file, include but are not limited to:

Copy: Similar to stamp except that it copies all tiles from the application map to the initiating map, rather than just non-zero tiles. The offset features offer fine control over placement, making this useful for developing "multi-layered" maps.
Multiply: Similar to Copy except instead of overwriting values, this function will multiply the values of each map's corresponding tiles together. An excellent means of masking when used with binary filtered maps.
Stretch: Will magnify, shrink or warp an area map, stretching it to supplied dimensions
Canvas: Similar to stretch but only changes the dimensions of an area map without altering the value of the tiles. The map may be cropped or new areas will be filled with the map's low value.
Orphan: Searches for non-zero tiles and tallies the number of adjacent non-zero tiles. If that number falls within a designated range, the tile is set to zero. This is useful for removing single-tile islands.
SetHeight: The inverse of GetHeight. This will assign a value to a particular tile. Useful for people who can't quite wrap their head around the concept of using single-dimensional arrays to plot two dimensional matrices.
Hex: This function returns the indices of tiles on an area map which are a specified distance from a selected tile. This is very handy for building influence maps, especially when you want to find tiles more than two tiles away. It automatically checks for map boundaries and wrapping.
Solarise: Runs an area map though a solar filter. These are excellent for generating channels of water or mountain ranges, depending on its application.
Fractal: This is an experimental fractal generator. While it's "functional", it doesn't yet produce acceptable results. Once I've worked out the kinks, it should generate fractals without the limitations of the one Firaxis has provided.

If you've actually read this far, then you'll probably want to know how to install the include file. After downloading it from the forum, extract the archived file to the following directory:
Code:
[Civ5 Directory]\assets\Gameplay\Lua

To use in your map script, add the following line to the top of your script file:
Code:
include("CustomUtilitiesPack");

As I said, though, at this stage I am primarily looking for feedback, particularly with regard to improving user-friendliness. To anyone who is a map scripter, or anybody thinking of developing one, is a more developed version of this something you think you would use? What functions not already supplied might you find useful? Do you think any of the function names could be made clearer?

Also, is there a way of effectively overloading functions? I believe lua has some kind of overloading capability but, as we're working with a typeless script, I haven't figured out how to set it up. Perhaps someone with more lua experience than I can help.

A word of warning, while I did perform a great deal of testing, there may still be a few bugs in the script. I'd actually be surprised if there weren't. If you run in to an exception or anomaly, please let me know and I'll try to fix it or provide a workaround as soon as possible. Thank you in advance for your patience and understanding.
 

Attachments

  • Civ5_Include_CustomUtilities_v0.00.zip
    11.5 KB · Views: 374
Okay. So, after a couple of months on hiatus I've come back to work on my Civ5 scripts nut now I'm getting an error that I can't track down and don't quite understand. I'm hoping someone with more experience with Lua might be able to point me in the right direction as to what's going wrong and how to fix it.

For some reason my CustomUtilitiesPack include file ceases to work. It used to work but not anymore. Here's the report I get when I try to run it with the World Builder:

Code:
The world size is Duel
Syntax Error: [string "Assets\Gameplay\Lua\AssignStartingPlots.lua"]:7631: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\AssignStartingPlots.lua"]:7598: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:382: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\AssignStartingPlots.lua"]:6029: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:379: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\AssignStartingPlots.lua"]:2396: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:378: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:515: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:743: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:375: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:450: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:743: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:374: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:441: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:348: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:255: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:208: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:326: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:249: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:43: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:116: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:172: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\TerrainGenerator.lua"]:185: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:43: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:115: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:169: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\TerrainGenerator.lua"]:44: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\MapGenerator.lua"]:20: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FractalWorld.lua"]:34: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\FeatureGenerator.lua"]:135: chunk has too many syntax levels
Syntax Error: [string "Assets\Gameplay\Lua\TerrainGenerator.lua"]:44: chunk has too many syntax levels
Syntax Error: [string "Assets\Maps\CustomUtilitiesPack_Test.lua"]:78: chunk has too many syntax levels
Map Script: Generating Map
Map Script: Generating Plot Types (Lua Continents) ...
Runtime Error: [string "Assets\Maps\CustomUtilitiesPack_Test.lua"]:185: attempt to index global 'AreaMap' (a nil value)
stack traceback:
	[string "Assets\Maps\CustomUtilitiesPack_Test.lua"]:185: in function 'GeneratePlotTypes'
	[string "Assets\Maps\CustomUtilitiesPack_Test.lua"]:229: in function 'GeneratePlotTypes'
	[string "Assets\Gameplay\Lua\MapGenerator.lua"]:783: in function <[string "Assets\Gameplay\Lua\MapGenerator.lua"]:775>
	[C]: ?

In that instance I was using a custom map script based on Continents but I ruled out a problem with the map script itself by applying the include file to Terra.lua and using it to call my AreaMap.Create() function. The Terra map generated as normal but I got a similar error report regarding syntax and a nil valued AreaMap object.

Can anyone help me understand why this is occurring? It seems strange to me that the inclusion of my file would generate errors with other include files that it does not call. I tried applying both my current include file as well as the somewhat buggier one I uploaded here to the forum and both produce the same result. Could it have something to do with a patch? That's the only change I can think of from then to now.
 
Hello Nefliqus,

The AreaMap pseudo class is really just a container for data in a grid and a collection of tools for manipulating that data. I included some functions to translate the AreaMap into landmass and terrain information that Civ5 would actually implement, but nothing for features, wonders, units, etc. Theoretically it could be used to manage those things; however, since I started getting errors I can't account for I've stopped working on the project. The Mirror function needed work anyway, since I didn't yet tell it how to deal with the half-tile offset of even rows on a hex grid.

I have a newer version of the lua file that fixed some errors and added a few new functions to the version I posted on here but there's no point in my uploading up until the "chunk has too many syntax levels" problem is solved. In the meantime I leave it here as a curiosity or in case somebody wants to pore through it for ideas. Oh, and in hindsight, for anybody who might take a page from this file I suggest writing functions that accept a single argument of a variable array, like Firaxis did, rather than my method of passing multiple arguments of individual variables. I thought it would be easier to read but in the long run it makes it more difficult to modify and update the script. I planned on changing that in a future version.
 
Hello Nefliqus,

The AreaMap pseudo class is really just a container for data in a grid and a collection of tools for manipulating that data. I included some functions to translate the AreaMap into landmass and terrain information that Civ5 would actually implement, but nothing for features, wonders, units, etc. Theoretically it could be used to manage those things; however, since I started getting errors I can't account for I've stopped working on the project. The Mirror function needed work anyway, since I didn't yet tell it how to deal with the half-tile offset of even rows on a hex grid.

I have a newer version of the lua file that fixed some errors and added a few new functions to the version I posted on here but there's no point in my uploading up until the "chunk has too many syntax levels" problem is solved. In the meantime I leave it here as a curiosity or in case somebody wants to pore through it for ideas. Oh, and in hindsight, for anybody who might take a page from this file I suggest writing functions that accept a single argument of a variable array, like Firaxis did, rather than my method of passing multiple arguments of individual variables. I thought it would be easier to read but in the long run it makes it more difficult to modify and update the script. I planned on changing that in a future version.

I'm thinking about preparing script that will build 4 corners map mirrored from 1 corner quoter. I want to do it in smarten way than I did it in my NOVAL mirror map script. I like your idea to manipulate/mirror MapAreas not just plots. I hope that you will continue your work and fix "chunk has too many syntax levels" problem.
 
Top Bottom