It looks like you're using a browser that does not support CSS - why don't you upgrade to a totally free new browser like Firefox?

Report Data

First step in making a report is deciding what data to use, and where that data comes from. For this report, we're looking at unit HP, which comes from RGDs in the attrib\ebps\races folder. For each RGD we want to know:
  • HP (GameData\health_ext\hitpoints)
  • Name (GameData\ui_ext\screen_name)
  • Is the unit only in the campaign? (filename will have "sp" in it)
A rough idea of the process is also helpful:
  1. Make list of units that have HP
  2. Sort list according to HP
  3. Print list

Making the unit list

We will have a table called unit_list into which suitable RGDs are placed: (The first line here is a neat little hack to put methods from table into the table itself)
unit_list = setmetatable({}, {__index = table})
 
function each_file(rgd)
	if rgd:GET("GameData","health_ext","hitpoints") then
		unit_list:insert(rgd)
	end
end

Sorting the list

By using the at_end function, we can carry out an action after all the RGDs have been processed. This is the perfect time for sorting our list: (no need to use GET() as we checked that the RGD had a hitpoint value before inserting it into the list)
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
end

Printing the list

We can only print the list after we've sorted it, so we will add printing code after the sorting code. For now we'll print the filename of the rgd (minus the initial "attrib\attrib\ebps\") along with the hitpoint value:
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	for pos,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		print("#" .. pos .. ": " .. unit_name .. " ("
		.. rgd.GameData.health_ext.hitpoints .. " hp)")
	end
end

Full macro, version 1

Here is the complete macro so far:
unit_list = setmetatable({}, {__index = table})
 
function each_file(rgd)
	if rgd:GET("GameData","health_ext","hitpoints") then
		unit_list:insert(rgd)
	end
end
 
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	for pos,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		print("#" .. pos .. ": " .. unit_name .. " ("
		.. rgd.GameData.health_ext.hitpoints .. " hp)")
	end
end
The output should consist of lots of lines like these:
...
#14: races\axis\buildings\hq_3.rgd (1500 hp)
#15: races\allies\buildings\hq_4.rgd (1500 hp)
#16: races\allies\buildings\hq_sp_m06.rgd (1500 hp)
#17: races\allies\buildings\hq_2.rgd (1500 hp)
#18: races\axis\vehicles\tiger_ace.rgd (1500 hp)
#19: races\axis\buildings\hq_sp_noterritory.rgd (1500 hp)
#20: races\allies\buildings\hq_3.rgd (1500 hp)
...

Replacing file name with unit name

Let's change the printing code to get the value of GameData\ui_ext\screen_name, turn it into text if it's a UCS reference, and then print that in addition to the file name. If screen_name is "$0" (eg, the default UCS reference), then we'll treat it as if screen_name didn't exist. We'll then use the UCS() function to turn the UCS reference into normal text:
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	for pos,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		screen_name = rgd.GameData:GET("ui_ext","screen_name")
		if screen_name and screen_name != "$0"
			screen_name = UCS(screen_name)
			unit_name = screen_name.." ("..unit_name..")"
		end
		print("#" .. pos .. ": " .. unit_name .. " ("
		.. rgd.GameData.health_ext.hitpoints .. " hp)")
	end
end
Output is now something like this:
...
#14: Reich Headquarters (races\axis\buildings\hq_3.rgd) (1500 hp)
#15: Headquarters (races\allies\buildings\hq_4.rgd) (1500 hp)
#16: Headquarters (races\allies\buildings\hq_sp_m06.rgd) (1500 hp)
#17: Headquarters (races\allies\buildings\hq_2.rgd) (1500 hp)
#18: Tiger Ace (races\axis\vehicles\tiger_ace.rgd) (1500 hp)
...

Filtering out campaign files

Campaign files all have "sp" appear in the filename (eg. "rifleman_sp.rgd"), but not every file with "sp" in the name is from the campaign (eg. "spartaaa.rgd"). Therefore the problem is slightly more complex than looking for "sp" in the name. A better filter is: slash/underscore followed by "sp" followed by slash/underscore/dot. If a file path matches that, we'll say it's from the campaign. Therefore we'll only add a file to our list if it doesn't match that:
unit_list = setmetatable({}, {__index = table})
campaign_file = "[/_\\]sp[/_%.\\]"
 
function each_file(rgd)
	if (not rgd.path:find(campaign_file)) and
		rgd:GET("GameData","health_ext","hitpoints") then
		unit_list:insert(rgd)
	end
end
Output is looking slightly better:
...
#12: Headquarters (races\allies\buildings\hq_3.rgd) (1500 hp)
#13: Tiger Ace (races\axis\vehicles\tiger_ace.rgd) (1500 hp)
#14: Headquarters (races\allies\buildings\hq.rgd) (1500 hp)
#15: Tiger (races\axis\vehicles\tiger.rgd) (1064 hp)
#16: Tiger Ace (races\axis\vehicles\tiger_spg_ace.rgd) (1064 hp)
#17: M26 Pershing (races\allies\vehicles\m26_pershing.rgd) (990 hp)
...

Filtering out duplicates

After looking at the output, you'll see that many units have several variations with the same name and hp value. We can filter these out by keeping a list of unit names and hp values, and before printing a result, checking if it is unique. One caveat is that we can no longer use the 'pos' variable given to us from ipairs as we may choose not to print some entries:
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	local pos = 0
	for _,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		screen_name = rgd.GameData:GET("ui_ext","screen_name")
		local hp = rgd.GameData.health_ext.hitpoints
		local unique = true
		if screen_name and screen_name != "$0"
			screen_name = UCS(screen_name)
			unit_name = screen_name.." ("..unit_name..")"
			if unit_list[screen_name] == hp
				unique = false
			else
				unit_list[screen_name] = hp
			end
		end
		if unique
			pos = pos + 1
			print("#" .. pos .. ": " .. unit_name .. " ("
			.. rgd.GameData.health_ext.hitpoints .. " hp)")
		end
	end
end
Output at this point is looking rather good:
...
#3: Tiger Ace (races\axis\vehicles\tiger_ace.rgd) (1500 hp)
#4: Tiger (races\axis\vehicles\tiger.rgd) (1064 hp)
#5: Tiger Ace (races\axis\vehicles\tiger_spg_ace.rgd) (1064 hp)
#6: M26 Pershing (races\allies\vehicles\m26_pershing.rgd) (990 hp)
...

Giving equal places equal numbers

You may have noticed that the top few results have the same HP value, but different numbers at the start. To finish, let's fix this by remembering the HP value of the previous unit and only incrementing the pos variable if this unit's HP is different:
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	local pos = 0
	local prev_hp
	for _,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		screen_name = rgd.GameData:GET("ui_ext","screen_name")
		local hp = rgd.GameData.health_ext.hitpoints
		local unique = true
		if screen_name and screen_name != "$0"
			screen_name = UCS(screen_name)
			unit_name = screen_name.." ("..unit_name..")"
			if unit_list[screen_name] == hp
				unique = false
			else
				unit_list[screen_name] = hp
			end
		end
		if unique
			if hp ~= prev_hp
				pos = pos + 1
			end
			prev_hp = hp
			print("#" .. pos .. ": " .. unit_name .. " ("
			.. rgd.GameData.health_ext.hitpoints .. " hp)")
		end
	end
end
At this point, the report is complete.
...
#35: Mortar Team (races\allies\soldiers\mortar_gunner.rgd) (55 hp)
#35: Engineers (races\allies\soldiers\engineer.rgd) (55 hp)
#35: Repair Engineer (races\allies\soldiers\repair_engineer.rgd) (55 hp)
#36: Mines (races\allies\mines\allies_greyhound_drop_mine.rgd) (20 hp)

Full macro, version 2

Here is the complete macro: (included in the mod studio macro folder as "CoH Unit List by HP.lua"
unit_list = setmetatable({}, {__index = table})
campaign_file = "[/_\\]sp[/_%.\\]"
 
function each_file(rgd)
	if (not rgd.path:find(campaign_file)) and
		rgd:GET("GameData","health_ext","hitpoints") then
		unit_list:insert(rgd)
	end
end
 
function at_end()
	unit_list:sort( [ (a,b)
		| a.GameData.health_ext.hitpoints
		> b.GameData.health_ext.hitpoints
	] )
	local pos = 0
	local prev_hp
	for _,rgd in ipairs(unit_list)
		local unit_name = rgd.path:after "ebps\\"
		screen_name = rgd.GameData:GET("ui_ext","screen_name")
		local hp = rgd.GameData.health_ext.hitpoints
		local unique = true
		if screen_name and screen_name != "$0"
			screen_name = UCS(screen_name)
			unit_name = screen_name.." ("..unit_name..")"
			if unit_list[screen_name] == hp
				unique = false
			else
				unit_list[screen_name] = hp
			end
		end
		if unique
			if hp ~= prev_hp
				pos = pos + 1
			end
			prev_hp = hp
			print("#" .. pos .. ": " .. unit_name .. " ("
			.. rgd.GameData.health_ext.hitpoints .. " hp)")
		end
	end
end

Back to tutorial list
This documentation is provided under the GNU General Public License. All trademarks / copyrights are tm/r/c their respective owners.