void GalleryScroll::setProgress(float value) {
	if (value * _progress < 0.0f) {
		value = 0.0f;
	}

	float p = _primaryId + value;

	if (value <= -1.0f) {
		// move back
		_progress = 0.0f;

		if (!_secondary) {
			_secondaryId = _primaryId - 1;
			_secondary = construct<GalleryImage>(_images.at(_secondaryId), _imageCallback);
			_secondary->setAnchorPoint(Vec2(0.5f, 0.5f));
			_secondary->setPosition(Vec2(_contentSize.width/2.0f, _contentSize.height/2.0f));
			_secondary->setContentSize(_contentSize);
			addChild(_secondary, int(_images.size() - _secondaryId));
		}

		_primary->removeFromParent();
		_primaryId = _secondaryId;
		_primary = _secondary;
		_secondary = nullptr;

		_primary->setPositionX(_contentSize.width/2.0f + - _progress * _contentSize.width);
		_primary->setOpacity(255);
		_primary->setScale(1.0f);
		stopActionByTag("GalleryScrollProgress"_tag);
	} else if (value >= 1.0f) {
		//move forvard
		_progress = 0.0f;

		if (!_secondary) {
			_secondaryId = _primaryId + 1;
			if (_secondaryId < _images.size()) {
				_secondary = construct<GalleryImage>(_images.at(_secondaryId), _imageCallback);
				_secondary->setAnchorPoint(Vec2(0.5f, 0.5f));
				_secondary->setPosition(Vec2(_contentSize.width/2.0f, _contentSize.height/2.0f));
				_secondary->setContentSize(_contentSize);
				addChild(_secondary, int(_images.size() - _secondaryId));
			}
		}

		_primary->removeFromParent();
		_primaryId = _secondaryId;
		_primary = _secondary;
		_secondary = nullptr;

		_primary->setPositionX(_contentSize.width/2.0f + - _progress * _contentSize.width);
		_primary->setOpacity(255);
		_primary->setScale(1.0f);
		stopActionByTag("GalleryScrollProgress"_tag);
	} else if (value == 0.0f) {
		_progress = value;
		if (_secondary) {
			_secondary->removeFromParent();
			_secondary = nullptr;
		}

		_primary->setPositionX(_contentSize.width/2.0f + - _progress * _contentSize.width);
		_primary->setOpacity(255);
		_primary->setScale(1.0f);
	} else {
		if (_progress == 0) {
			if (_secondary) {
				_secondary->removeFromParent();
				_secondary = nullptr;
			}

			if (value > 0) {
				_secondaryId = _primaryId + 1;
			} else {
				_secondaryId = _primaryId - 1;
			}

			if (_secondaryId < _images.size()) {
				_secondary = construct<GalleryImage>(_images.at(_secondaryId), _imageCallback);
				_secondary->setAnchorPoint(Vec2(0.5f, 0.5f));
				_secondary->setPosition(Vec2(_contentSize.width/2.0f, _contentSize.height/2.0f));
				_secondary->setContentSize(_contentSize);
				addChild(_secondary, int(_images.size() - _secondaryId));
			}
		}
		_progress = value;

		if (_progress > 0) {
			_primary->setPositionX(_contentSize.width/2.0f + - _progress * _contentSize.width);

			if (_secondary) {
				_secondary->setPositionX(_contentSize.width/2.0);
				_secondary->setOpacity(progress(0, 255, _progress));
				_secondary->setScale(progress(0.75f, 1.0f, _progress));
			}
		} else {
			if (_secondary) {
				_secondary->setPositionX(_contentSize.width/2.0f + ((-1.0 - _progress) * _contentSize.width));
			}

			_primary->setPositionX(_contentSize.width/2.0);
			_primary->setOpacity(progress(0, 255, 1.0f + _progress));
			_primary->setScale(progress(0.75f, 1.0f, 1.0f + _progress));
		}
	}

	if (_positionCallback) {
		_positionCallback(p);
	}
}
void WildThumperCom::_parseValidMessage() {
	byte teamNumber = _rxMessageBuffer[TEAM_NUMBER_BYTE];
//	if (teamNumber != _teamNumber
//		&& !(_rxMessageBuffer[COMMAND_BYTE] == COMMAND_TEAM_NUMBER_REQUEST ||
//			 _rxMessageBuffer[COMMAND_BYTE] == COMMAND_TEAM_NUMBER_REPLY ||
//			 _rxMessageBuffer[COMMAND_BYTE] == COMMAND_TEAM_NUMBER_CHANGE_REQUEST)) {
//		// Silently do nothing if it's not our team.
//		return;
//	}
	switch (_rxMessageBuffer[COMMAND_BYTE]) {
	case COMMAND_WHEEL_SPEED:
		if (_wheelSpeedCallback != NULL) {
			_wheelSpeedCallback(_rxMessageBuffer[WHEEL_SPEED_LEFT_MODE],
					_rxMessageBuffer[WHEEL_SPEED_RIGHT_MODE],
					_rxMessageBuffer[WHEEL_SPEED_LEFT_DUTY_CYCLE],
					_rxMessageBuffer[WHEEL_SPEED_RIGHT_DUTY_CYCLE]);
		}
		break;
	case COMMAND_SET_ARM_POSITION:
		if (_positionCallback != NULL) {
			int joint1Angle = _rxMessageBuffer[SET_ARM_POSITION_JOINT_1_MSB];
			joint1Angle = joint1Angle << 8;
			joint1Angle += _rxMessageBuffer[SET_ARM_POSITION_JOINT_1_LSB];
			int joint2Angle = _rxMessageBuffer[SET_ARM_POSITION_JOINT_2_MSB];
			joint2Angle = joint2Angle << 8;
			joint2Angle += _rxMessageBuffer[SET_ARM_POSITION_JOINT_2_LSB];
			int joint3Angle = _rxMessageBuffer[SET_ARM_POSITION_JOINT_3_MSB];
			joint3Angle = joint3Angle << 8;
			joint3Angle += _rxMessageBuffer[SET_ARM_POSITION_JOINT_3_LSB];
			int joint4Angle = _rxMessageBuffer[SET_ARM_POSITION_JOINT_4_MSB];
			joint4Angle = joint4Angle << 8;
			joint4Angle += _rxMessageBuffer[SET_ARM_POSITION_JOINT_4_LSB];
			int joint5Angle = _rxMessageBuffer[SET_ARM_POSITION_JOINT_5_MSB];
			joint5Angle = joint5Angle << 8;
			joint5Angle += _rxMessageBuffer[SET_ARM_POSITION_JOINT_5_LSB];
			_positionCallback(joint1Angle, joint2Angle, joint3Angle,
					joint4Angle, joint5Angle);
		}
		break;
	case COMMAND_SET_JOINT_ANGLE:
		if (_jointAngleCallback != NULL) {
			int jointAngle = _rxMessageBuffer[SET_JOINT_ANGLE_ANGLE_MSB];
			jointAngle = jointAngle << 8;
			jointAngle += _rxMessageBuffer[SET_JOINT_ANGLE_ANGLE_LSB];
			_jointAngleCallback(_rxMessageBuffer[SET_JOINT_ANGLE_JOINT_NUMBER],
					jointAngle);
		}
		break;
	case COMMAND_SET_GRIPPER_DISTANCE:
		if (_gripperCallback != NULL) {
			int gripperDistance = _rxMessageBuffer[SET_GRIPPER_DISTANCE_MSB];
			gripperDistance = gripperDistance << 8;
			gripperDistance += _rxMessageBuffer[SET_GRIPPER_DISTANCE_LSB];
			_gripperCallback(gripperDistance);
		}
		break;
	case COMMAND_BATTERY_VOLTAGE_REQUEST:
		if (_batteryVoltageRequestCallback != NULL) {
			_batteryVoltageRequestCallback();
		}
		break;
	case COMMAND_BATTERY_VOLTAGE_REPLY:
		if (_batteryVoltageReplyCallback != NULL) {
			int batteryVoltageMillivolts =
					_rxMessageBuffer[BATTERY_VOLTAGE_REPLY_MSB];
			batteryVoltageMillivolts = batteryVoltageMillivolts << 8;
			batteryVoltageMillivolts +=
					_rxMessageBuffer[BATTERY_VOLTAGE_REPLY_LSB];
			_batteryVoltageReplyCallback(batteryVoltageMillivolts);
		}
		break;
	case COMMAND_WHEEL_CURRENT_REQUEST:
		if (_wheelCurrentRequestCallback != NULL) {
			_wheelCurrentRequestCallback();
		}
		break;
	case COMMAND_WHEEL_CURRENT_REPLY:
		if (_wheelCurrentReplyCallback != NULL) {
			int leftWheelMotorsMilliamps =
					_rxMessageBuffer[WHEEL_CURRENT_REPLY_LEFT_MSB];
			leftWheelMotorsMilliamps = leftWheelMotorsMilliamps << 8;
			leftWheelMotorsMilliamps += 
					_rxMessageBuffer[WHEEL_CURRENT_REPLY_LEFT_LSB];
			int rightWheelMotorsMilliamps =
					_rxMessageBuffer[WHEEL_CURRENT_REPLY_RIGHT_MSB];
			rightWheelMotorsMilliamps = rightWheelMotorsMilliamps << 8;
			rightWheelMotorsMilliamps += 
					_rxMessageBuffer[WHEEL_CURRENT_REPLY_RIGHT_LSB];
			_wheelCurrentReplyCallback(leftWheelMotorsMilliamps, rightWheelMotorsMilliamps);
		}
		break;
	case COMMAND_TEAM_NUMBER_REQUEST:
		if (_teamNumberRequestCallback != NULL) {
			_teamNumberRequestCallback();
		}
		break;
	case COMMAND_TEAM_NUMBER_REPLY:
		if (_teamNumberReplyCallback != NULL) {
			_teamNumberReplyCallback(_rxMessageBuffer[TEAM_NUMBER_REPLY_TEAM_NUMBER]);
		}
		break;
	case COMMAND_TEAM_NUMBER_CHANGE_REQUEST:
		if (_teamNumberChangeRequestCallback != NULL) {
			_teamNumberChangeRequestCallback(_rxMessageBuffer[TEAM_NUMBER_CHANGE_REQUEST_NEW_TEAM_NUMBER]);
		}
		break;
	case COMMAND_ATTACH_SELECTED_SERVOS:
		if (_attachSelectedServosCallback != NULL) {
			_attachSelectedServosCallback(_rxMessageBuffer[ATTACH_SELECTED_SERVOS_ENABLE_BYTE]);
		}
		break;
	default:
		// Silently do nothing with unknown commands
		break;
	}
}