DIFF

DigitalCodeLockUI.c

class DigitalCodeLockUI : DigitalCodeLockUIBase
{
	private const int MOVE_LEFT = 0;
	private const int MOVE_RIGHT = 1;
	private const int MOVE_UP = 2;
	private const int MOVE_DOWN = 3;
	
	private const int RESET_BUTTON_INDEX = 9;
	private const int CONFIRM_BUTTON_INDEX = 11;
	
	private const float BT_REPEAT_DELAY = 0.35;				// Delay from first press to begin tick repeat state
	private const float BT_REPEAT_TIME = 0.09;				// Tick repeat frequency time
	private const float BT_THRESHOLD_VALUE = 0.8;			// Threshold values for analog inputs
	
	private const int MIN_LOCK_PIN_LENGTH = 4;
	private const int MAX_LOCK_PIN_LENGTH = 6;
	
#ifndef PLATFORM_CONSOLE
	private const int KEY_DIGIT_1 = 0;
	private const int KEY_DIGIT_2 = 1;
	private const int KEY_DIGIT_3 = 2;
	private const int KEY_DIGIT_4 = 3;
	private const int KEY_DIGIT_5 = 4;
	private const int KEY_DIGIT_6 = 5;
	private const int KEY_DIGIT_7 = 6;
	private const int KEY_DIGIT_8 = 7;
	private const int KEY_DIGIT_9 = 8;
	private const int KEY_DIGIT_CLEAR = 9;
	private const int KEY_DIGIT_0 = 10;
	private const int KEY_DIGIT_CONFIRM = 11;
	
	protected ref map<KeyCode, int> m_KeyCodeMap;
#endif
	
	private ref array<string> m_InvInputNames = {"UAUILeftInventory","UAUIRightInventory","UAUIUpInventory","UAUIDownInventory"};
	private ref array<UAIDWrapper> m_InvInputWrappers;
	private ref array<float> m_InvInputTimes;
	private int m_InvInputWrappersCount;
	private float m_SensitivityThreshold = 0.0;
	private UAInput m_InvUAInput;
	private int m_InvInputActive = 0;
	
	private ref array<string> m_InputExcludes = {"menu_move"};
	
	private ref array<ButtonWidget> m_NumButtons;
	private int m_SelectedButtonIndex = 0; 					// Default to button "5" (index 4)
	protected bool m_IsActive = false;
	
	private string m_EnteredPIN;
	private EntityAI m_TargetLock;
	private vector m_TargetPosition;
	private bool m_HasPIN = false;
	
	protected Widget m_ConsoleToolbar;
	protected RichTextWidget m_ConsoleToolbarRichText;
	
	void DigitalCodeLockUI()
	{
	#ifndef PLATFORM_CONSOLE
		if (!m_KeyCodeMap)
		{
			m_KeyCodeMap = new map<KeyCode, int>();
			m_KeyCodeMap.Insert(KeyCode.KC_1, KEY_DIGIT_1);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD1, KEY_DIGIT_1);
			m_KeyCodeMap.Insert(KeyCode.KC_2, KEY_DIGIT_2);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD2, KEY_DIGIT_2);
			m_KeyCodeMap.Insert(KeyCode.KC_3, KEY_DIGIT_3);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD3, KEY_DIGIT_3);
			m_KeyCodeMap.Insert(KeyCode.KC_4, KEY_DIGIT_4);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD4, KEY_DIGIT_4);
			m_KeyCodeMap.Insert(KeyCode.KC_5, KEY_DIGIT_5);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD5, KEY_DIGIT_5);
			m_KeyCodeMap.Insert(KeyCode.KC_6, KEY_DIGIT_6);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD6, KEY_DIGIT_6);
			m_KeyCodeMap.Insert(KeyCode.KC_7, KEY_DIGIT_7);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD7, KEY_DIGIT_7);
			m_KeyCodeMap.Insert(KeyCode.KC_8, KEY_DIGIT_8);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD8, KEY_DIGIT_8);
			m_KeyCodeMap.Insert(KeyCode.KC_9, KEY_DIGIT_9);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD9, KEY_DIGIT_9);
			m_KeyCodeMap.Insert(KeyCode.KC_BACK, KEY_DIGIT_CLEAR);
			m_KeyCodeMap.Insert(KeyCode.KC_0, KEY_DIGIT_0);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPAD0, KEY_DIGIT_0);
			m_KeyCodeMap.Insert(KeyCode.KC_NUMPADENTER, KEY_DIGIT_CONFIRM);
			m_KeyCodeMap.Insert(KeyCode.KC_RETURN, KEY_DIGIT_CONFIRM);
		}
	#endif
	}
	
	void ~DigitalCodeLockUI()
	{
		m_EnteredPIN = "";
		m_TargetLock = null;
	}
	
	void InitInputWrapperData()
	{
		int namesCount = m_InvInputNames.Count();
		m_InvInputWrappers = new array<UAIDWrapper>();
		m_InvInputTimes = new array<float>;
		
		foreach (string inputName : m_InvInputNames)
		{
			m_InvInputWrappers.Insert(GetUApi().GetInputByName(inputName).GetPersistentWrapper());
			m_InvInputTimes.Insert(0);
		}
		
		m_InvInputWrappersCount = m_InvInputWrappers.Count();
	}
	
	override void SetTargetLock(CodeLockComponent lock)
	{
		if (m_TargetLock || !lock)
			return;
		
		m_TargetLock = lock.GetParent();
		m_TargetPosition = lock.GetPosition();
		m_HasPIN = lock.HasPIN();
		
		#ifdef CODELOCKCOMPONENT_LOG
		ErrorEx("Set lock=" + m_TargetLock, ErrorExSeverity.INFO);
		ErrorEx("Lock position=" + m_TargetPosition, ErrorExSeverity.INFO);
		ErrorEx("Has code=" + m_HasPIN, ErrorExSeverity.INFO);
		#endif
	}	
	
	protected string GetLayout()
	{
		return "gui/layouts/day_z_digital_lock.layout";
	}
	
	override Widget Init()
	{
		layoutRoot = GetGame().GetWorkspace().CreateWidgets(GetLayout());
		
		m_ConsoleToolbar = layoutRoot.FindWidget("ConsoleToolbar");
		m_ConsoleToolbarRichText = RichTextWidget.Cast(layoutRoot.FindAnyWidget("ContextToolbarText"));
		
		InitInputWrapperData();
		InitializeButtons();
		
		#ifdef PLATFORM_CONSOLE
		GetGame().GetMission().GetOnInputDeviceChanged().Insert(OnInputDeviceChanged);
		#else
		m_ConsoleToolbar.Show(false);
		#endif
		
		return layoutRoot;
	}
	
	void InitializeButtons()
	{
		// Store references to all number buttons in order (1-9, C, 0, Enter)
		m_NumButtons = new array<ButtonWidget>;
		
		// Add all buttons in the layout to the array
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber1_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber2_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber3_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber4_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber5_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber6_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber7_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber8_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber9_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyClear_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyNumber0_Button")));
		m_NumButtons.Insert(ButtonWidget.Cast(layoutRoot.FindAnyWidget("KeyLock_Button")));
	}
			
	protected void ClearInput()
	{
		m_EnteredPIN = "";
		for (int i = 0; i < MAX_LOCK_PIN_LENGTH; ++i)
		{
			TextWidget digitTextW = TextWidget.Cast(layoutRoot.FindAnyWidget("PasswordDigitNumber_Text" + i));
			if (digitTextW)
				digitTextW.SetText("");
		}
	}
	
	protected void Select()
	{
		int i;
		TextWidget digitTextW;
		
		if (m_SelectedButtonIndex >= 0 && m_SelectedButtonIndex < m_NumButtons.Count())
		{
			Param1<int> uiAction = new Param1<int>(0);
			if (m_SelectedButtonIndex == CONFIRM_BUTTON_INDEX)		// Confirm
			{
				uiAction.param1 = CodeLockUIAction.CONFIRM_INPUT;
				g_Game.RPCSingleParam(m_TargetLock, ERPCs.RPC_CODE_LOCK_UI_ACTION, uiAction, true);
				ConfirmCode();
			}
			else if (m_SelectedButtonIndex == RESET_BUTTON_INDEX) 	// Reset
			{
				uiAction.param1 = CodeLockUIAction.CLEAR_INPUT;
				g_Game.RPCSingleParam(m_TargetLock, ERPCs.RPC_CODE_LOCK_UI_ACTION, uiAction, true);
				ClearInput();
			}
			else									// Select digit
			{
				uiAction.param1 = CodeLockUIAction.DIGIT_INPUT;
				g_Game.RPCSingleParam(m_TargetLock, ERPCs.RPC_CODE_LOCK_UI_ACTION, uiAction, true);
				
				string codeText;
				if (m_EnteredPIN == "")
				{
					codeText = "";
				}
				else
				{
					codeText = m_EnteredPIN;
				}
								
				int codeLength = codeText.Length();
				if (codeLength < MAX_LOCK_PIN_LENGTH)
				{
					int digit;
					if (m_SelectedButtonIndex <= 9)
						digit = m_SelectedButtonIndex + 1;
					else if (m_SelectedButtonIndex == 10)
						digit = 0;
					
					codeText = codeText + digit.ToString();
					m_EnteredPIN = codeText;
					
					#ifdef CODELOCKCOMPONENT_LOG
					ErrorEx("Code=" + m_EnteredPIN, ErrorExSeverity.INFO);
					#endif
					
					string digitS = m_EnteredPIN[0];
					
					for (i = 0; i < m_EnteredPIN.Length(); ++i)
					{										
						digitTextW = TextWidget.Cast(layoutRoot.FindAnyWidget("PasswordDigitNumber_Text" + i));
						if (digitTextW)
							digitTextW.SetText(m_EnteredPIN[i]);
					}
				}
			}
		}
	}
	
	protected void ConfirmCode()
	{
		if (m_EnteredPIN == "" || !m_TargetLock)
			return;
		
		int pinLength = m_EnteredPIN.Length();
		int minPINLength = MIN_LOCK_PIN_LENGTH;
		
		DigitalCodeLock codeLock;
		if (Class.CastTo(codeLock, m_TargetLock))
			minPINLength = codeLock.GetMinLockPINLength();
		
		if (pinLength < minPINLength || pinLength > MAX_LOCK_PIN_LENGTH)
			return;
		
		#ifdef CODELOCKCOMPONENT_LOG
		ErrorEx("PIN set to=" + m_EnteredPIN, ErrorExSeverity.INFO);
		#endif
		
		for (int i = 0; i < MAX_LOCK_PIN_LENGTH - 1; ++i)
		{
			TextWidget digitTextW = TextWidget.Cast(layoutRoot.FindAnyWidget("PasswordDigitNumber_Text" + i));
			if (digitTextW)
				digitTextW.SetText("");
		}
		
		Param1<string> pin = new Param1<string>(m_EnteredPIN);
		g_Game.RPCSingleParam(m_TargetLock, ERPCs.RPC_CODE_LOCK_REQUEST, pin, true);
		m_EnteredPIN = "";
	}
	
	override void CodeResponse(bool response) 
	{
		if (m_HasPIN)
		{
			if (response)
			{
				// Lock has code and dialed code was correct. CLOSE UI
				Close();
			}
			else
			{			
				// Lock has code and dialed code was incorrect. CLOSE UI
				Close();
			}
		}
		else
		{
			if (response)
			{
				// Lock has no code and dialed code was accepted. CLOSE UI
				Close();
			}
		}
	}
	
	protected void ControllerMovementButtonTickHandler(float timeslice)
	{
		float tickvalue = 0;
		
		if (m_SensitivityThreshold > 0.0)
		{
			if (m_SensitivityThreshold < BT_REPEAT_TIME) //sensitivity solution...
			{
				m_SensitivityThreshold += timeslice;
				return;
			}
			else
			{
				m_SensitivityThreshold = 0.0;
			}
		}
		
		for (int i = 0; i < m_InvInputWrappersCount; i++)
		{
			m_InvUAInput = m_InvInputWrappers[i].InputP();
			
			if (m_InvUAInput.LocalValue() > BT_THRESHOLD_VALUE)
			{
				tickvalue = m_InvInputTimes[i];
				tickvalue += timeslice;
			}
			else
			{
				tickvalue = 0.0;
			}
			
			if (tickvalue < BT_REPEAT_DELAY && m_InvUAInput.LocalPress())
			{
				m_InvInputActive |= (1 << i);
				m_SensitivityThreshold += timeslice;
			}
			else if (tickvalue > (BT_REPEAT_DELAY + BT_REPEAT_TIME))
			{
				while (tickvalue > (BT_REPEAT_DELAY + BT_REPEAT_TIME))
				{
					tickvalue -= BT_REPEAT_TIME;
				}
				
				m_InvInputActive |= (1 << i);
				m_SensitivityThreshold += timeslice;
			}
			else
			{
				m_InvInputActive &= ~(1 << i);
			}
			
			m_InvInputTimes[i] = tickvalue;
		}
	}
	
	override void Update(float timeslice)
	{
		super.Update(timeslice);
		
		// Close menu if player reaches certain distance to lock
		if (m_TargetLock)
		{
			Man player = g_Game.GetPlayer();
			vector playerPos = player.GetPosition();
			
			if (vector.DistanceSq(playerPos, m_TargetPosition) > 5.0 * 5.0)
			{
				Close();
				return;
			}
		}
		
		Input input = GetGame().GetInput();
		if (input.LocalPress("UAUIBack", false))
		{
			Close();
			return;
		}
				
		#ifdef PLATFORM_CONSOLE		
		CheckControllerInputs(input, timeslice);
		#endif
	}
	
	protected void CheckControllerInputs(Input input, float timeslice)
	{
		if (input.LocalPress("UAUICtrlX", false))
		{
			ConfirmCode();
			return;
		}
		else if (input.LocalPress("UAUICtrlY", false))
		{
			ClearInput();
			return;
		}
		
		// lock ui movement
		ControllerMovementButtonTickHandler(timeslice);
		
		if (m_InvInputActive & (1 << MOVE_RIGHT))
		{
			NavigateRight();
		}
		
		if (m_InvInputActive & (1 << MOVE_LEFT))
		{
			NavigateLeft();
		}
		
		if (m_InvInputActive & (1 << MOVE_UP))
		{
			NavigateUp();
		}
		
		if (m_InvInputActive & (1 << MOVE_DOWN))
		{
			NavigateDown();
		}
		
		m_InvInputActive = 0;
	}
	
	protected void NavigateRight()
	{
		int row = m_SelectedButtonIndex / 3;
		int col = m_SelectedButtonIndex % 3;
		
		// Move right (if not at right edge)
		if (col < 2)
		{
			m_SelectedButtonIndex = row * 3 + (col + 1);
			UpdateFocus();
		}
	}
    
	protected void NavigateLeft()
	{
		int row = m_SelectedButtonIndex / 3;
		int col = m_SelectedButtonIndex % 3;
		
		// Move left (if not at left edge)
		if (col > 0)
		{
			m_SelectedButtonIndex = row * 3 + (col - 1);
			UpdateFocus();
	    }
	}
    
	protected void NavigateUp()
	{
		int row = m_SelectedButtonIndex / 3;
		int col = m_SelectedButtonIndex % 3;
		
		// Move up (if not at top edge)
		if (row > 0)
		{
			m_SelectedButtonIndex = (row - 1) * 3 + col;
			UpdateFocus();
		}
	}
    
	protected void NavigateDown()
	{
		int row = m_SelectedButtonIndex / 3;
		int col = m_SelectedButtonIndex % 3;
		
		// Move down (if not at bottom edge)
		if (row < 3)
		{
			m_SelectedButtonIndex = (row + 1) * 3 + col;
			UpdateFocus();
	    }
	}
	
	protected void UpdateFocus()
	{
		if (m_SelectedButtonIndex >= 0 && m_SelectedButtonIndex < m_NumButtons.Count())
		{
			// Position highlight over selected button
			ButtonWidget selectedBtn = m_NumButtons[m_SelectedButtonIndex];
			if (selectedBtn)
			{
				SetFocus(selectedBtn);
			}
		}
	}
	
	void SetActive(bool active)
	{
		m_IsActive = active;
	
		if (active)
		{
			m_SelectedButtonIndex = -1;
		}
	}
		
	// Console toolbar
	protected void UpdateConsoleToolbar()
	{
		string select = string.Format(" %1", InputUtils.GetRichtextButtonIconFromInputAction("UAUISelect", "Select", EUAINPUT_DEVICE_CONTROLLER, InputUtils.ICON_SCALE_TOOLBAR));
		string confirm = string.Format(" %1", InputUtils.GetRichtextButtonIconFromInputAction("UAUICtrlX", "Confirm PIN", EUAINPUT_DEVICE_CONTROLLER, InputUtils.ICON_SCALE_TOOLBAR));
		string clear = string.Format(" %1", InputUtils.GetRichtextButtonIconFromInputAction("UAUICtrlY", "Clear PIN", EUAINPUT_DEVICE_CONTROLLER, InputUtils.ICON_SCALE_TOOLBAR));
		string close = string.Format(" %1", InputUtils.GetRichtextButtonIconFromInputAction("UAUIBack", "Exit", EUAINPUT_DEVICE_CONTROLLER, InputUtils.ICON_SCALE_TOOLBAR));
		
		string navigateIcon;
		#ifdef PLATFORM_PS4
		navigateIcon = string.Format("<image set=\"%1\" name=\"%2\" scale=\"%3\" />", "playstation_buttons2", "DPAD_all_directions");
		#else
		navigateIcon = string.Format("<image set=\"%1\" name=\"%2\" scale=\"%3\" />", "xbox_buttons2", "DPAD_all_directions");
		#endif
		string navigate = string.Format(" %1 %2", navigateIcon, "Navigate");
		string text = string.Format("%1 %2 %3 %4 %5", navigate, select, confirm, clear, close);
		m_ConsoleToolbarRichText.SetText(text);
	}
	
	protected void OnInputDeviceChanged(EInputDeviceType pInputDeviceType)
	{
		int i;
		ButtonWidget button;
		MissionGameplay mission = MissionGameplay.Cast(g_Game.GetMission());
		
		switch (pInputDeviceType)
		{
			case EInputDeviceType.CONTROLLER:
			{
				m_ConsoleToolbar.Show(true);
				
				if (mission)
					mission.MoveHudForInventory(true);
				
				g_Game.GetUIManager().ShowUICursor(false);
								
				button = ButtonWidget.Cast(GetFocus());
				if (button)
					button.SetState(false);
				
				SetFocus(m_NumButtons[0]);
				
				UpdateConsoleToolbar();
				break;
			}
			default:
			{
				if (g_Game.GetInput().IsEnabledMouseAndKeyboardEvenOnServer())
				{
					m_ConsoleToolbar.Show(false);
					
					if (mission)
						mission.MoveHudForInventory(false);
					
					g_Game.GetUIManager().ShowUICursor(true);
										
					button = ButtonWidget.Cast(GetFocus());
					if (button)
						button.SetState(false);
					
					SetFocus(m_NumButtons[0]);
				}
				break;
			}
		}
	}
	
	override void OnShow()
	{
		super.OnShow();
		
		MissionGameplay mission = MissionGameplay.Cast(g_Game.GetMission());
		if (!mission)
			return;
		
		mission.AddActiveInputExcludes(m_InputExcludes);
		
		#ifdef PLATFORM_CONSOLE
		Widget w = m_NumButtons[0];
		if (w)
			SetFocus(w);
		
		mission.MoveHudForInventory(true);
		
		OnInputDeviceChanged(g_Game.GetInput().GetCurrentInputDevice());
		#endif
	}
		
	override void OnHide()
	{
		super.OnHide();
		
		MissionGameplay mission = MissionGameplay.Cast(g_Game.GetMission());
		if (!mission)
			return;
		
		mission.RemoveActiveInputExcludes(m_InputExcludes);
		#ifdef PLATFORM_CONSOLE
		mission.MoveHudForInventory(false);
		#endif
		
		ClearInput();
	}
	
	override bool OnClick(Widget w, int x, int y, int button)
	{
		super.OnClick(w, x, y, button);
		
		for (int i = 0; i < m_NumButtons.Count(); i++)
		{
			ButtonWidget bw = m_NumButtons[i];
			if (w == bw)
			{
				m_SelectedButtonIndex = i;
				Select();
				return true;
			}
		}
		
		return false;
	}

#ifndef PLATFORM_CONSOLE
	override void KeyPress(int key)
	{
		super.KeyPress(key);	
		
		int buttonIndex = -1;
		if (m_KeyCodeMap.Find(key, buttonIndex) && buttonIndex > -1)
		{
			m_SelectedButtonIndex = buttonIndex;
			Select();
		}
	}
#endif
}