I was digging into the bit-level meanings of the gdm/png files for preserving heaps on the ground when heighttypes are increased, and it occurred to me that the same would be applicable for preserving work-angles, plow state, fertilization count, etc in updating for more terrain angles. For example, some of the youtube walkthroughs for this process mention that plow state is lost when you use the density map from a savegame.
Crudely, a pixel in the density for the default 2 angle channels has values
Code: Select all
AAAABBCC|DDDDDDDD|DDDDDDDD
red |green |blue
where A contains the current ground state (cultivated, seeded, ...), B seems to be assorted spray states (fert/herb, manure, lime, chopped straw, ...), C is the work angle, and D is other stuff (fertilization level, plow "counter") and a lot of unused bits.
If we add one angle channel in groundshader.xml and the map i3d file, and then load an fewer-angle png in giants editor, the data is read through our new bitmask
but the data wasn't written in that format. The fert level/plow counter and anything but a pure 0 degree work angle will be misinterpreted.
To preserve it, you can insert a processing step between using the GRLE converter and loading in giants editor. When adding a single angle channel, we convert the data to
just prepending the appropriate number of zeros to the original angle data, and shifting all of D to the right (dropping some thankfully-unused bits).
I wrote a quick python script to do this, and thought I'd share it in case anyone else might find it useful. I wrote it to be used from the command line in linux, but the update_pixel function should be easily adaptable to other workflows.
Code: Select all
#!/usr/bin/python3
import sys,argparse
from os.path import exists
from PIL import Image
def update_pixel(p,inchan,outchan):
pixel = p[0] + (256 * p[1]) + (65536 * p[2])
outpixel = (
(pixel & 0x3F) | #ground state. red masked by [00111111]
( (pixel & ~0x3F) << (outchan-inchan) ) ) #rest of data shifted left by #channels added, starting at bit 7 (maintains fert level, plow counter, etc)
#keeps old angle data at most-significant bits to save old angle data
return (outpixel % 256, outpixel // 256, outpixel // 65536)
def main(argv):
parser = argparse.ArgumentParser(description='Converts cultivator density map between # terrain angles without data loss')
parser.add_argument('-i', '--inchannels', action='store', type=int, default=2, dest='inchannels', help='# angle channels in input png')
parser.add_argument('-o', '--outchannels', action='store', type=int, dest='outchannels', required=True, help='# angle channels in output png')
parser.add_argument('-I', '--inpng', action='store', dest='infile', required=True, help='input png (output from GRLE converter)')
parser.add_argument('-O', '--outpng', action='store', dest='outfile', required=True, help='file to write')
args = parser.parse_args()
print(args)
print("in channels:", args.inchannels)
print("out channels:", args.outchannels)
print("in file:", args.infile)
print("out file:", args.outfile)
addchannels = args.outchannels - args.inchannels
if not exists(args.infile):
print("in file missing")
sys.exit(1)
elif exists(args.outfile):
print("out file exists. won't overwrite")
sys.exit(1)
elif args.inchannels < 2:
print("inchannels invalid")
sys.exit(1)
elif addchannels == 0:
print("nothing to do")
sys.exit(0)
elif addchannels < 0:
print("inchannels > outchannels. why?")
sys.exit(1)
img = Image.open(args.infile)
pixels = list(img.getdata())
new_pixels = [update_pixel(p,args.inchannels,args.outchannels) for p in pixels]
img.putdata(new_pixels)
img.save(args.outfile)
if __name__ == "__main__":
main(sys.argv[1:])
I haven't examined it exhaustively on a long-running save, but spotchecks in giants editor seem ok. To be clear, you would use the grle converter as before, run this script on its output, and then copy this script's result into the mapDE or mapUS folder (all other steps still required). There's unlikely to be any need for this extra step if you're working from the original cultivator_density.gdm provided with a map.
(related post for max height-types here:
viewtopic.php?f=1014&t=155894&start=30#p1415818)