-- This is a player context script for MoO2 1.50 patch project.
--
-- This tactical combat script shows a damage report of your currently active ship, showing exact shield values and
-- partial damage of subsystems and of the next weapon to be destroyed for every weapon stack.
-- There are several options which allow a different style of presentation as well as additional information to display.
--
-- To run the script press 8 in main screen (the file should be named MAIN8.LUA)

local ALIGN_NUMBERS_RIGHT = false
local SHOW_WEAPONS_LOST_IN_SLOT_IF_ANY = false
local SHOW_COMPUTER_DAMAGE = false
local SHOW_SHIELD_GENERATOR_DAMAGE = false
local SHOW_DRIVE_DAMAGE = false
local SHOW_MARINES = false
local SHOW_ABSORBER_CHARGE_IF_PRESENT = false
local SHOW_PLASMA_WEBS_DAMAGE_IF_PRESENT = false

-- if the left term has to be shortened to fit, then use the right term instead
CUSTOM_ABBREVIATIONS = {
	['example term that is too long'] = 'shorter version'
}

-- anything below this line is not supposed to be changed

ALIGN_LEFT = 'left'
ALIGN_MIDDLE = 'middle'
ALIGN_RIGHT = 'right'

ABBREVIATION_CHARACTER = '.'

--[[ NOTES: 
font index (FONT.LBX) is 3, distance between two characters is 2 pixels
@ is displayed as ~
^ is displayed as •
| is displayed empty 1 pixel wide (producing a 3 pixel gap between characters)
~ is displayed is displayed as 0 pixel wide (producing a 2 pixel gap between characters)
--]]

CHARACTER_TO_PIXEL_WIDTH = {
	[' '] = 3, ['!'] = 2, ['"'] = 6, ['#'] = 7, ['$'] = 6, ['%'] = 11,['&'] = 7, ["'"] = 4, ['('] = 3, [')'] = 3, ['*'] = 13,
	['+'] = 7, [','] = 3, ['-'] = 4, ['.'] = 2, ['/'] = 4, ['0'] = 6, ['1'] = 4, ['2'] = 6, ['3'] = 6, ['4'] = 7,
	['5'] = 6, ['6'] = 6, ['7'] = 6, ['8'] = 6, ['9'] = 6, [':'] = 2, [';'] = 3, ['<'] = 6, ['='] = 6, ['>'] = 6,
	['?'] = 6, ['@'] = 10,['A'] = 8, ['B'] = 8, ['C'] = 7, ['D'] = 8, ['E'] = 7, ['F'] = 7, ['G'] = 8, ['H'] = 8,
	['I'] = 2, ['J'] = 6, ['K'] = 8, ['L'] = 7, ['M'] = 10,['N'] = 8, ['O'] = 8, ['P'] = 8, ['Q'] = 8, ['R'] = 9,
	['S'] = 8, ['T'] = 8, ['U'] = 8, ['V'] = 8, ['W'] = 14,['X'] = 9, ['Y'] = 10,['Z'] = 9, ['['] = 3, ['\\'] = 4,
	[']'] = 3, ['^'] = 9, ['_'] = 8, ['`'] = 3, ['a'] = 6, ['b'] = 6, ['c'] = 6, ['d'] = 6, ['e'] = 6, ['f'] = 4,
	['g'] = 6, ['h'] = 6, ['i'] = 2, ['j'] = 3, ['k'] = 6, ['l'] = 2, ['m'] = 10,['n'] = 6, ['o'] = 6, ['p'] = 6,
	['q'] = 6, ['r'] = 5, ['s'] = 6, ['t'] = 4, ['u'] = 6, ['v'] = 8, ['w'] = 12,['x'] = 8, ['y'] = 8, ['z'] = 6,
	['{'] = 4, ['|'] = 1, ['}'] = 4, ['~'] = 0,
}
DISTANCE_BETWEEN_CHARACTERS = 2
MAXIMUM_WIDTH_FOR_MSGBOX = 339
ROW_SEPARATOR_CHARACTER = ' ' -- only a single character is supported yet, need to adjust ROW_SEPARATOR_WIDTH otherwise
ROW_SEPARATOR_WIDTH = DISTANCE_BETWEEN_CHARACTERS + CHARACTER_TO_PIXEL_WIDTH[ROW_SEPARATOR_CHARACTER] + DISTANCE_BETWEEN_CHARACTERS
ADD_TWO_PIXELS_CHARACTER = '~'
ADD_THREE_PIXELS_CHARACTER = '|'
ADD_FOUR_PIXELS_CHARACTER = '~~'
ADD_FIVE_PIXELS_CHARACTER = ' '
ABBREVIATION_CHARACTER_WIDTH = CHARACTER_TO_PIXEL_WIDTH[ABBREVIATION_CHARACTER]

function get_pixel_width_of_string(input)
	local str = tostring(input)
	if str == nil then
		return 0
	end
	local width = 0
	local gain = 0
    for i = 1, #str do
        gain = CHARACTER_TO_PIXEL_WIDTH[str:sub(i, i)]
		if gain ~= nil then
			width = width + gain + DISTANCE_BETWEEN_CHARACTERS
		end
    end
	if #str > 0 then -- first character produces no spacing-between-characters
		width = width - DISTANCE_BETWEEN_CHARACTERS
	end
	return width
end

function trim(str)
	for i = #str, 0, -1 do
		if str:sub(i, i) ~= ' ' then
			return str:sub(0, i)
		end
	end
end

function shorten_without_aligment(input, width_to_lose, desired_pixel_width)
	local str = tostring(input)
	if #str == 0 then
		return str
	end
    for i = #str, 0, -1 do
        loss = CHARACTER_TO_PIXEL_WIDTH[str:sub(i, i)]
		if loss ~= nil then
			width_to_lose = width_to_lose - loss - DISTANCE_BETWEEN_CHARACTERS
		end
		if width_to_lose + DISTANCE_BETWEEN_CHARACTERS + ABBREVIATION_CHARACTER_WIDTH <= 0 then
			str = str:sub(0 ,i - 1)
			break
		end
    end
	return trim(str) .. ABBREVIATION_CHARACTER
end

function fill_up_string_left_aligned(str, missing_width, desired_pixel_width)
	if missing_width == 2 then
		str = str .. ADD_TWO_PIXELS_CHARACTER
	elseif missing_width == 3 then
		str = str .. ADD_THREE_PIXELS_CHARACTER
	elseif missing_width == 4 then
		str = str .. ADD_FOUR_PIXELS_CHARACTER
	elseif missing_width >= 5 then
		local divisible_by_5_remainder = missing_width % 5
		if divisible_by_5_remainder == 4 then -- 9->5, 14->10, ...
			str = str .. ADD_FOUR_PIXELS_CHARACTER
			missing_width = missing_width - 4
		elseif divisible_by_5_remainder == 3 then -- 8->5, 13->10, ...
			str = str .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 3
		elseif divisible_by_5_remainder == 2 then -- 7->5, 12->10, ...
			str = str .. ADD_TWO_PIXELS_CHARACTER
			missing_width = missing_width - 2
		elseif divisible_by_5_remainder == 1 then -- 6->0, 11->5, ...
			str = str .. ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 6
		end
		if (desired_pixel_width - get_pixel_width_of_string(str)) % 5 ~= 0 then
			msgbox('unexpected remainder for str ' .. tostring(str) .. ': ' .. tostring(desired_pixel_width - get_pixel_width_of_string(str)) % 5) -- TODO: remove this after testing
		end
		str = str .. string.rep(ADD_FIVE_PIXELS_CHARACTER, (missing_width) / 5)
	end
	return str
end

function fill_up_string_right_aligned(str, missing_width, desired_pixel_width)
	if missing_width == 2 then
		str = ADD_TWO_PIXELS_CHARACTER .. str
	elseif missing_width == 3 then
		str = ADD_THREE_PIXELS_CHARACTER .. str
	elseif missing_width == 4 then
		str = ADD_FOUR_PIXELS_CHARACTER .. str
	elseif missing_width >= 5 then
		local divisible_by_5_remainder = missing_width % 5
		if divisible_by_5_remainder == 4 then -- 9->5, 14->10, ...
			str = ADD_FOUR_PIXELS_CHARACTER .. str
			missing_width = missing_width - 4
		elseif divisible_by_5_remainder == 3 then -- 8->5, 13->10, ...
			str = ADD_THREE_PIXELS_CHARACTER .. str
			missing_width = missing_width - 3
		elseif divisible_by_5_remainder == 2 then -- 7->5, 12->10, ...
			str = ADD_TWO_PIXELS_CHARACTER .. str
			missing_width = missing_width - 2
		elseif divisible_by_5_remainder == 1 then -- 6->0, 11->5, ...
			str = ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER .. str
			missing_width = missing_width - 6
		end
		if (desired_pixel_width - get_pixel_width_of_string(str)) % 5 ~= 0 then
			msgbox('unexpected remainder for str ' .. tostring(str) .. ': ' .. tostring(desired_pixel_width - get_pixel_width_of_string(str)) % 5)
		end
		str = string.rep(ADD_FIVE_PIXELS_CHARACTER, (missing_width) / 5) .. str
	end
	return str
end

function fill_up_string_middle_aligned(str, missing_width, desired_pixel_width)
	if missing_width == 2 then
		str = ADD_TWO_PIXELS_CHARACTER .. str
	elseif missing_width == 3 then
		str = ADD_THREE_PIXELS_CHARACTER .. str	
	elseif missing_width == 4 then
		str = ADD_TWO_PIXELS_CHARACTER .. str .. ADD_TWO_PIXELS_CHARACTER
	elseif missing_width == 5 then
		str = ADD_THREE_PIXELS_CHARACTER .. str .. ADD_TWO_PIXELS_CHARACTER
	elseif missing_width == 6 then
		str = ADD_THREE_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER
	elseif missing_width == 7 then
		str = ADD_FOUR_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER
	elseif missing_width == 8 then
		str = ADD_FOUR_PIXELS_CHARACTER .. str .. ADD_FOUR_PIXELS_CHARACTER
	elseif missing_width == 9 then
		str = ADD_FIVE_PIXELS_CHARACTER .. str .. ADD_FOUR_PIXELS_CHARACTER
	elseif missing_width >= 10 then
		local divisible_by_10_remainder = missing_width % 10
		if divisible_by_10_remainder == 9 then
			str = ADD_FIVE_PIXELS_CHARACTER .. str .. ADD_FOUR_PIXELS_CHARACTER
			missing_width = missing_width - 9
		elseif divisible_by_10_remainder == 8 then
			str = ADD_FOUR_PIXELS_CHARACTER .. str .. ADD_FOUR_PIXELS_CHARACTER
			missing_width = missing_width - 8
		elseif divisible_by_10_remainder == 7 then
			str = ADD_FOUR_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 7
		elseif divisible_by_10_remainder == 6 then
			str = ADD_THREE_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 6
		elseif divisible_by_10_remainder == 5 then
			str = ADD_THREE_PIXELS_CHARACTER .. str .. ADD_TWO_PIXELS_CHARACTER
			missing_width = missing_width - 5
		elseif divisible_by_10_remainder == 4 then -- 14 -> add 2, 2 -> remaining 5, 5
			str = ADD_TWO_PIXELS_CHARACTER .. str .. ADD_TWO_PIXELS_CHARACTER
			missing_width = missing_width - 4
		elseif divisible_by_10_remainder == 3 then -- 13 -> add 7, 6 -> remaining 0, 0
			str = ADD_FOUR_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 13
		elseif divisible_by_10_remainder == 2 then -- 12 -> add 6, 6 -> remaining 0, 0
			str = ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER .. str .. ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER
			missing_width = missing_width - 12
		elseif divisible_by_10_remainder == 1 then -- 11 -> add 6, 5 -> remaining 0, 0
			str =  ADD_THREE_PIXELS_CHARACTER .. ADD_THREE_PIXELS_CHARACTER .. str .. ADD_FIVE_PIXELS_CHARACTER
			missing_width = missing_width - 11
		end
		if (desired_pixel_width - get_pixel_width_of_string(str)) % 10 ~= 0 then
			msgbox('unexpected remainder for str ' .. tostring(str) .. ': ' .. tostring(desired_pixel_width - get_pixel_width_of_string(str)) % 10)
		end
		local to_add = string.rep(ADD_FIVE_PIXELS_CHARACTER, (missing_width) / 10)
		str = to_add .. str .. to_add
	end
	return str
end

function align(input, desired_pixel_width, alignment_type)
	if alignment_type ~= ALIGN_LEFT and alignment_type ~= ALIGN_RIGHT and alignment_type ~= ALIGN_MIDDLE then
		msgbox("ERROR: Unknown alignment type: " .. tostring(alignment_type))
		return tostring(input)
	end
	if desired_pixel_width < DISTANCE_BETWEEN_CHARACTERS then
		-- attempting this would lead to an infinite while loop!
		msgbox("ERROR: cannot set pixel width to less than " .. tostring(DISTANCE_BETWEEN_CHARACTERS) .. " for\n'" .. tostring(input) .. "'")
		return ''
	end
	local str = tostring(input)
	if str == nil then
		str = '~'
	end
	local current_width = get_pixel_width_of_string(str)
	local missing_width = desired_pixel_width - current_width
	-- allow a hierarchy of abbreviations, because why not
	while CUSTOM_ABBREVIATIONS[str] ~= nil and missing_width < 0 do
		str = CUSTOM_ABBREVIATIONS[str]		
		current_width = get_pixel_width_of_string(str)	
		missing_width = desired_pixel_width - current_width			
	end
	if missing_width < 0 then
		str = align(shorten_without_aligment(str, -missing_width, desired_pixel_width), desired_pixel_width, alignment_type)
	elseif alignment_type == ALIGN_LEFT then
		str = fill_up_string_left_aligned(str, missing_width, desired_pixel_width)
	elseif alignment_type == ALIGN_RIGHT then
		str = fill_up_string_right_aligned(str, missing_width, desired_pixel_width)
	elseif alignment_type == ALIGN_MIDDLE then
		str = fill_up_string_middle_aligned(str, missing_width, desired_pixel_width)
	end
	collectgarbage()
	return str
end

function main()
	local ship = get_active_combat_ship()

	local function shorten_string(str, max_length)
		local formated_string = str
		if #formated_string > max_length then
			formated_string = formated_string:sub(0, max_length - 1) .. "."
		end
		return formated_string
	end


	local function format_tech_string(str, custom_tech_names)
		if custom_tech_names[str] ~= nil then
			return custom_tech_names[str]
		end
		if str == 'anti_missile_rocket' then
			return 'Anti-Missile Rockets'
		elseif str == 'anti_matter_bomb' then
			return 'Anti-Matter Bomb'
		elseif str == 'anti_matter_drive' then
			return 'Anti-Matter Drive'
		elseif str == 'anti_matter_torpedo' then
			return 'Anti-Matter Torpedo'
		elseif str == 'anti_grav_harness' then
			return 'Anti-Grav Harness'
		elseif str == 'bio_terminator' then
			return 'Bio-Terminator'
		elseif str == 'class_iii_shield' then
			return 'Class III Shield'
		elseif str == 'class_vii_shield' then
			return 'Class VII Shield'
		elseif str == 'cyber_security_link' then
			return 'Cyber-Security Link'
		elseif str == 'hyper_x_capacitors' then
			return 'Hyper-X Capacitors'
		elseif str == 'multi_wave_ecm_jammer' then
			return 'Multi-Wave Ecm Jammer'
		elseif str == 'robo_miners' then
			return 'Robo-Miners'
		elseif str == 'sub_space_communications' then
			return 'Sub-Space Communications'
		end
		local formated_string = str:sub(1,1):upper()
		local next_char_uppercase = false
		for i = 2, #str do
			if str:sub(i,i) == '_' then
				formated_string = formated_string .. ' '
				next_char_uppercase = true
			else
				if next_char_uppercase then
					formated_string = formated_string .. str:sub(i,i):upper()
					next_char_uppercase = false
				else
					formated_string = formated_string .. str:sub(i,i)
				end
			end
		end
		return formated_string
	end
			
	local function get_current_gun_hp(weapon)
		if weapon.ngood == 0 then
			return 0
		else
			return weapon.slot_HP - (weapon.slot_gun_HP * (weapon.ngood - 1))
		end
	end

	local function get_current_special_hp(special)
		if special.destroyed == 1 then
			return 0
		else
			return special.HP
		end
	end
	
	local function get_maxium_value(a, b)
		if a > b then
			return a
		else
			return b
		end
	end
	
	all_custom_tech_names = get_conf("tech_name")
	custom_tech_names = {}
	for original_name, custom_name in pairs(all_custom_tech_names) do
		if custom_name ~= 0 then
			custom_tech_names[original_name] = custom_name
		end
	end
	all_custom_tech_names = nil

	local max_marines = 0
	if SHOW_MARINES == true then
		local hull_config = get_conf("hull")
		if ship.drive_type == 'no_drive' then
			if ship.name == 'Star Base' then
				max_marines = hull_config['star_base'].marines
			elseif ship.name == 'Battlestation' then
				max_marines = hull_config['battlestation'].marines			
			elseif ship.name == 'Star Fortress' then
				max_marines = hull_config['star_fortress'].marines			
			end
		else
			max_marines = hull_config[ship.size_class].marines
		end
		hull_config = nil
		collectgarbage()
		for i, sp in pairs(ship.specials) do
			if sp.name == 'troop_pods' and sp.destroyed == 0 then
				max_marines = max_marines * 2
				break
			end
		end
	end
	
	collectgarbage()
	
	local n_a_width = CHARACTER_TO_PIXEL_WIDTH['-']
	local csd_current_value_group_width = n_a_width
	local csd_maximum_value_group_width = n_a_width
	local equipment_current_value_group_width = n_a_width
	local equipment_maximum_value_group_width = n_a_width
	local lost_group_width = CHARACTER_TO_PIXEL_WIDTH['1']
	if SHOW_COMPUTER_DAMAGE == true then
		csd_current_value_group_width = get_maxium_value(csd_current_value_group_width, get_pixel_width_of_string(ship.computer_HP))
		csd_maximum_value_group_width = get_maxium_value(csd_maximum_value_group_width, get_pixel_width_of_string(ship.computer_HP_base))
	end
	if SHOW_SHIELD_GENERATOR_DAMAGE == true then
		csd_current_value_group_width = get_maxium_value(csd_current_value_group_width, get_pixel_width_of_string(ship.shield_generator_HP))
		csd_maximum_value_group_width = get_maxium_value(csd_maximum_value_group_width, get_pixel_width_of_string(ship.shield_generator_HP_base))
	end
	if SHOW_DRIVE_DAMAGE == true then
		csd_current_value_group_width = get_maxium_value(csd_current_value_group_width, get_pixel_width_of_string(ship.drive_HP))
		csd_maximum_value_group_width = get_maxium_value(csd_maximum_value_group_width, get_pixel_width_of_string(ship.drive_HP_base))
	end
	if SHOW_MARINES == true then
		if ALIGN_NUMBERS_RIGHT == true then
			csd_current_value_group_width = get_maxium_value(csd_current_value_group_width, get_pixel_width_of_string(ship.marines))
			csd_maximum_value_group_width = get_maxium_value(csd_maximum_value_group_width, get_pixel_width_of_string(max_marines))
		else
			equipment_current_value_group_width = get_maxium_value(equipment_current_value_group_width, get_pixel_width_of_string(ship.marines))
			equipment_maximum_value_group_width = get_maxium_value(equipment_maximum_value_group_width, get_pixel_width_of_string(max_marines))
		end
	end
	for i, w in pairs(ship.weapons) do
		local weapons_lost_in_slot_string_width = 0
		if SHOW_WEAPONS_LOST_IN_SLOT_IF_ANY == true then
			local weapons_lost_in_slot = w.slot_HP_base / w.slot_gun_HP - w.ngood
			if weapons_lost_in_slot > 0 then
				lost_group_width = get_maxium_value(lost_group_width, get_pixel_width_of_string(weapons_lost_in_slot))
			end
		end
		equipment_current_value_group_width = get_maxium_value(equipment_current_value_group_width, get_pixel_width_of_string(get_current_gun_hp(w)))
		equipment_maximum_value_group_width = get_maxium_value(equipment_maximum_value_group_width, get_pixel_width_of_string(w.slot_gun_HP))
	end
	for i, sp in pairs(ship.specials) do
		equipment_current_value_group_width = get_maxium_value(equipment_current_value_group_width, get_pixel_width_of_string(get_current_special_hp(sp)))
		equipment_maximum_value_group_width = get_maxium_value(equipment_maximum_value_group_width, get_pixel_width_of_string(sp.HP_base))
	end
	if ALIGN_NUMBERS_RIGHT == true then
		csd_current_value_group_width = get_maxium_value(csd_current_value_group_width, equipment_current_value_group_width)
		equipment_current_value_group_width = csd_current_value_group_width
		csd_maximum_value_group_width = get_maxium_value(csd_maximum_value_group_width, equipment_maximum_value_group_width)
		equipment_maximum_value_group_width = csd_maximum_value_group_width
	end
	
	-- assuming largest possible is 'turn 444' -> 59; 
	output = align('Damage report for ' .. ship.name .. ' ', MAXIMUM_WIDTH_FOR_MSGBOX - 61, ALIGN_LEFT) .. align('turn ' .. tostring(get_combat_info().turn), 59, ALIGN_RIGHT) .. '\n'
	output = output .. '\n'
	local widest_shield_value_in_pixels = get_pixel_width_of_string(ship.shield_HP_arc[0])
	if get_pixel_width_of_string(ship.shield_HP_arc[1]) > widest_shield_value_in_pixels then
		widest_shield_value_in_pixels = get_pixel_width_of_string(ship.shield_HP_arc[1])
	end
	if get_pixel_width_of_string(ship.shield_HP_arc[2]) > widest_shield_value_in_pixels then
		widest_shield_value_in_pixels = get_pixel_width_of_string(ship.shield_HP_arc[2])
	end
	if get_pixel_width_of_string(ship.shield_HP_arc[3]) > widest_shield_value_in_pixels then
		widest_shield_value_in_pixels = get_pixel_width_of_string(ship.shield_HP_arc[3])
	end
	print('widest shield: ' .. tostring(widest_shield_value_in_pixels))
	-- For shields: assuming largest possible is 'R 4444' -> 50px
	-- add a whitespace between the three shield columns -> up to 50 + 7 + 50 + 7 + 50 = 164
	local shield_column_width = get_pixel_width_of_string('R ') + DISTANCE_BETWEEN_CHARACTERS + widest_shield_value_in_pixels
	local shield_all_columns_used_width = 3 * shield_column_width + 2 * ROW_SEPARATOR_WIDTH
	local shield_all_columns_and_fillup_width = 164
	output = output .. align(align('F ' .. align(ship.shield_HP_arc[0], widest_shield_value_in_pixels, ALIGN_RIGHT), shield_all_columns_used_width, ALIGN_MIDDLE), shield_all_columns_and_fillup_width, ALIGN_LEFT)
	-- assuming largest possible is 'Computer 444 / 444' -> 139px; 'Computer ' is 69, '444' is 25px -> 27px
	if SHOW_COMPUTER_DAMAGE == true then
		local computer_str = align('Computer', 69, ALIGN_LEFT)
		if ship.computer_HP_base == 0 then
			computer_str = computer_str .. align('-', csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		else
			computer_str = computer_str .. align(ship.computer_HP, csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' .. align(ship.computer_HP_base, csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		end
		output = output .. align(computer_str, MAXIMUM_WIDTH_FOR_MSGBOX - 166, ALIGN_RIGHT)
		computer_str = nil
	else
		output = output .. '\n'
	end
	output = output .. align(align('L ' .. align(ship.shield_HP_arc[3], widest_shield_value_in_pixels, ALIGN_RIGHT), shield_all_columns_used_width / 2, ALIGN_LEFT) .. align('R ' .. align(ship.shield_HP_arc[1], widest_shield_value_in_pixels, ALIGN_RIGHT), (shield_all_columns_used_width / 2) - 2, ALIGN_RIGHT), shield_all_columns_and_fillup_width, ALIGN_LEFT)
	if SHOW_SHIELD_GENERATOR_DAMAGE == true then
		local shield_str = align('Shield', 69, ALIGN_LEFT)
		if ship.shield_generator_HP_base == 0 then
			shield_str = shield_str .. align('-', csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		else
			shield_str = shield_str .. align(ship.shield_generator_HP, csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' .. align(ship.shield_generator_HP_base, csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		end
		output = output .. align(shield_str, MAXIMUM_WIDTH_FOR_MSGBOX - 166, ALIGN_RIGHT)
		shield_str = nil
	else
		output = output .. '\n'
	end
	output = output .. align(align('B ' .. align(ship.shield_HP_arc[2], widest_shield_value_in_pixels, ALIGN_RIGHT), shield_all_columns_used_width, ALIGN_MIDDLE), shield_all_columns_and_fillup_width, ALIGN_LEFT)
	if SHOW_DRIVE_DAMAGE == true then
		local drive_str = align('Drive', 69, ALIGN_LEFT)
		if ship.drive_HP_base == 0 then
			drive_str = drive_str .. align('-', csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		else
			drive_str = drive_str .. align(ship.drive_HP, csd_current_value_group_width, ALIGN_RIGHT) .. ' / ' .. align(ship.drive_HP_base, csd_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
		end
		output = output .. align(drive_str, MAXIMUM_WIDTH_FOR_MSGBOX - 166, ALIGN_RIGHT)
		drive_str = nil
	else
		output = output .. '\n'
	end	

	if SHOW_MARINES == true then
		output = output .. '\n'	
		local marine_string = 'Marines'
		if max_marines == 0 then
			if ALIGN_NUMBERS_RIGHT == true then
				-- largest amount possible is cur + ' / ' + max -> 14 + 6 + cur + max + 2 + 69 = cur + max + 91
				-- when aligned right, align with CSD, otherwise with equipment
				output = output .. align('', MAXIMUM_WIDTH_FOR_MSGBOX - 91 - csd_current_value_group_width - csd_maximum_value_group_width, ALIGN_LEFT) .. align(marine_string, 69, ALIGN_LEFT) .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '  ' .. marine_string .. '\n'
			end
		else
			if ALIGN_NUMBERS_RIGHT == true then
				output = output .. align('', MAXIMUM_WIDTH_FOR_MSGBOX - 91 - csd_current_value_group_width - csd_maximum_value_group_width, ALIGN_LEFT) .. align(marine_string, 69, ALIGN_LEFT) .. align(ship.marines, equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(max_marines, equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align(ship.marines, equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(max_marines, equipment_maximum_value_group_width, ALIGN_RIGHT) .. '  ' .. marine_string .. '\n'
			end
		end
	end

	output = output .. '\n'
	for i, w in pairs(ship.weapons) do
		local weapons_lost_in_slot_string = ''
		local weapons_lost_in_slot_string_width = 0
		if SHOW_WEAPONS_LOST_IN_SLOT_IF_ANY == true then
			local weapons_lost_in_slot = w.slot_HP_base / w.slot_gun_HP - w.ngood
			if weapons_lost_in_slot > 0 then
				-- 'Computer ' is 69
				weapons_lost_in_slot_string = 'lost ' .. align(weapons_lost_in_slot, lost_group_width, ALIGN_RIGHT)
				if ALIGN_NUMBERS_RIGHT == false then
					weapons_lost_in_slot_string = '  ' .. weapons_lost_in_slot_string
				end
				weapons_lost_in_slot_string_width = DISTANCE_BETWEEN_CHARACTERS + get_pixel_width_of_string(weapons_lost_in_slot_string)
			end
		end
		if w.slot_gun_HP == 0 then
			if ALIGN_NUMBERS_RIGHT == true then
				output = output .. align(format_tech_string(w.type, custom_tech_names), MAXIMUM_WIDTH_FOR_MSGBOX - 91 - equipment_current_value_group_width - equipment_maximum_value_group_width + 2 + 69, ALIGN_LEFT) .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '  ' .. format_tech_string(w.type, custom_tech_names) .. '\n'
			end
		else
			if ALIGN_NUMBERS_RIGHT == true then
				output = output .. align(format_tech_string(w.type, custom_tech_names), MAXIMUM_WIDTH_FOR_MSGBOX - 91 - equipment_current_value_group_width - equipment_maximum_value_group_width, ALIGN_LEFT) .. align(weapons_lost_in_slot_string, 69, ALIGN_LEFT) .. align(get_current_gun_hp(w), equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(w.slot_gun_HP, equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align(get_current_gun_hp(w), equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(w.slot_gun_HP, equipment_maximum_value_group_width, ALIGN_RIGHT) .. weapons_lost_in_slot_string .. '  ' .. tostring(format_tech_string(w.type, custom_tech_names)) .. '\n'
			end
		end
		weapons_lost_in_slot_string_width = nil
	end

	local has_absorber = false
	output = output .. '\n'
	for i, sp in pairs(ship.specials) do
		if sp.HP_base == 0 then
			if ALIGN_NUMBERS_RIGHT == true then
				output = output .. align(format_tech_string(sp.name, custom_tech_names), MAXIMUM_WIDTH_FOR_MSGBOX - 91 - equipment_current_value_group_width - equipment_maximum_value_group_width + 2 + 69, ALIGN_LEFT) .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align('-', equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align('-', equipment_maximum_value_group_width, ALIGN_RIGHT) .. '  ' .. format_tech_string(sp.name, custom_tech_names) .. '\n'
			end
		else
			if ALIGN_NUMBERS_RIGHT == true then
				output = output .. align(format_tech_string(sp.name, custom_tech_names), MAXIMUM_WIDTH_FOR_MSGBOX - 91 - equipment_current_value_group_width - equipment_maximum_value_group_width + 2 + 69, ALIGN_LEFT) .. align(get_current_special_hp(sp), equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(sp.HP_base, equipment_maximum_value_group_width, ALIGN_RIGHT) .. '\n'
			else
				output = output .. align(get_current_special_hp(sp), equipment_current_value_group_width, ALIGN_RIGHT) .. ' / ' ..  align(sp.HP_base, equipment_maximum_value_group_width, ALIGN_RIGHT) .. '  ' .. format_tech_string(sp.name, custom_tech_names) .. '\n'
			end
		end
		if sp.name == 'energy_absorber' then
			has_absorber = true
		end
	end
	if has_absorber == true and ship.absorber_charge > 0 and SHOW_ABSORBER_CHARGE_IF_PRESENT == true then
		output = output .. '\n'	
		output = output .. align('Damage stored in absorber ', 267, ALIGN_LEFT) .. align(ship.absorber_charge, MAXIMUM_WIDTH_FOR_MSGBOX - 267 - DISTANCE_BETWEEN_CHARACTERS, ALIGN_RIGHT) 
	end
	if ship.plasma_webs_damage >0 and SHOW_ABSORBER_CHARGE_IF_PRESENT == true then
		output = output .. '\n'	
		output = output .. align('Damage accumulated by plasma webs ', 267, ALIGN_LEFT) .. align(ship.plasma_webs_damage * 4, MAXIMUM_WIDTH_FOR_MSGBOX - 267 - DISTANCE_BETWEEN_CHARACTERS, ALIGN_RIGHT)
	end
	msgbox(output)
end

if type(get_active_combat_ship) ~= "function" then
	msgbox('report for tactical combat')
else
	main()
end