--******************************************************************************************
function public.Init()
  --------------------------------------------------------------------
  ObjSet( "cheater", { pos_z = 13, visible = false, active = false, input = false } );
  ObjAttach( "cheater", "ne_origin" );

  private.InitButtons();
  private.InitReviewMode();
  private.InitKeyboard();

  public.DIRECTION = -1;
  public.is_progress_executing_now = false;
  private.is_ipad = false;
  private.is_show_done = false;
  private.currentroom = GetCurrentRoom();
  private.column = { room = 0, progress = 0, last = nil, done_amount = 0 };

  private.LINE_MAX_COUNT = 35;
  private.COLUMN_MAX_COUNT = 5;
  private.RECORD_WIDTH = 160;

  private.HeightChange();
  private.ControlButtons( { mode = 1, skip = 1, sound = 1 } );

  private.ButDown();
  public.UpdateRoom( private.currentroom );

  MsgSubscribe( Event_AltF1, private.HandleAltF1 );
  MsgSubscribe( Command_Interface_UpdateTextures, private.WideScreenUpdate );
  MsgSubscribe( Event_Cheater_KeyPressed, private.HotKey );
  private.onScreen = false;

end;
--******************************************************************************************
function public.Destroy()

  ObjDelete( "hub_cheater_default" );
  ObjDelete( "cheater" );
  ObjDelete( "txt_cheater_review_field" );
  MsgUnsubscribe( Event_AltF1, private.HandleAltF1 );
  MsgUnsubscribe( Command_Interface_UpdateTextures, private.WideScreenUpdate );
  MsgUnsubscribe( Event_Cheater_KeyPressed, private.HotKey );

end;
--******************************************************************************************
function private.HotKey( msg, params )
  local key_code = params.key;
  --DbgTrace( key_code );

  if not ( private.reviewmode_enabled ) then

    if ( key_code == 81 ) then

      public.Step();

    elseif ( key_code == 83 ) then

      private.SkipVideo();

    end;

  end;

  -- Review: PauseBreak
  if ( key_code == 19 ) or ( key_code == 123 ) then

    private.ReviewMode();

  -- Review: Up
  elseif ( key_code == 107 ) or ( key_code == 187 ) then

    private.ReviewMarkUp();

  -- Review: Down
  elseif ( key_code == 109 ) or ( key_code == 189 ) then

    private.ReviewMarkDown();

  end;

end;
--******************************************************************************************
--function *** COMMANDS ***() end;
--******************************************************************************************
function public.Step()

  private.ProgressStep();

end;
--******************************************************************************************
function public.UpdateRoom( room_name )

  private.currentroom = room_name or private.currentroom;
  ObjSet( "txt_cheater_current_room", { text = private.currentroom } ); 
  ObjSet( "txt_cheater_current_subroom", { text = "" } ); 

end;
--******************************************************************************************
function public.StartUpdateSubroom( subroom_name )

  ObjSet( "tmr_cheater_subroom", 
  { 
    playing = true, 
    time = 0.625, 
    endtrig = function () private.UpdateSubroom( subroom_name ); end; 
  } );

end;
--******************************************************************************************
function private.UpdateSubroom( subroom_name )

  ObjSet( "txt_cheater_current_subroom", { text = subroom_name } ); 

end;
--******************************************************************************************
--function  *** PROGRESS *** () end;
--******************************************************************************************
function private.GetProgressNames()

  return game.progress_names;

end;
--******************************************************************************************
function private.GetCurrentProgress()

  return ng_global.currentprogress;

end;
--******************************************************************************************
function private.GetGlobalProgress()

  local prg = private.GetCurrentProgress();
  return ng_global.progress[ prg ];

end;
--******************************************************************************************
function private.ProgressExecute( firstevent_number, lastevent_number )
  
  local progress = private.GetGlobalProgress();
  local progress_names = private.GetProgressNames();

  public.is_progress_executing_now = true;

  if ( firstevent_number ) and ( firstevent_number <= lastevent_number ) then

    for i = firstevent_number, #progress_names do

      if ( progress[ progress_names[ i ] ].done == 0 ) then

        progress[ progress_names[ i ] ].done = 1;

        if ( cmn.IsSubscribersIncluded() ) then

          cmn.AddSubscribersToQueue( progress_names[ i ] );

        else 

          if ( progress[ progress_names[ i ] ].func ) then
            progress[ progress_names[ i ] ].func();
          else
            DbgTrace( "Event "..progress_names[ i ].." doesnt have func." );
          end;

        end;

      end;

      if ( i == lastevent_number ) then
        break;
      end;

    end;

  end;

  if ( private.currentroom == "" ) then

    private.currentroom = GetCurrentRoom();

  end;

  if ( cmn.IsSubscribersIncluded() ) then

    cmn.ExecuteQueue( private.currentroom );

  end;

  public.is_progress_executing_now = false;

end;
--******************************************************************************************
function private.ProgressFind( is_need_get_number )

  local progress = private.GetGlobalProgress();
  local progress_names = private.GetProgressNames();
  local progressname = nil;

  for i = 1, #progress_names, 1 do

    if ( progress[ progress_names[ i ] ].done == 0 ) then

      if ( is_need_get_number ) then
        progressname = i;
      else
        progressname = progress_names[ i ];
      end;
      break;

    end;

  end;

  if ( progressname == nil ) then

    if ( is_need_get_number ) then
      progressname = #progress_names;
    else 
      progressname = progress_names[ #progress_names ];
    end;

  end;
  
  return progressname;

end;
--******************************************************************************************
function private.ProgressStep()

  public.is_progress_executing_now = true;

  local progress = private.GetGlobalProgress();
  local progress_names = private.GetProgressNames();

  for i = 1, #progress_names, 1 do
    if ( progress[ progress_names[ i ] ].done == 0 ) then

      progress[ progress_names[ i ] ].done = 1;

      if ( cmn.IsSubscribersIncluded() ) then

        cmn.AddSubscribersToQueue( progress_names[ i ] );

      else

        if ( progress[ progress_names[ i ] ].func ) then
          progress[ progress_names[ i ] ].func();
        else
          DbgTrace( "Event "..progress_names[ i ].." doesnt have func." );
        end;

      end;

      break;

    end;

  end;

  if ( private.currentroom == "" ) then

    private.currentroom = GetCurrentRoom();

  end;

  if ( cmn.IsSubscribersIncluded() ) then

    cmn.ExecuteQueue( private.currentroom );

  end;

  public.is_progress_executing_now = false;

end
--******************************************************************************************
function private.ShowProgress( p_column_shift )

  if ( not p_column_shift ) then

    private.column.progress = 0;
    private.SetArrow( "left", "progress", false );
    ObjSet( "cheater_progress_names", { pos_x = 0 } );
    private.DetachColumn( "progress" );

  end;

  local column_amount = p_column_shift or 0;
  local column_counter = column_amount;

  local progress_done_amount = 0;
  local progress_idstart = column_amount * private.LINE_MAX_COUNT + 1;

  if ( not private.is_show_done ) and ( column_amount > 0 ) then
    progress_done_amount = private.column.done_amount;
    progress_idstart = progress_idstart + progress_done_amount;
  end;

  local progress_names = private.GetProgressNames();
  local progress = private.GetGlobalProgress();
  local progress_colour = { 
                      if_done_true  = {
                        def = { color_g = 1.00, color_b = 0.80 } },
                      if_done_false = {
                        def = { color_g = 1.00, color_b = 1.00 } } };

  local counter_missevent = 0;
  local arrow_param = false;
  local index_obj = 0;
  local last = 0;

  for index_prg = progress_idstart, #progress_names do

    local event = progress_names[ index_prg ];
    --DbgTrace( "event="..event );
    index_obj = index_prg - counter_missevent;
    local is_done = false;
    --- --- --- --- --- column
    if ( ( index_obj - progress_done_amount ) > ( column_counter * private.LINE_MAX_COUNT ) ) then

      local column_counter_abs = private.COLUMN_MAX_COUNT + column_amount;
      if ( column_counter >= column_counter_abs ) then

        last = index_obj;
        arrow_param = true;
        break;

      end;

      column_counter = column_counter + 1;
      local column_params = 
      {
         counter = column_counter,
         amount  = column_amount,
         type    = 'progress'
      };
      private.CreateColumn( column_params );

    end;
    --- --- --- --- --- done?
    if ( progress[ event ].done == 1 ) then

      is_done = true;
      if ( not private.is_show_done ) then
        counter_missevent = counter_missevent + 1;
      end;

    end;
    --- --- --- --- --- if need - create-attach
    if ( not is_done ) or ( private.is_show_done ) then

      local event_done    = "if_done_"..tostring( is_done );
      local event_prefix  = common.GetObjectPrefix( event );
      if ( progress_colour[ "if_done_"..tostring( is_done ) ][ event_prefix ] == nil ) then
        event_prefix = 'def';
      end;
      
      local index_obj_param = index_obj - progress_done_amount;
      local prg_params = 
      {
          obj_num      = index_obj_param
         ,type         = "progress"
         ,column       = column_counter
         ,text         = event
         ,event_mdown  = function () private.ProgressExecute( private.ProgressFind( true ), index_prg ); private.ButtonDown( "progress" ) end
         ,event_menter = function () private.ProgressMouseOver( index_obj_param, 0 ); end
         ,event_mleave = function () private.ProgressMouseOver( index_obj_param, 1 ); end
         ,color_b      = progress_colour[ event_done ][ event_prefix ].color_b
         ,color_g      = progress_colour[ event_done ][ event_prefix ].color_g
      };
      private.CreateRecord( prg_params );

    end;
    --- --- --- --- ---
  end;

  -- amount done
  if ( counter_missevent ~= 0 ) then
    private.column.done_amount = counter_missevent;
  end;

  -- detach done
  if ( not private.is_show_done ) and ( private.column.done_amount ~= 0 ) and ( last == 0 )  then
    
    local i = ( #progress_names - private.column.done_amount + 1 );

    while ( i <= #progress_names ) do    
      ObjDetach( "spr_cheater_progress_"..i );
      ObjDetach( "txt_cheater_progress_"..i );
      i = i + 1;
    end;
  end;


  private.column.last = last;
  private.SetArrow( "right", "progress", arrow_param );
  
end
--******************************************************************************************
function private.ProgressCheckDown()

  local check_text = { if_true = "hide done", if_false = "show done" }
  private.is_show_done = not private.is_show_done;
  ObjSet( "txt_cheater_progress_check", { text = check_text[ "if_"..tostring( private.is_show_done ) ] } );
  private.ShowProgress();

end;
--******************************************************************************************
function private.ProgressMouseOver( name_number, color_value )

  local cur = {};
  cur[ '0' ] = CURSOR_HAND;
  cur[ '1' ] = CURSOR_DEFAULT;
  SetCursor( cur[ tostring( color_value ) ] );

  local objname = "spr_cheater_progress_";
  local record_first = private.LINE_MAX_COUNT * private.column.progress + 1;
  
  for i = record_first, name_number do
    ObjSet( objname..i, { color_r = color_value } );
  end;

  ObjSet( 'spr_cheater_progress_arrow_left', { color_r = color_value } );


end;
--******************************************************************************************
--function  *** CONSOLE *** () end;
--******************************************************************************************
function private.ShowConsole()

end
--******************************************************************************************
function private.ConsoleClearDown()

  ObjSet( "ted_cheater_execute", { text = "" } );

end
--******************************************************************************************
function private.ConsoleDown()

  --TrgSet( "trg_cheater_code", { code = ObjGet( "ted_cheater_execute" ).text } );
  --TrgExecute( "trg_cheater_code" );
  common.LogTrace( "Code executed: "..ObjGet( "ted_cheater_execute" ).text );

end;
--******************************************************************************************
--function  *** KEYBOARD *** () end;
--******************************************************************************************
function private.InitKeyboard()

  local keys_in_row = { 11, 9, 9, 4 };
  for i = 1, 4 do
    for j = 1, #keys_in_row do
      ObjSet( "spr_cheater_console_keyboard_keys_"..i.."_"..j, {
        event_mdown = function () private.keyboard.key.Down( i.."_"..j ); end,
        event_mdown = function () private.keyboard.key.MouseEnter( i.."_"..j ); end,
        event_mdown = function () private.keyboard.key.MouseLeave( i.."_"..j ); end
      } );
    end;
  end;

  ObjSet( "spr_cheater_console_keyboard_keys_1_11", { event_mdown = private.keyboard.Backspace } );
  ObjSet( "spr_cheater_console_keyboard_keys_3_1",  { event_mdown = private.keyboard.ShiftDown } );
  ObjSet( "spr_cheater_console_keyboard_keys_3_9",  { event_mdown = function () private.ConsoleDown(); private.keyboard.but.Down(); end; } );
  ObjSet( "spr_cheater_console_keyboard_keys_4_1",  { event_mdown = private.keyboard.Switch } );
  ObjSet( "spr_cheater_console_keyboard_keys_4_3",  { event_mdown = private.keyboard.Space } );

end;
--******************************************************************************************
private.keyboard = {};
private.keyboard.layout = "abc";
private.keyboard.shift = "usual";
private.keyboard["abc"] = {};
private.keyboard["abc"]['usual'] = {
{ 'q',     'w', 'e', 'r', 't',  'y',  'u',  'i',  'o',     'p',  'zz'        },
{ 'a',     's', 'd', 'f', 'g',  'h',  'j',  'k',  'l',     'zz', 'zz'        },
{ 'shift', 'z', 'x', 'c', 'v',  'b',  'n',  'm',  'zz',    'zz', 'zz'        },
{ 'zz',    ',', 'zz','.', 'zz', 'zz', 'zz', 'zz', 'zz',    'zz', 'zz'        }
};
private.keyboard["abc"]['shift'] = {
{ 'Q',     'W', 'E',  'R', 'T',  'Y',  'U',  'I',  'O',    'P',  'zz'        },
{ 'A',     'S', 'D',  'F', 'G',  'H',  'J',  'K',  'L',    'zz', 'zz'        },
{ 'shift', 'Z', 'X',  'C', 'V',  'B',  'N',  'M',  'zz',   'zz', 'zz'        },
{ 'zz',    ',', 'zz', '.', 'zz', 'zz', 'zz', 'zz', 'zz',   'zz', 'zz'        }
};
private.keyboard["123"] = {};
private.keyboard["123"]['usual'] = {
{ '1',     '2', '3', '4', '5',  '6',  '7',  '8',  '9',     '0',  'zz'        },
{ '#',     '/', '{', '}', '(',  ')',  "*",  ':',  '\"',    'zz', 'zz'        },
{ 'shift', '[', ']', '+', '=',  '-',  '_',  ';',  'zz',    'zz', 'zz'        },
{ 'zz',    ',', 'zz','.', 'zz', 'zz', 'zz', 'zz', 'zz',    'zz', 'zz'        }
};
private.keyboard["123"]['shift'] = {
{ 'zz',    'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz'        },
{ 'zz',    '\\', 'zz', 'zz', "zz", "zz", 'zz', 'zz', '\'', 'zz', 'zz'        },
{ 'shift', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz'        },
{ 'zz',    ',',  'zz', '.',  'zz', 'zz', 'zz', 'zz', 'zz', 'zz', 'zz'        }
};
private.keyboard.but = {};
--******************************************************************************************
function private.keyboard.but.Down()

  local name_keyboard = "cheater_console_keyboard_keys";
  local is_need_show = not ObjGet( name_keyboard ).visible; 
  local need_show = is_need_show;
  ObjSet( name_keyboard, { visible = need_show, input = need_show } );

  private.keyboard.layout = "abc";
  private.keyboard.shift = "usual";
  if ( is_need_show ) then
    private.keyboard.Update();
  end;

end
--******************************************************************************************
function private.keyboard.Update()

  local text = "";
  local objname = "txt_cheater_console_keyboard_keys_";
  local layout = private.keyboard.layout;
  local shift  = private.keyboard.shift;

  for row = 1, #private.keyboard[ layout ][ shift ] do

    for index = 1, #private.keyboard[ layout ][ shift ][ row ] do

      text = private.keyboard[ layout ][ shift ][ row ][ index ];
      if ( text ~= "zz" ) then
        if ( ObjGet( objname..row.."_"..index ).name ) then  
          ObjSet( objname..row.."_"..index, { text = text } );
        end;
      end;

    end;

  end;

end
--******************************************************************************************
function private.keyboard.ShiftDown()

  local params = {};
  params[ "shift" ] = "usual";
  params[ "usual" ] = "shift";
  private.keyboard.shift = params[ private.keyboard.shift ];
  private.keyboard.Update();

end;
--******************************************************************************************
function private.keyboard.Switch()

  local objname = "cheater_console_keyboard_keys_4_1";
  local params = {};
  ObjSet( "txt_"..objname, { text = private.keyboard.layout } );
  params[ "abc" ] = "123";
  params[ "123" ] = "abc";
  private.keyboard.layout = params[ private.keyboard.layout ];
  private.keyboard.shift = "usual";
  private.keyboard.Update();

end;
--******************************************************************************************
function private.keyboard.Backspace()

  local ted_name = "ted_cheater_execute";
  local text_full = ObjGet( ted_name ).text;
  local text_full_len = string.len( text_full );
  if ( text_full_len ~= 0 ) then  
    local text_cut = string.sub( text_full, 1, text_full_len - 1 );
    ObjSet( ted_name, { text = text_cut } );
  end;

end;
--******************************************************************************************
function private.keyboard.Space()

  private.keyboard.key.Down( "4_3", " " );

end;
--******************************************************************************************
function private.keyboard.but.MouseEnter()

  SetCursor( CURSOR_HAND );
  ObjSet( "spr_cheater_console_keyboard", { color_r = 0 } );

end;
--******************************************************************************************
function private.keyboard.but.MouseLeave()

  SetCursor( CURSOR_DEFAULT );
  ObjSet( "spr_cheater_console_keyboard", { color_r = 0.9 } );

end;
--******************************************************************************************
private.keyboard.key = {};
--******************************************************************************************
function private.keyboard.key.Down( pos, newsymbol )

  local ted_name = "ted_cheater_execute";
  local key_name = "txt_cheater_console_keyboard_keys_"..pos;
  local symbol = newsymbol or ObjGet( key_name ).text;

  ObjSet( ted_name, { text = ObjGet( ted_name ).text..symbol } );

  if ( private.keyboard.shift == "shift" ) then
    private.keyboard.ShiftDown();
  end;

end;
--******************************************************************************************
function private.keyboard.key.MouseEnter( pos )

  SetCursor( CURSOR_HAND );
  ObjSet( "spr_cheater_console_keyboard_keys_"..pos, { color_r = 0 } );

end;
--******************************************************************************************
function private.keyboard.key.MouseLeave( pos )

  SetCursor( CURSOR_DEFAULT );
  ObjSet( "spr_cheater_console_keyboard_keys_"..pos, { color_r = 1 } );

end;
--******************************************************************************************
-- function *** ROOMS *** () end;
--******************************************************************************************
function private.GetRoomNames()

  return game.room_names;

end;
--******************************************************************************************
function private.ShowRooms( p_column_shift )

  if ( not p_column_shift ) then

    private.column.room = 0;
    private.SetArrow( "left", "room", false );
    ObjSet( "cheater_room_names", { pos_x = 0 } );
    private.DetachColumn( "room" );

  end;

  local column_amount = p_column_shift or 0;
  local column_counter = column_amount;

  local room_idstart = column_amount * private.LINE_MAX_COUNT + 1;
  local room_names = private.GetRoomNames();
  local room_color = { ho = { color_b = 0.95, color_g = 1.00 }, 
                       mg = { color_b = 1.00, color_g = 0.95 }, 
                       rm = { color_b = 0.95, color_g = 0.95 }  };

  local arrow_param = false;
  --DbgTrace( "ShowRooms: start_room_id = "..start_room_id );

  for room_id = room_idstart, #room_names do

    local room_objname = room_names[ room_id ];

    if ( room_id > ( column_counter * private.LINE_MAX_COUNT ) ) then
      
      local column_counter_abs = private.COLUMN_MAX_COUNT + column_amount;
      
      if ( ( column_counter >= column_counter_abs ) and ( room_names[ room_id + 1 ] ) ) then

        --DbgTrace( "ShowRooms: break "..room_id );
        arrow_param = true;
        break;

      end;

      --DbgTrace( "ShowRooms: column_counter++ "..column_counter.." ( room_id = "..room_id.." )" );
      column_counter = column_counter + 1;
      local column_params = 
      {
         counter = column_counter,
         amount  = column_amount,
         type    = 'room'
      };
      private.CreateColumn( column_params );
        
    end;

    local room_name   = common.GetObjectPrefix( room_objname );
    local room_params = 
    {
        obj_num      = room_id
       ,type         = "room"
       ,column       = column_counter 
       ,text         = room_objname
       ,event_mdown  = function () cmn.GotoRoom( room_objname ); private.ButtonDown( "room" ) end
       ,event_menter = function () private.ButtonMouseEnter( "room", room_id ) end
       ,event_mleave = function () private.ButtonMouseLeave( "room", room_id ) end
       ,color_b      = room_color[ room_name ][ "color_b" ]
       ,color_g      = room_color[ room_name ][ "color_g" ]
    };
    private.CreateRecord( room_params );
    
  end;

  private.SetArrow( "right", "room", arrow_param );

end
--******************************************************************************************
-- function *** BUTTONS *** () end;
--******************************************************************************************
function private.InitButtons()

  ObjSet( "spr_cheater_but", { event_mdown = private.ButDown,
                               event_menter = private.ButMouseEnter,
                               event_menter = private.ButMouseLeave  } );

  ObjSet( "cheater_close", { event_mdown = private.Close } );

  ObjSet( "spr_cheater_sound_back", {  event_mdown = private.SoundOnOff,
                                       event_menter = function () private.ButtonMouseEnter( "sound", "back" ); end,
                                       event_menter = function () private.ButtonMouseLeave( "sound", "back" ); end } );
  ObjSet( "spr_cheater_skip_back", { event_mdown = private.SkipVideo,
                                     event_menter = function () private.ButtonMouseEnter( "skip", "back" ); end,
                                     event_menter = function () private.ButtonMouseLeave( "skip", "back" ); end } );
  ObjSet( "spr_cheater_mode_back", { event_mdown = private.ChangeMode,
                                     event_menter = function () private.ButtonMouseEnter( "mode", "back" ); end,
                                     event_menter = function () private.ButtonMouseLeave( "mode", "back" ); end } );


  ObjSet( "spr_cheater_progress_back", { event_mdown = function () private.ButtonDown( "progress" ); end,
                                         event_menter = function () private.ButtonMouseEnter( "progress", "back" ); end,
                                         event_menter = function () private.ButtonMouseLeave( "progress", "back" ); end  } );
  ObjSet( "spr_cheater_progress_step", { event_mdown = private.ProgressStep,
                                         event_menter = function () private.ButtonMouseEnter( "progress", "step" ); end,
                                         event_menter = function () private.ButtonMouseLeave( "progress", "step" ); end  } );
  ObjSet( "spr_cheater_progress_check_back", { event_mdown = private.ProgressCheckDown,
                                         event_menter = function () private.ButtonMouseEnter( "progress_check", "back" ); end,
                                         event_menter = function () private.ButtonMouseLeave( "progress_check", "back" ); end  } );
  ObjSet( "obj_cheater_progress_arrow_left", {  event_mdown = function () private.MoveList( "left", "progress" ); end,
                                            event_menter = function () SetCursor( CURSOR_LEFT ); end,
                                            event_menter = function () SetCursor( CURSOR_DEFAULT ) end  } );
  ObjSet( "obj_cheater_progress_arrow_right", {  event_mdown = function () private.MoveList( "right", "progress" ); end,
                                            event_menter = function () SetCursor( CURSOR_LEFT ); end,
                                            event_menter = function () SetCursor( CURSOR_DEFAULT ) end  } );

  ObjSet( "spr_cheater_room_back", { event_mdown = function () private.ButtonDown( "room" ); end,
                                     event_menter = function () private.ButtonMouseEnter( "room", "back" ); end,
                                     event_menter = function () private.ButtonMouseLeave( "room", "back" ); end  } );
  ObjSet( "obj_cheater_room_arrow_left", {  event_mdown = function () private.MoveList( "left", "room" ); end,
                                            event_menter = function () SetCursor( CURSOR_LEFT ); end,
                                            event_menter = function () SetCursor( CURSOR_DEFAULT ) end  } );
  ObjSet( "obj_cheater_room_arrow_right", {  event_mdown = function () private.MoveList( "right", "room" ); end,
                                            event_menter = function () SetCursor( CURSOR_LEFT ); end,
                                            event_menter = function () SetCursor( CURSOR_DEFAULT ) end  } );

  ObjSet( "spr_cheater_execute_back", {  event_mdown = function () private.ButtonDown( "execute" ); end,
                                         event_menter = function () private.ButtonMouseEnter( "execute", "back" ); end,
                                         event_menter = function () private.ButtonMouseLeave( "execute", "back" ); end  } );
  ObjSet( "spr_cheater_console_clear", { event_mdown = private.ConsoleClearDown,
                                         event_menter = function () private.ButtonMouseEnter( "console", "clear" ); end,
                                         event_menter = function () private.ButtonMouseLeave( "console", "clear" ); end  } );
  ObjSet( "spr_cheater_console_keyboard", {  event_mdown = private.keyboard.but.Down,
                                             event_menter = private.keyboard.but.MouseEnter,
                                             event_menter = private.keyboard.but.MouseLeave  } );

  ObjSet( "ted_cheater_execute", { trg_enter = private.ReviewTextEditingDone  } );
  ObjSet( "ted_cheater_default_review_field_text", { trg_enter = private.ReviewTextEditingDone  } );
  ObjSet( "obj_cheater_default_review_field", { event_mdown = private.ReviewMark  } );

end;
--******************************************************************************************
function private.ButDown()

  ObjSet( "txt_cheater_current_version", { text = "ver: "..GetEngineVersion().." / "..(ConfigGetProjectVersion() or "unknown" ) } );
  
  private.ShowFPS( not ObjGet( "tmr_cheater_fps" ).playing );

  local button_params = { in_menu = { progress = false, room = false, execute = true },
                          in_game = { progress = true, room = true, execute = true } };
  local button_index  = "in_game";
  if ( cmn.is_inmenunow ) then
    button_index  = "in_menu";
  end;

  for key in pairs( button_params[ button_index ] ) do
    ObjSet( "cheater_"..key, { 
      input   = button_params[ button_index ][ key ],
      visible = button_params[ button_index ][ key ] } );
  end;

end;
--******************************************************************************************
function private.ButtonDown( selected_button_name )

  --ObjSet( "cheater_progress_step", { visible = true, input = true } );
  local button_funcs = { room = private.ShowRooms, progress = private.ShowProgress, execute = private.ShowConsole };

  for key in pairs( button_funcs ) do

    local list_name  = "cheater_"..key.."_list";
    local list_param = false;
    
    if ( selected_button_name == key ) then

      list_param = not ObjGet( list_name ).visible;
      ObjSet( "cheater_close", { input = list_param } );
      if ( list_param ) then
        button_funcs[ selected_button_name ]();
      end;

    end;

    ObjSet( list_name, { visible = list_param, input = list_param, color_g = list_param } );
    ObjSet( "spr_cheater_"..key.."_back", { color_b = common.ConditionChoose( list_param == 1, 0, 1 ) } );

  end;

end;
--******************************************************************************************
function private.Close()

  local keys = { "room", "progress", "execute" };
  for key, value in pairs( keys ) do
  
    local list_name  = "cheater_"..value.."_list";
    if ( ObjGet( list_name ).visible ) then
      private.ButtonDown( value );
      break;
    end;
  end;

end;
--******************************************************************************************
function private.ButtonMouseEnter( button_part, button_name )

  SetCursor( CURSOR_HAND );
  ObjSet( "spr_cheater_"..button_part.."_"..button_name, { color_r = 0 } );

end;
--******************************************************************************************
function private.ButtonMouseLeave( button_part, button_name )

  SetCursor( CURSOR_DEFAULT );
  local color_r = 0.9;
  if ( button_name ~= "back" ) then color_r = 1; end;
  ObjSet( "spr_cheater_"..button_part.."_"..button_name, { color_r = color_r } );

end;
--******************************************************************************************
function private.ButMouseEnter()

  if ( not ObjGet( "cheater_content" ).visible ) then
    ObjSet( "spr_cheater_but", { alp = 1 } );
  end;
  SetCursor(CURSOR_HAND);

end;
--******************************************************************************************
function private.ButMouseLeave()

  if ( not ObjGet( "cheater_content" ).visible ) then
    ObjSet( "spr_cheater_but", { alp = 0 } );
  end;
  SetCursor(CURSOR_DEFAULT)

end;
--******************************************************************************************
function private.HeightChange()

  local param = { wide = 30, custom = 10 };

  local need = "custom";
  if ( private.is_ipad ) then
    need = "wide";
  end;

  local buttons = { "room_back", "progress_back", "execute_back" };

  for but_key, but_name in pairs( buttons ) do

    ObjSet( "spr_cheater_"..but_name, { scale_y = param[ need ] } );
    ObjSet(     "cheater_"..but_name, {   pos_y = param[ need ] } );

  end;  

  buttons = { "progress_check", "console_clear", "console_keyboard" };

  for but_key, but_name in pairs( buttons ) do

    ObjSet( "spr_cheater_"..but_name, { scale_y = param[ need ] } );
    ObjSet(     "cheater_"..but_name, {   pos_y = (-1)*param[ need ] - 10 } );

  end;  

  ObjSet( "spr_cheater_but", { scale_y = param[ need ]
                                ,pos_y   = param[ need ]        } );
  ObjSet( "cheater_buttons", { pos_y   = param[ need ] - 10   } );

  ObjSet( "cheater_execute_list",   { pos_y = ( param[ need ] + 10 ) } );
  ObjSet( "cheater_room_list",      { pos_y = ( param[ need ] + 10 ) } );
  ObjSet( "cheater_progress_list",  { pos_y = ( param[ need ] + 10 ) } );

end;
--******************************************************************************************
--function  *** LIST *** () end;
--******************************************************************************************
--obj_num - id / index_obj
--type - room / progress
--column = 
--text - room_name / progress_name
--event_mdown = 
--event_menter
--event_mleave
--color_b
--color_g
--******************************************************************************************
function private.CreateRecord( record ) 

    local new_pos_y   = 0;
    local record_name = "cheater_"..record.type.."_"..record.obj_num;
    local column_name = "cheater_"..record.type.."_column_"..record.column;
    local txt_params  = {};
    local spr_params  = {};
    
    new_pos_y = ( ( record.obj_num - 1 ) - ( record.column - 1 )*private.LINE_MAX_COUNT ) * 20;
  
    txt_params.pos_y        = new_pos_y;
    txt_params.text        = record.text;

    spr_params.pos_y        = new_pos_y;
    spr_params.event_mdown  = record.event_mdown;
    spr_params.event_menter = record.event_menter;
    spr_params.event_mleave = record.event_mleave;
    spr_params.color_b      = record.color_b;
    spr_params.color_g      = record.color_g;

    if ( not ObjGet( "spr_"..record_name ) ) then

      ObjCreate( "txt_"..record_name, "text" );
      ObjCreate( "spr_"..record_name, "spr" );

      txt_params.res = "assets/shared/cheater/courier_new";
      txt_params.fontsize    = 12;
      txt_params.align       = 0;
      txt_params.disprawtext = 1;
      txt_params.pos_x       = -80
      txt_params.color_r     = 0;
      txt_params.color_g     = 0;
      txt_params.color_b     = 0;
      txt_params.visible     = true; 
      txt_params.input       = false; 
      txt_params.active      = false;

      spr_params.res = "assets/shared/cheater/editor_back";
      spr_params.scale_x = 80;
      spr_params.scale_y = 10;
      spr_params.visible = true; 
      spr_params.alp     = 1;
      spr_params.input   = true;
      spr_params.active  = false;

    end;

    ObjSet( "txt_"..record_name, txt_params );
    ObjSet( "spr_"..record_name, spr_params );

    ObjAttach( "spr_"..record_name, column_name );
    ObjAttach( "txt_"..record_name, column_name );

end;
--******************************************************************************************
function private.DetachColumn( param0, param1 ) 

  local column_name_temp = "cheater_%s_column_%i";

  if ( param1 ) then

    local column_shifted_amount  = private.column[ param0 ];
    local column_num = column_shifted_amount; 

    if ( param1 == 'left' ) then
      column_num = column_num + private.COLUMN_MAX_COUNT + 1;
    end;

    local column_name = string.format( column_name_temp, param0, column_num );

    ObjDetach( column_name );

  else
    
    local counter = private.COLUMN_MAX_COUNT;
    local column_name = string.format( column_name_temp, param0, counter );

    while ( ObjGet( column_name ) and ObjGet( column_name ).name ) do

      ObjDetach( column_name );
      counter = counter + 1;
      column_name = string.format( column_name_temp, param0, counter);

    end

  end;

end;
--******************************************************************************************
function private.CreateColumn( column ) 

  local hub_name    = string.format( "cheater_%s_names", column.type );
  local column_numb = column.counter;    
  local column_name = string.format( "cheater_%s_column_%i", column.type, column_numb );
  local column_posx = private.RECORD_WIDTH * ( column.counter - 1 );

  if ( not ObjGet( column_name ) ) then
    ObjCreate( column_name, "obj" );
  end;

  ObjSet( column_name, 
  {
    pos_x  = column_posx,
    active = 0
  } );

  ObjAttach( column_name,  hub_name );

end;
--******************************************************************************************
function private.SetArrow( side, name_part, param )

  local arrow_name  = string.format( "obj_cheater_%s_arrow_%s", name_part, side );
  ObjSet( arrow_name,
    {
        input       = param
       ,visible     = param
    } );

end;
--******************************************************************************************
function private.MoveList( side, type )

  --DbgTrace( "MoveList: Moving "..side..". " );

  local direction = { right = 1, left = -1 };
  local show_func = { room = private.ShowRooms, progress = private.ShowProgress }

  private.column[ type ] = private.column[ type ] + direction[ side ];
  local column_shift = private.column[ type ];

  show_func[ type ]( column_shift );

  local hub = string.format( "cheater_%s_names", type );
  ObjSet( hub, 
  {
    pos_x = ( - 1 ) * private.RECORD_WIDTH * column_shift
  } );

  private.DetachColumn( type, side );

  private.SetArrow( "left", type, ( column_shift ~= 0 ) );

end;
--******************************************************************************************
--function *** OPTIONS ***() end;
--******************************************************************************************
function private.ChangeMode() 

  private.is_ipad = not private.is_ipad;
  private.HeightChange();

end;
--******************************************************************************************
function private.SkipVideo() 

  if ( _G[ "int_dialog_video" ] ) then

    int_dialog_video.SkipClick();

  end;

end;
--******************************************************************************************
private.sound = { sfx = 0, env = 0, soundtrack = 0, voice = 0 };
private.sound.is_on = true;
--******************************************************************************************
function private.SoundOnOff()

  private.sound.is_on = not private.sound.is_on;

  for type, value in pairs( private.sound ) do

    if ( private.sound.is_on ) then
      value = 0.7;
    else
      value = 0;
    end;
    SetSoundVolume( type, value );
  
  end;  

end
--******************************************************************************************
function private.ControlButtons( options )

  local buttons = 
    { 
      mode = "cheater_mode", 
      skip = "cheater_skip", 
      sound = "cheater_sound" 
    };
  local popups  = 
    { 
      mode = "Change cheater buttons height", 
      skip = "Skip video", 
      sound = "Enable or disable sounds" 
    };
  for option, value in pairs( options ) do

    local option_name = string.format( "spr_%s_back", buttons[ option ] );
    local option_params = ObjGet( option_name );
    local color = value / 2 + 0.5;
    local events = { enter = option_params.event_menter, leave = option_params.event_mleave };
    if ( value == 0 ) then
      events.enter = "";
      events.leave = "";
      popups[ option ] = popups[ option ].." ( disabled ) ";
    end;
    ObjSet( option_name, 
      { 
        color_r = color,
        color_g = color,
        color_b = color
        --event_menter = events.enter.."interface.PopupShow( '"..popups[ option ].."' );",
        --event_mleave = events.leave.."interface.PopupHide();"
      } );

  end;

end;
--******************************************************************************************
--function *** REVIWEW ***() end;
--******************************************************************************************
function private.InitReviewMode()
  private.default = {};

  private.default.review_field = common.GetObjParamsForSet( "obj_cheater_default_review_field" );

  private.default.review_field_mark = common.GetObjParamsForSet( "spr_cheater_default_review_field_mark" );
  private.default.review_field_text = common.GetObjParamsForSet( "ted_cheater_default_review_field_text" );
  private.default.review_field_back = common.GetObjParamsForSet( "spr_cheater_default_review_field_back" );
  private.default.review_field_link = common.GetObjParamsForSet( "grd_cheater_default_review_field_link" );

  private.reviewmode_enabled = false;

  private.review_mark_count = 0;

  private.review_mark_dragged = nil;
  private.review_back_dragged = nil;
end;
--******************************************************************************************
function private.ReviewMode()

  if not ( private.reviewmode_enabled ) then

    private.reviewmode_enabled = true;

    ObjSet( "ng_level", { active = 0 } );

    ObjCreate( "obj_cheater_review_field", "obj" );

    ObjSet( "obj_cheater_review_field", private.default.review_field );
    ObjAttach( "obj_cheater_review_field", "ng_application" );

    ObjAttach( "txt_cheater_review_field", "obj_cheater_review_field" );

    ObjAnimate( "txt_cheater_review_field", "alp", 1, 0, "",
    {
      0.0, 3, 0.5,
      1.5, 3, 1.0,
      3.0, 3, 0.5
    } );

  else

    if ( private.review_text_editing ) then

      private.ReviewTextEditingDone();

    end;

    MsgSubscribe( Event_Level_ScreenshotSaved, private.ReleaseReview );
    MsgSend( Event_Level_CheaterSaveScreenshot );

  end;

end;
--******************************************************************************************
function private.ReviewMark()

  if not ( private.review_text_editing ) then

    private.review_mark_count = private.review_mark_count + 1;
    local current_num = private.review_mark_count;

    local cur_pos = GetGameCursorPos();
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    local mark_object = "spr_cheater_review_field_mark_"..private.review_mark_count;
    local mark_params = private.default.review_field_mark;

    mark_params.pos_x = cur_pos[ 1 ];
    mark_params.pos_y = cur_pos[ 2 ];

    mark_params.event_startdrag = function () private.ReviewMarkDragStart( current_num ); end;
    mark_params.event_drag      = function () private.ReviewMarkDrag( current_num ); end;
    mark_params.event_dragdrop  = function () private.ReviewMarkDrop( current_num ); end;

    ObjCreate( mark_object, "spr" );
    ObjSet( mark_object, mark_params );
    ObjAttach( mark_object, "obj_cheater_review_field" );

    ObjAnimate( mark_object, "color_rgb", 1, 0, "",
    {
      0.0, 3, 1, 0, 0,
      0.3, 3, 1, 1, 1,
      0.6, 3, 1, 0, 0
    } );
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    local back_object = "spr_cheater_review_field_back_"..private.review_mark_count;
    local back_params = private.default.review_field_back;

    back_params.pos_x = 150 + 36 + mark_params.pos_x;
    back_params.pos_y = mark_params.pos_y;

    if ( mark_params.pos_x > 512 ) then

      back_params.pos_x = - 150 - 36 + mark_params.pos_x;

    end;

    back_params.event_startdrag = function () private.ReviewBackDragStart( current_num ); end;
    back_params.event_drag      = function () private.ReviewBackDrag( current_num ); end;
    back_params.event_dragdrop  = function () private.ReviewBackDrop( current_num ); end;

    ObjCreate( back_object, "spr" );
    ObjSet( back_object, back_params );
    ObjAttach( back_object, "obj_cheater_review_field" );
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    local text_object = "ted_cheater_review_field_text_"..private.review_mark_count;
    local text_params = private.default.review_field_text;

    text_params.pos_x = 150 + 36 + mark_params.pos_x;
    text_params.pos_y = mark_params.pos_y;

    if ( mark_params.pos_x > 512 ) then

      text_params.pos_x = - 150 - 36 + mark_params.pos_x;

    end;

    ObjCreate( text_object, "textedit" );
    ObjSet( text_object, text_params );
    ObjAttach( text_object, "obj_cheater_review_field" );
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    local link_object = "grd_cheater_review_field_link_"..private.review_mark_count;
    local link_params = private.default.review_field_link; 

    ObjCreate( link_object, "grid" );
    ObjSet( link_object, link_params );
    ObjAttach( link_object, "obj_cheater_review_field" );

    private.ReviewLinkUpdatePos( private.review_mark_count );
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    private.review_text_editing = true;

    local timer_object = "tmr_cheater_review_field_text_"..private.review_mark_count;

    ObjCreate( timer_object, "timer" );
    ObjAttach( timer_object, "obj_cheater_review_field" );
    ObjSet( timer_object, { time = 0.01, endtrig = private.ReviewBackUpdate, playing = true } );
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

  end;

end;
--******************************************************************************************
function private.ReviewBackUpdate()

  if ( private.review_text_editing ) then

    local text_object = "ted_cheater_review_field_text_"..private.review_mark_count;
    local text_params = ObjGet( text_object );

    local back_object = "spr_cheater_review_field_back_"..private.review_mark_count;
    ObjSet( back_object, { scale_y = ( text_params.draw_height / 64 ) } );

    local timer_object = "tmr_cheater_review_field_text_"..private.review_mark_count;
    ObjSet( timer_object, { time = 0.01, endtring = private.ReviewBackUpdate, playing = true } );

  end;

end;
--******************************************************************************************
function private.ReviewTextEditingDone()

  private.review_text_editing = false;

  local timer_object = "tmr_cheater_review_field_text_"..private.review_mark_count;
  ObjDelete( timer_object );

  local text_object = "ted_cheater_review_field_text_"..private.review_mark_count;
  local text_params = common.GetObjParamsForSet( text_object );
  ObjDelete( text_object );

  local mark_object = "spr_cheater_review_field_mark_"..private.review_mark_count;
  local back_object = "spr_cheater_review_field_back_"..private.review_mark_count;
  local link_object = "grd_cheater_review_field_link_"..private.review_mark_count;

  if ( text_params.text == "" ) then

    ObjDelete( mark_object );
    ObjDelete( back_object );
    ObjDelete( link_object );

  else

    local mark_params = ObjGet( mark_object );
    
    ObjAnimate( mark_object, "color_rgb", 0, 0, "",
    {
      0.0, 3, mark_params.color_r, mark_params.color_g, mark_params.color_b,
      0.3, 3, 1, 0, 0
    } );

    ObjSet( mark_object, { input = true } );
    ObjSet( back_object, { input = true } );

    -- Создание нового.
    text_object = "txt_cheater_review_field_text_"..private.review_mark_count;
    ObjCreate( text_object, "text" );
    text_params.input = false;
    ObjSet( text_object, text_params );
    ObjAttach( text_object, "obj_cheater_review_field" );

  end;

end;
--******************************************************************************************
function private.ReviewMarkDragStart( id )

  SetCursor( CURSOR_NULL );

  private.review_mark_dragged = id;

end;
--******************************************************************************************
function private.ReviewMarkDrag( id )

  local cur_pos = GetGameCursorPos();

  local new_x = cur_pos[ 1 ];
  local new_y = cur_pos[ 2 ];

  local mark_object = "spr_cheater_review_field_mark_"..id;

  ObjSet( mark_object, { pos_x = new_x, pos_y = new_y } );

  private.ReviewLinkUpdatePos( id );

end;
--******************************************************************************************
function private.ReviewMarkDrop( id )

  SetCursor( CURSOR_DEFAULT );

  private.review_mark_dragged = nil;

end;
--******************************************************************************************
function private.ReviewBackDragStart( id )

  SetCursor( CURSOR_NULL );

end;
--******************************************************************************************
function private.ReviewBackDrag( id )

  local cur_pos = GetGameCursorPos();

  local new_x = cur_pos[ 1 ];
  local new_y = cur_pos[ 2 ];

  local back_object = "spr_cheater_review_field_back_"..id;
  local text_object = "txt_cheater_review_field_text_"..id;

  ObjSet( back_object, { pos_x = new_x, pos_y = new_y } );
  ObjSet( text_object, { pos_x = new_x, pos_y = new_y } );

  private.ReviewLinkUpdatePos( id );

end;
--******************************************************************************************
function private.ReviewBackDrop( id )

  SetCursor( CURSOR_DEFAULT );

end;
--******************************************************************************************
function private.ReviewLinkUpdatePos( id )

  local mark_object = "spr_cheater_review_field_mark_"..id;
  local mark_params = ObjGet( mark_object );

  local back_object = "spr_cheater_review_field_back_"..id;
  local back_params = ObjGet( back_object );

  local angle = math.atan2( ( back_params.pos_y - mark_params.pos_y ), ( back_params.pos_x - mark_params.pos_x ) );

  -- Право.
  if     ( angle >= - 0.785 ) and ( angle <   0.785 ) then

    mark_params.pos_x = mark_params.pos_x + 0.5 * 64 * mark_params.scale_x;

    back_params.pos_x = back_params.pos_x - 150;

  -- Верх.
  elseif ( angle >= - 2.355 ) and ( angle < - 0.785 ) then

    mark_params.pos_y = mark_params.pos_y - 0.5 * 64 * mark_params.scale_y;

    back_params.pos_y = back_params.pos_y + 0.5 * 64 * back_params.scale_y;

  -- Низ.
  elseif ( angle >=   0.785 ) and ( angle <   2.355 ) then

    mark_params.pos_y = mark_params.pos_y + 0.5 * 64 * mark_params.scale_y;

    back_params.pos_y = back_params.pos_y - 0.5 * 64 * back_params.scale_y;

  -- Лево.
  elseif ( ( angle >=   2.355 ) and ( angle <   3.142 ) )
  or     ( ( angle >= - 3.142 ) and ( angle < - 2.355 ) ) then

    mark_params.pos_x = mark_params.pos_x - 0.5 * 64 * mark_params.scale_x;

    back_params.pos_x = back_params.pos_x + 150;

  end;

  angle = math.atan2( ( back_params.pos_y - mark_params.pos_y ), ( back_params.pos_x - mark_params.pos_x ) );

  -- Половина высоты ресурса линка.
  local radius = 9;

  local point0 = { pos_x = mark_params.pos_x + radius * math.cos( - 1.57 + angle ),
                   pos_y = mark_params.pos_y + radius * math.sin( - 1.57 + angle ) };

  local point2 = { pos_x = mark_params.pos_x + radius * math.cos(   1.57 + angle ),
                   pos_y = mark_params.pos_y + radius * math.sin(   1.57 + angle ) };

  local point1 = { pos_x = back_params.pos_x + radius * math.cos( - 1.57 + angle ),
                   pos_y = back_params.pos_y + radius * math.sin( - 1.57 + angle ) };

  local point3 = { pos_x = back_params.pos_x + radius * math.cos(   1.57 + angle ),
                   pos_y = back_params.pos_y + radius * math.sin(   1.57 + angle ) };

  local link_object = "grd_cheater_review_field_link_"..id;
  GridSet( link_object, 0,
  {
    0, point0,
    2, point2,
    1, point1,
    3, point3,
  } );

end;
--******************************************************************************************
function private.ReviewMarkUp()

  if ( private.review_mark_dragged ) then

    local mark_object = "spr_cheater_review_field_mark_"..private.review_mark_dragged;
    local mark_params = ObjGet( mark_object );

    local scale_new = mark_params.scale_x + 0.1;

    ObjSet( mark_object, { scale_x = scale_new, scale_y = scale_new } );

  end;

end;
--******************************************************************************************
function private.ReviewMarkDown()

  if ( private.review_mark_dragged ) then

    local mark_object = "spr_cheater_review_field_mark_"..private.review_mark_dragged;
    local mark_params = ObjGet( mark_object );

    local scale_new = mark_params.scale_x - 0.1;

    ObjSet( mark_object, { scale_x = scale_new, scale_y = scale_new } );

  end;

end;
--******************************************************************************************
function private.ReleaseReview()  

  MsgUnsubscribe( Event_Level_ScreenshotSaved, private.ReleaseReview );
  private.reviewmode_enabled = false;
  ObjDetach( "txt_cheater_review_field" );
  ObjDelete( "obj_cheater_review_field" );
  ObjSet( "ng_level", { active = true } );
  private.text_editing = false;

end;
--******************************************************************************************
-- function *** OTHER *** () end;
--******************************************************************************************
function private.ShowFPS( playing_value )

  ObjSet( "txt_cheater_current_fps", { text = "fps: "..string.format( "%.1f", GetFPS() ) } );
  ObjSet( "tmr_cheater_fps",     { playing = playing_value, time = 0.2, endtrig = function () private.ShowFPS( playing_value ); end } );

end;
--******************************************************************************************
function private.HandleAltF1()

  private.onScreen = not private.onScreen;
  ObjSet("cheater",{ visible = private.onScreen, active = private.onScreen, input = private.onScreen } );

end;
--******************************************************************************************
function private.WideScreenUpdate()

  --interface.WideScreenUpdate( "cheater" );

end;
--******************************************************************************************