Drone::Drone(int txPin, int rxPin) {
  _serialIO = SerialIO(txPin, rxPin);
  _callback = Callback();
  _incomingPacketReader = IncomingPacketReader(&_serialIO);
  _vitals = Vitals(&_serialIO, &_incomingPacketReader, &_callback);
  _responseHandler = ResponseHandler(&_serialIO, &_incomingPacketReader, &_callback, &_vitals);
  _rc = RC(&_serialIO, &_callback, &_incomingPacketReader);
  _gpio = GPIO(&_serialIO, &_callback, &_incomingPacketReader);
  _i2c = I2C(&_serialIO, &_callback, &_incomingPacketReader);
  _pose = Pose(&_serialIO, &_callback, &_incomingPacketReader);
  _autopilot = Autopilot(&_serialIO, &_callback, &_incomingPacketReader);
  _transmitterSupport = TransmitterSupport(&_serialIO, &_callback, &_incomingPacketReader);
}
LONG CTranscendenceWnd::WMKeyDown (int iVirtKey, DWORD dwKeyData)

//	WMKeyDown
//
//	Handle WM_KEYDOWN

	{
	bool bKeyRepeat = uiIsKeyRepeat(dwKeyData);

	switch (m_State)
		{
		case gsInGame:
			{
			//	If no player, then nothing to do

			if (GetPlayer() == NULL)
				NULL;

			//	Deal with console

			else if (m_bDebugConsole)
				{
				if (iVirtKey == VK_ESCAPE)
					m_bDebugConsole = false;
				else
					m_DebugConsole.OnKeyDown(iVirtKey, dwKeyData);
				}

			//	If we're paused, then check for unpause key

			else if (m_bPaused)
				{
				if ((iVirtKey < 'A' || iVirtKey > 'Z') && iVirtKey != VK_SPACE && iVirtKey != VK_F9)
					{
					m_bPaused = false;
					DisplayMessage(CONSTLIT("Game continues"));
					}

				//	We allow access to the debug console

				else
					{
					CGameKeys::Keys iCommand = m_pTC->GetKeyMap().GetGameCommand(iVirtKey);
					if (iCommand == CGameKeys::keyShowConsole 
							&& m_pTC->GetOptionBoolean(CGameSettings::debugMode)
							&& !g_pUniverse->IsRegistered())
						m_bDebugConsole = !m_bDebugConsole;
					}
				}

			//	Handle menu, if it is up

			else if (m_CurrentMenu != menuNone)
				{
				if (iVirtKey == VK_ESCAPE)
					m_CurrentMenu = menuNone;
				else
					{
					CGameKeys::Keys iCommand = m_pTC->GetKeyMap().GetGameCommand(iVirtKey);
					if ((iCommand == CGameKeys::keyInvokePower && m_CurrentMenu == menuInvoke)
							|| (iCommand == CGameKeys::keyCommunications && m_CurrentMenu == menuCommsTarget))
						m_CurrentMenu = menuNone;
					}
				}

			//	Handle picker

			else if (m_CurrentPicker != pickNone)
				{
				if (iVirtKey == VK_RETURN)
					{
					switch (m_CurrentPicker)
						{
						case pickUsableItem:
							DoUseItemCommand(m_MenuData.GetItemData(m_PickerDisplay.GetSelection()));
							break;

						case pickEnableDisableItem:
							GetPlayer()->SetUIMessageEnabled(uimsgEnableDeviceHint, false);
							DoEnableDisableItemCommand(m_MenuData.GetItemData(m_PickerDisplay.GetSelection()));
							break;
						}
					}
				else if (iVirtKey == VK_LEFT)
					m_PickerDisplay.SelectPrev();

				else if (iVirtKey == VK_RIGHT)
					m_PickerDisplay.SelectNext();

				else if (iVirtKey == VK_ESCAPE)
					m_CurrentPicker = pickNone;

				else
					{
					CGameKeys::Keys iCommand = m_pTC->GetKeyMap().GetGameCommand(iVirtKey);
					if ((iCommand == CGameKeys::keyEnableDevice && m_CurrentPicker == pickEnableDisableItem)
							|| (iCommand == CGameKeys::keyUseItem && m_CurrentPicker == pickUsableItem))
						m_CurrentPicker = pickNone;
					}
				}

			//	Otherwise we're in normal game mode

			else
				{
				//	If showing the map, then we need to handle some keys

				if (m_bShowingMap)
					{
					switch (iVirtKey)
						{
						case 'H':
							GetPlayer()->SetMapHUD(!GetPlayer()->IsMapHUDActive());
							break;

						case VK_SUBTRACT:
						case VK_OEM_MINUS:
							if (m_iMapScale < (MAP_SCALE_COUNT - 1))
								{
								m_iMapScale++;
								m_iMapZoomEffect = 100;
								}
							break;

						case VK_ADD:
						case VK_OEM_PLUS:
							if (m_iMapScale > 0)
								{
								m_iMapScale--;
								m_iMapZoomEffect = -100;
								}
							break;
						}

					//	Fall through because normal commands are available with the map
					}

				//	See if this is a command

				CGameKeys::Keys iCommand = m_pTC->GetKeyMap().GetGameCommand(iVirtKey);
				switch (iCommand)
					{
					case CGameKeys::keyAutopilot:
						Autopilot(!m_bAutopilot);
						GetPlayer()->SetUIMessageEnabled(uimsgAutopilotHint, false);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyEnableDevice:
						if (!GetPlayer()->DockingInProgress() 
								&& !GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							ShowEnableDisablePicker();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyCommunications:
						if (!GetPlayer()->DockingInProgress()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetUIMessageEnabled(uimsgCommsHint, false);
							ShowCommsTargetMenu();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyDock:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped()
								&& !bKeyRepeat)
							{
							Autopilot(false);
							GetPlayer()->Dock();
							m_bDockKeyDown = true;
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyTargetNextFriendly:
						GetPlayer()->SelectNextFriendly(1);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyTargetPrevFriendly:
						GetPlayer()->SelectNextFriendly(-1);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyEnterGate:
						if (!GetPlayer()->DockingInProgress()
								&& !GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->Gate();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyInvokePower:
						if (!GetPlayer()->DockingInProgress()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							ShowInvokeMenu();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyShowMap:
						if (m_bShowingMap)
							Autopilot(false);
						m_bShowingMap = !m_bShowingMap;
						GetPlayer()->SetUIMessageEnabled(uimsgMapHint, false);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyShowGalacticMap:
						g_pHI->HICommand(CONSTLIT("uiShowGalacticMap"));
						GetPlayer()->SetUIMessageEnabled(uimsgGalacticMapHint, false);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyPause:
						{
						m_bPaused = true;
						if (GetPlayer())
							{
							GetPlayer()->SetThrust(false);
							GetPlayer()->SetManeuver(IShipController::NoRotation);
							GetPlayer()->SetFireMain(false);
							GetPlayer()->SetFireMissile(false);
							}
						DisplayMessage(CONSTLIT("Game paused"));
						m_chKeyDown = iVirtKey;
						break;
						}

					case CGameKeys::keySquadronCommands:
						if (!GetPlayer()->DockingInProgress()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							ShowCommsSquadronMenu();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyClearTarget:
						GetPlayer()->SetTarget(NULL);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyShipStatus:
						if (!GetPlayer()->DockingInProgress())
							GetModel().ShowShipScreen();
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyTargetNextEnemy:
						GetPlayer()->SelectNextTarget(1);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyTargetPrevEnemy:
						GetPlayer()->SelectNextTarget(-1);
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyUseItem:
						if (!GetPlayer()->DockingInProgress()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							ShowUsePicker();
							}
						m_chKeyDown = iVirtKey;
						break;

					case CGameKeys::keyNextWeapon:
						if (GetPlayer() && !m_bNextWeaponKey)
							{
							Autopilot(false);
							GetPlayer()->SetFireMain(false);
							GetPlayer()->ReadyNextWeapon(1);
							UpdateWeaponStatus();
							m_chKeyDown = iVirtKey;
							m_bNextWeaponKey = true;
							}
						break;

					case CGameKeys::keyPrevWeapon:
						if (GetPlayer() && !m_bPrevWeaponKey)
							{
							Autopilot(false);
							GetPlayer()->SetFireMain(false);
							GetPlayer()->ReadyNextWeapon(-1);
							UpdateWeaponStatus();
							m_chKeyDown = iVirtKey;
							m_bPrevWeaponKey = true;
							}
						break;

					case CGameKeys::keyThrustForward:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetThrust(true);
							}
						break;

					case CGameKeys::keyRotateLeft:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetManeuver(IShipController::RotateLeft);
							}
						break;

					case CGameKeys::keyRotateRight:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetManeuver(IShipController::RotateRight);
							}
						break;

					case CGameKeys::keyStop:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetStopThrust(true);
							}
						break;

					case CGameKeys::keyFireWeapon:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetFireMain(true);
							}
						break;

					case CGameKeys::keyFireMissile:
						if (!GetPlayer()->GetShip()->IsOutOfFuel()
								&& !GetPlayer()->GetShip()->IsTimeStopped())
							{
							Autopilot(false);
							GetPlayer()->SetFireMissile(true);
							GetPlayer()->SetUIMessageEnabled(uimsgFireMissileHint, false);
							}
						break;

					case CGameKeys::keyNextMissile:
						if (!m_bNextMissileKey)
							{
							Autopilot(false);
							GetPlayer()->ReadyNextMissile(1);
							UpdateWeaponStatus();
							GetPlayer()->SetUIMessageEnabled(uimsgSwitchMissileHint, false);
							m_bNextMissileKey = true;
							}
						break;

					case CGameKeys::keyPrevMissile:
						if (!m_bPrevMissileKey)
							{
							Autopilot(false);
							GetPlayer()->ReadyNextMissile(-1);
							UpdateWeaponStatus();
							GetPlayer()->SetUIMessageEnabled(uimsgSwitchMissileHint, false);
							m_bPrevMissileKey = true;
							}
						break;

					case CGameKeys::keyShowHelp:
						g_pHI->HICommand(CONSTLIT("uiShowHelp"));
						break;

					case CGameKeys::keyShowGameStats:
						g_pHI->HICommand(CONSTLIT("uiShowGameStats"));
						break;

					case CGameKeys::keyVolumeDown:
						{
						int iVolume = GetSoundVolumeOption();
						if (--iVolume >= 0)
							{
							SetSoundVolumeOption(iVolume);
							DisplayMessage(strPatternSubst(CONSTLIT("Volume %d"), iVolume));
							}
						break;
						}

					case CGameKeys::keyVolumeUp:
						{
						int iVolume = GetSoundVolumeOption();
						if (++iVolume <= 10)
							{
							SetSoundVolumeOption(iVolume);
							DisplayMessage(strPatternSubst(CONSTLIT("Volume %d"), iVolume));
							}
						break;
						}

					case CGameKeys::keyShowConsole:
						{
						if (m_pTC->GetOptionBoolean(CGameSettings::debugMode)
								&& !g_pUniverse->IsRegistered())
							m_bDebugConsole = !m_bDebugConsole;
						break;
						}

					case CGameKeys::keyEnableAllDevices:
						if (!GetPlayer()->GetShip()->IsTimeStopped())
							{
							GetPlayer()->SetUIMessageEnabled(uimsgEnableDeviceHint, false);
							GetPlayer()->EnableAllDevices(true);
							}
						break;

					case CGameKeys::keyDisableAllDevices:
						if (!GetPlayer()->GetShip()->IsTimeStopped())
							{
							GetPlayer()->SetUIMessageEnabled(uimsgEnableDeviceHint, false);
							GetPlayer()->EnableAllDevices(false);
							}
						break;

					case CGameKeys::keyEnableAllDevicesToggle:
						if (!GetPlayer()->GetShip()->IsTimeStopped())
							{
							GetPlayer()->SetUIMessageEnabled(uimsgEnableDeviceHint, false);
							GetPlayer()->EnableAllDevices(!GetPlayer()->AreAllDevicesEnabled());
							}
						break;

					default:
						{
						if (iCommand >= CGameKeys::keyEnableDeviceToggle00 
								&& iCommand <= CGameKeys::keyEnableDeviceToggle31)
							{
							if (!GetPlayer()->GetShip()->IsTimeStopped())
								{
								int iDevice = (iCommand - CGameKeys::keyEnableDeviceToggle00);

								GetPlayer()->SetUIMessageEnabled(uimsgEnableDeviceHint, false);
								GetPlayer()->ToggleEnableDevice(iDevice);
								}
							}
						else if (iVirtKey == VK_ESCAPE)
							{
							if (m_bShowingMap)
								m_bShowingMap = false;
							else if (m_bAutopilot)
								Autopilot(false);
							else
								ShowGameMenu();
							}
						break;
						}
					}
				}
			break;
			}

		case gsIntro:
			OnKeyDownIntro(iVirtKey, dwKeyData);
			break;

		case gsProlog:
			m_bContinue = true;
			break;

		case gsDocked:
			{
			//	Deal with console

			if (m_bDebugConsole)
				{
				if (iVirtKey == VK_ESCAPE)
					m_bDebugConsole = false;
				else
					m_DebugConsole.OnKeyDown(iVirtKey, dwKeyData);
				}

			//	Other commands

			else
				{
				CGameKeys::Keys iCommand = m_pTC->GetKeyMap().GetGameCommand(iVirtKey);
				switch (iCommand)
					{
					case CGameKeys::keyShowConsole:
						{
						if (m_pTC->GetOptionBoolean(CGameSettings::debugMode)
								&& !g_pUniverse->IsRegistered())
							m_bDebugConsole = !m_bDebugConsole;
						break;
						}

					default:
						switch (iVirtKey)
							{
							case VK_F1:
								g_pHI->HICommand(CONSTLIT("uiShowHelp"));
								break;

							case VK_F2:
								g_pHI->HICommand(CONSTLIT("uiShowGameStats"));
								break;

							default:
								{
								//	Let the dock screen handle it.

								m_CurrentDock.HandleKeyDown(iVirtKey);
								}
							}
						break;
					}
				}

			break;
			}
		}

	return 0;
	}