-- If you are a common themer, DO NOT INCLUDE THIS FILE IN YOUR THEME. -- This layer is purely for error reporting, so that you can see errors on screen easily while running stepmania. -- If you include this file in your theme, you will not benefit from any improvements in error reporting. -- If you want a different background behind the errors, then include "ScreenSystemLayer errorbg.lua" in your theme. -- If you want to adjust how long errors stay on screen for, call the "SetErrorMessageTime" function. (see usage notes in comments above that function) -- Minor text formatting functions from Kyzentun. -- TODO: Figure out why BitmapText:maxwidth doesn't do what I want. local function width_limit_text(text, limit, natural_zoom) natural_zoom= natural_zoom or 1 if text:GetWidth() * natural_zoom > limit then text:zoomx(limit / text:GetWidth()) else text:zoomx(natural_zoom) end end local function width_clip_text(text, limit) local full_text= text:GetText() local fits= text:GetZoomedWidth() <= limit local prev_max= #full_text - 1 local prev_min= 0 if not fits then while prev_max - prev_min > 1 do local new_max= math.round((prev_max + prev_min) / 2) text:settext(full_text:sub(1, 1+new_max)) if text:GetZoomedWidth() <= limit then prev_min= new_max else prev_max= new_max end end text:settext(full_text:sub(1, 1+prev_min)) end end local function width_clip_limit_text(text, limit, natural_zoom) natural_zoom= natural_zoom or text:GetZoomY() local text_width= text:GetWidth() * natural_zoom if text_width > limit * 2 then text:zoomx(natural_zoom * .5) width_clip_text(text, limit) else width_limit_text(text, limit, natural_zoom) end end local text_actors= {} local line_height= 12 -- A good line height for Common Normal at .5 zoom. local line_width= SCREEN_WIDTH - 20 local next_message_actor= 1 local show_requested= false local min_message_time= {show= 1, hide= .03125} local default_message_time= {show= 4, hide= .125} local message_time= {} for k, v in pairs(default_message_time) do message_time[k]= v end -- Example usage: -- "SetErrorMessageTime('show' 5)" sets errors to show for 5 seconds before beginning to hide. -- "SetErrorMessageTime('hide' .5)" sets errors to hide one error every .5 seconds after the show time has passed. function SetErrorMessageTime(which, t) if not min_message_time[which] then MESSAGEMAN:Broadcast( "ScriptError", { Message= "Attempted to set invalid overlay message time field: " .. tostring(which)}) return end if t < min_message_time[which] then MESSAGEMAN:Broadcast( "ScriptError", { Message= "Attempted to set overlay message " .. which .. " time to below minimum of " .. min_message_time[which] .. "."}) return end message_time[which]= t end function GetErrorMessageTime(which) return message_time[which] end function GetErrorMessageTimeMin(which) return min_message_time[which] end function GetErrorMessageTimeDefault(which) return default_message_time[which] end local frame_args= { Name="Error frame", InitCommand= function(self) self:y(-SCREEN_HEIGHT) end, ScriptErrorMessageCommand = function(self, params) show_requested= false self:stoptweening() self:visible(true) local covered_height= math.min(line_height * next_message_actor, SCREEN_HEIGHT) self:y(-SCREEN_HEIGHT + covered_height) self:sleep(message_time.show) self:queuecommand("DecNextActor") -- Shift the text on all the actors being shown up by one. for i= next_message_actor, 1, -1 do if text_actors[i] then text_actors[i]:visible(true) if i > 1 and text_actors[i-1] then text_actors[i]:settext(text_actors[i-1]:GetText()) else -- Someone long ago decided that it was a good idea for "::" to be -- a synonym for "\n", so that strings in metrics could have line -- breaks. -- So replace "::" with ":" so we don't have unwanted line breaks. text_actors[i]:settext(params.Message:gsub("::", ":")) end width_clip_limit_text(text_actors[i], line_width) end end if next_message_actor <= #text_actors then next_message_actor= next_message_actor + 1 end end, ToggleErrorsMessageCommand= function(self, params) if show_requested then self:playcommand("HideErrors") else self:playcommand("ShowErrors") end end, ShowErrorsMessageCommand= function(self, params) show_requested= true for i, mactor in ipairs(text_actors) do if mactor:GetText() ~= "" then mactor:visible(true) next_message_actor= next_message_actor + 1 end end next_message_actor= next_message_actor - 1 local covered_height= math.min(line_height * next_message_actor, SCREEN_HEIGHT) self:stoptweening() self:visible(true) self:linear(message_time.hide) self:y(-SCREEN_HEIGHT + covered_height) end, HideErrorsMessageCommand= function(self, params) if not show_requested then return end show_requested= false self:stoptweening() next_message_actor= 1 self:linear(message_time.hide) self:y(-SCREEN_HEIGHT) self:queuecommand("HideText") end, ClearErrorsMessageCommand= function(self, params) -- This is so that someone using ShowErrorsMessageCommand can clear ones -- they've dealt with. -- Only allow clearing errors that we have scrolled off screen. for i, mactor in ipairs(text_actors) do if not mactor:GetVisible() then mactor:settext("") end end end, DecNextActorCommand= function(self) self:linear(message_time.hide) self:y(self:GetY()-line_height) if text_actors[next_message_actor] then text_actors[next_message_actor]:visible(false) end next_message_actor= next_message_actor - 1 if next_message_actor > 1 then self:queuecommand("DecNextActor") else self:queuecommand("Off") end end, HideTextCommand= function(self) for i, mactor in ipairs(text_actors) do mactor:visible(false) end end, OffCommand= cmd(visible,false), Def.Quad { Name= "errorbg", InitCommand= function(self) self:setsize(SCREEN_WIDTH, SCREEN_HEIGHT) self:horizalign(left) self:vertalign(top) self:diffuse(color("0,0,0,0")) self:diffusealpha(.85) end, } } -- Create enough text actors that we can fill the screen. local num_text= SCREEN_HEIGHT / line_height for i= 1, num_text do frame_args[#frame_args+1]= LoadFont("Common","Normal") .. { Name="Text" .. i, InitCommand= function(self) -- Put them in the list in reverse order so the ones at the bottom of the screen are used first. text_actors[num_text-i+1]= self self:horizalign(left) self:vertalign(top) self:x(SCREEN_LEFT + 10) self:y(SCREEN_TOP + (line_height * (i-1)) + 2) self:shadowlength(1) self:zoom(.5) self:visible(false) end, OffCommand= cmd(visible,false), } end Trace("Loaded error layer.") return Def.ActorFrame(frame_args)