Script - Bitching Betty

From FreeSpace Wiki
Jump to: navigation, search

This script is intended to emulate the "bitching Betty" mechanisms found on modern jet fighters and airliners. These are designed to play audio warnings whenever something requiring pilot attention comes up. This script was originally written for Blue Planet: War In Heaven.

How it works

Once gameplay starts, the script does its initialization. It scans the player ship for commonly used subsystem names, and starts to monitor ship status. As soon as one of the predefined triggers is hit, a message will be added to a message queue, which will then be processed with a definable gap between each message. This script relies on voice-acted audio cues that are defined in the mods' sounds.tbl, like so:

; Bitching Betty
$Name:	500	bettycomms.ogg,				0, 1.0, 0
$Name:	501	bettyengine.ogg,			0, 1.0, 0
$Name:	502	bettynav.ogg,				0, 1.0, 0
$Name:	503	bettysensors.ogg,			0, 1.0, 0
$Name:	504	bettyweapons.ogg,			0, 1.0, 0
$Name:	505	bettyhull.ogg,				0, 1.0, 0
$Name:	506	bettyeject.ogg,				0, 1.0, 0
$Name:	507	bettyprimaryammo1.ogg,		0, 1.0, 0
$Name:	508	bettyprimaryammo2.ogg,		0, 1.0, 0
$Name:	509	bettyprimaryammo3.ogg,		0, 1.0, 0
$Name:	510	bettysecondaryammo1.ogg,	0, 1.0, 0
$Name:	511	bettysecondaryammo2.ogg,	0, 0.50, 0
$Name:	512	bettysecondaryammo3.ogg,	0, 0.50, 0
$Name:	513	bettysecondaryammo4.ogg,	0, 0.50, 0
$Name:	514	bettysecondaryempty1.ogg,	0, 1.0, 0
$Name:	515	bettysecondaryempty2.ogg,	0, 0.50, 0
$Name:	516	bettysecondaryempty3.ogg,	0, 0.50, 0
$Name:	517	bettysecondaryempty4.ogg,	0, 1.0, 0
$Name:	518	bettyprimaryempty1.ogg,		0, 0.50, 0
$Name:	519	bettyprimaryempty2.ogg,		0, 0.50, 0
$Name:	520	bettyprimaryempty3.ogg,		0, 0.50, 0

The sound indices are hardcoded in the script, and need to match up with the sounds.tbl indices.

Things you should be aware of

  • This script can be disabled at runtime by setting the lua variable "BettyOverride" to true. This can be done via the script-eval sexp, or from within another script.
  • In BP:WiH, this script is only enabled for UEF fighters. The relevant checks have been commented out here; look at the part of the script running in the $On Frame: hook for details.

Table entry

;The bitching betty script. Monitors subsystem health status, and queues up audio warnings from sounds.tbl to blast at the player.

#Conditional Hooks

$Application: FS2_Open

$State: GS_STATE_GAME_PLAY

$On State Start:
[
function initbetty()
	if not bettyinited then
		betty = {}
		if not betty.plr then 
			betty.plr = hv.Player 
			if betty.plr:isValid() then
				betty.plrship = mn.getObjectFromSignature(betty.plr:getSignature())
			end
		end
		if betty.plrship:isValid() then
			betty.subsystems = {}
			betty.subsystems.comms = false
			betty.subsystems.engine = false
			betty.subsystems.engine01 = false
			betty.subsystems.engine02 = false
			betty.subsystems.engine03 = false
			betty.subsystems.engine04 = false
			betty.subsystems.nav = false
			betty.subsystems.sensors = false
			betty.subsystems.weapons = false
			betty.status = {}
			betty.status.secondarywarned = {false, false, false, false}
			betty.status.secondaryemptywarned = {false, false, false, false}
			betty.status.primarywarned = {}
			betty.status.primarywarned = {false, false, false}
			betty.status.primaryemptywarned = {}
			betty.status.primaryemptywarned = {false, false, false}
			betty.queue = {}
			betty.queueprocessdelay = 2 --Minimum delay for queue processing
			betty.comms =			500 --see sounds.tbl
			betty.engine =			501
			betty.nav = 			502
			betty.sensors = 		503
			betty.weapons = 		504
			betty.hull = 			505
			betty.eject = 			506
			betty.primary = {}
			betty.primary[1] =		507
			betty.primary[2] =		508
			betty.primary[3] =		509
			betty.secondary = {}
			betty.secondary[1] = 	510
			betty.secondary[2] = 	511
			betty.secondary[3] = 	512
			betty.secondary[4] = 	513
			betty.secondaryempty = {}
			betty.secondaryempty[1] = 514
			betty.secondaryempty[2] = 515
			betty.secondaryempty[3] = 516
			betty.secondaryempty[4] = 517
			betty.primaryempty = {}
			betty.primaryempty[1] = 518
			betty.primaryempty[2] = 519
			betty.primaryempty[3] = 520
			betty.subspace = 		521
			if not betty.subsystems.comms then
				if betty.plrship["communications"]:isValid() then
					betty.subsystems.comms = betty.plrship["communications"]
				end
			end
				
			if not betty.subsystems.engine then
				if betty.plrship["engine"]:isValid() then
					betty.subsystems.engine = betty.plrship["engine"]
					betty.enginenum = 0
				end
				if betty.plrship["engines"]:isValid() then
					betty.subsystems.engine = betty.plrship["engines"]
					betty.enginenum = 0
				end
			end
				
			if not betty.subsystems.engine01 then
				if betty.plrship["engine01"]:isValid() then
					betty.subsystems.engine01 = betty.plrship["engine01"]
					betty.enginenum = 1
				end
			end
			
			if not betty.subsystems.engine02 then
				if betty.plrship["engine02"]:isValid() then
					betty.subsystems.engine02 = betty.plrship["engine02"]
					betty.enginenum = 2
				end
			end
			
			if not betty.subsystems.engine03 then
				if betty.plrship["engine03"]:isValid() then
					betty.subsystems.engine03 = betty.plrship["engine03"]
					betty.enginenum = 3
				end
			end
				
			if not betty.subsystems.engine04 then
				if betty.plrship["engine04"]:isValid() then
					betty.subsystems.engine04 = betty.plrship["engine04"]
					betty.enginenum = 4
				end
			end
			
			if not betty.subsystems.engine01 then
				if betty.plrship["engine01a"]:isValid() then
					betty.subsystems.engine01 = betty.plrship["engine01a"]
					betty.enginenum = 1
				end
			end
			
			if not betty.subsystems.engine02 then
				if betty.plrship["engine02a"]:isValid() then
					betty.subsystems.engine02 = betty.plrship["engine02a"]
					betty.enginenum = 2
				end
			end
			
			if not betty.subsystems.engine03 then
				if betty.plrship["engine03a"]:isValid() then
					betty.subsystems.engine03 = betty.plrship["engine03a"]
					betty.enginenum = 3
				end
			end
				
			if not betty.subsystems.engine04 then
				if betty.plrship["engine04a"]:isValid() then
					betty.subsystems.engine04 = betty.plrship["engine04a"]
					betty.enginenum = 4
				end
			end
				
			if not betty.subsystems.nav then 
				if betty.plrship["navigation"]:isValid() then
					betty.subsystems.nav = betty.plrship["navigation"]
				end
			end
				
			if not betty.subsystems.sensors then 
				if betty.plrship["sensors"]:isValid() then
					betty.subsystems.sensors = betty.plrship["sensors"]
				end
			end
				
			if not betty.subsystems.weapons then 
				if betty.plrship["weapons"]:isValid() then
					betty.subsystems.weapons = betty.plrship["weapons"]
				end
			end
				
			betty.queue.timestamp = 0
			bettyinited = true
			getenginemaxhealth()
		else
			bettyinited = false
			BettyOverride = true
		end
	end
end
	
function getenginemaxhealth()
	if betty.enginenum == 0 then
		betty.enginemaxhealth = betty.subsystems.engine.HitpointsMax
	elseif betty.enginenum == 1 then
		betty.enginemaxhealth = betty.subsystems.engine01.HitpointsMax
	elseif betty.enginenum == 2 then
		betty.enginemaxhealth = betty.subsystems.engine01.HitpointsMax + betty.subsystems.engine02.HitpointsMax
	elseif betty.enginenum == 3 then
		betty.enginemaxhealth = betty.subsystems.engine01.HitpointsMax + betty.subsystems.engine02.HitpointsMax + betty.subsystems.engine03.HitpointsMax
	elseif betty.enginenum == 4 then
		betty.enginemaxhealth = betty.subsystems.engine01.HitpointsMax + betty.subsystems.engine02.HitpointsMax + betty.subsystems.engine03.HitpointsMax + betty.subsystems.engine04.HitpointsMax
	end
end
	
function getenginehealth()
	if betty.enginenum == 0 then
		betty.enginehealth = (betty.subsystems.engine.HitpointsLeft) / betty.enginemaxhealth
	elseif betty.enginenum == 1 then
		betty.enginehealth = (betty.subsystems.engine01.HitpointsLeft) / betty.enginemaxhealth
	elseif betty.enginenum == 2 then
		betty.enginehealth = (betty.subsystems.engine01.HitpointsLeft + betty.subsystems.engine02.HitpointsLeft) / betty.enginemaxhealth
	elseif betty.enginenum == 3 then
		betty.enginehealth = (betty.subsystems.engine01.HitpointsLeft + betty.subsystems.engine02.HitpointsLeft + betty.subsystems.engine03.HitpointsLeft) / betty.enginemaxhealth
	elseif betty.enginenum == 4 then
		betty.enginehealth = (betty.subsystems.engine01.HitpointsLeft + betty.subsystems.engine02.HitpointsLeft + betty.subsystems.engine03.HitpointsLeft + betty.subsystems.engine04.HitpointsLeft) / betty.enginemaxhealth
	end
end
	
function queueWarning(warning)
	local i = 1
	queued = false
	while not queued do
		if betty.queue[i] == nil then
			betty.queue[i] = warning
			queued = true
		else
			i = i + 1
		end
	end
end

function processQueue()
	local mtime = mn.getMissionTime()
	
	if mtime ~= 0 then
		if (mtime - betty.queue.timestamp) > betty.queueprocessdelay then
			if betty.queue[1] ~= nil then
				ad.playGameSound(betty.queue[1], 0, 100, 0)
				
				local i = 1
		
				local processed = false
				while not processed do
					if betty.queue[i + 1] ~= nil then
						betty.queue[i] = betty.queue[i + 1]
						i = i + 1
					else
						betty.queue[i] = nil
						processed = true
					end
				end
	
				betty.queue.timestamp = mtime
			end
		end
	end
end

function printQueue()
	local i = 1
	if betty.queue[1] ~= nil then
		while betty.queue[i] ~= nil do
			ba.print("BETTY DEBUG Item: " .. i .. ": " .. betty.queue[i] .. "\n")
			i = i + 1
		end
	end
end

function processTriggers()
	if betty.subsystems.comms then
		if betty.subsystems.comms.HitpointsLeft/betty.subsystems.comms.HitpointsMax < 0.01 and not betty.status.commwarned then
			queueWarning(betty.comms)
			betty.status.commwarned = true
		elseif betty.subsystems.comms.HitpointsLeft/betty.subsystems.comms.HitpointsMax > 0.1 then
			betty.status.commwarned = false
		end
	end
	
	getenginehealth()

	if betty.enginehealth < 0.01 and not betty.status.enginewarned then
		queueWarning(betty.engine)
		betty.status.enginewarned = true
	elseif betty.enginehealth > 0.1 then
		betty.status.enginewarned = false
	end

	if betty.subsystems.nav then
		if betty.subsystems.nav.HitpointsLeft/betty.subsystems.nav.HitpointsMax < 0.01 and not betty.status.navwarned then
			queueWarning(betty.nav)
			betty.status.navwarned = true
		elseif betty.subsystems.nav.HitpointsLeft/betty.subsystems.nav.HitpointsMax > 0.1 then
			betty.status.navwarned = false
		end
	end

	if betty.subsystems.sensors then
		if betty.subsystems.sensors.HitpointsLeft/betty.subsystems.sensors.HitpointsMax < 0.01 and not betty.status.sensorswarned then
			queueWarning(betty.sensors)
			betty.status.sensorswarned = true
		elseif betty.subsystems.sensors.HitpointsLeft/betty.subsystems.sensors.HitpointsMax > 0.1 then
			betty.status.sensorswarned = false
		end
	end

	if betty.subsystems.weapons then
		if betty.subsystems.weapons.HitpointsLeft/betty.subsystems.weapons.HitpointsMax < 0.01 and not betty.status.weaponswarned then
			queueWarning(betty.weapons)
			betty.status.weaponswarned = true
		elseif betty.subsystems.weapons.HitpointsLeft/betty.subsystems.weapons.HitpointsMax > 0.1 then
			betty.status.weaponswarned = false
		end
	end

	if betty.plrship.HitpointsLeft/betty.plrship.HitpointsMax < 0.41 and not betty.status.hullwarned then
		queueWarning(betty.hull)
		betty.status.hullwarned = true
	elseif betty.plrship.HitpointsLeft/betty.plrship.HitpointsMax > 0.41 then
		betty.status.hullwarned = false
	end

	if betty.plrship.HitpointsLeft/betty.plrship.HitpointsMax < 0.1 and not betty.status.ejectwarned then
		queueWarning(betty.eject)
		betty.status.ejectwarned = true
	elseif betty.plrship.HitpointsLeft/betty.plrship.HitpointsMax > 0.1 then
		betty.status.ejectwarned = false
	end

	for i = 1, 3, 1 do
		if betty.plrship.PrimaryBanks[i]:isValid() then
			if betty.plrship.PrimaryBanks[i].WeaponClass.Name == "Gattler" or betty.plrship.PrimaryBanks[i].WeaponClass.Name == "Archer" or betty.plrship.PrimaryBanks[i].WeaponClass.Name == "UX Accelerator" or betty.plrship.PrimaryBanks[i].WeaponClass.Name == "Redeemer" or betty.plrship.PrimaryBanks[i].WeaponClass.Name == "Vajra" then
				if betty.plrship.PrimaryBanks[i].AmmoLeft/betty.plrship.PrimaryBanks[i].AmmoMax < 0.26 and not betty.status.primarywarned[i] then
					queueWarning(betty.primary[i])
					betty.status.primarywarned[i] = true
				elseif betty.plrship.PrimaryBanks[i].AmmoLeft/betty.plrship.PrimaryBanks[i].AmmoMax > 0.27 then
					betty.status.primarywarned[i] = false
				end
				
				if betty.plrship.PrimaryBanks[i].AmmoLeft == 0 and not betty.status.primaryemptywarned[i] then
					queueWarning(betty.primaryempty[i])
					betty.status.primaryemptywarned[i] = true
				elseif betty.plrship.PrimaryBanks[i].AmmoLeft > 0 then
					betty.status.primaryemptywarned[i] = false
				end
			end
		end
	end

	for i = 1, 4, 1 do
		if betty.plrship.SecondaryBanks[i]:isValid() then
			if betty.plrship.SecondaryBanks[i].AmmoLeft/betty.plrship.SecondaryBanks[i].AmmoMax < 0.26 and not betty.status.secondarywarned[i] then
				queueWarning(betty.secondary[i])
				betty.status.secondarywarned[i] = true
			elseif betty.plrship.SecondaryBanks[i].AmmoLeft/betty.plrship.SecondaryBanks[i].AmmoMax > 0.27 then
				betty.status.secondarywarned[i] = false
			end
			
			if betty.plrship.SecondaryBanks[i].AmmoLeft == 0 and not betty.status.secondaryemptywarned[i] then
				queueWarning(betty.secondaryempty[i])
				betty.status.secondaryemptywarned[i] = true
			elseif betty.plrship.SecondaryBanks[i].AmmoLeft > 0 then
				betty.status.secondaryemptywarned[i] = false
			end
		end
	end
end
]

$On Frame:
[
if not BettyOverride then
	ttime = mn.getMissionTime()
		
	if ttime ~= 0 then
		if not bettyinited then
			initbetty()
		else
			--if betty.plrship.Class.Species.Name == "UEF" then									
				processTriggers()					
				printQueue()
				processQueue()
			--else
			--	betty = nil
			--	bettyinited = false
			--	BettyOverride = true
			--end
		end
	end
end
]

$On State End: 
[
	if bettyinited then
		i = 1
		while betty.queue[i] ~= nil do
			betty.queue[i] = nil
			ba.print("BETTY DEBUG: Clearing queue item " .. i .. "\n")
			i = i + 1		
		end
		betty = nil
		bettyinited = false
	end
]

#End