247 lines
8.8 KiB
C
247 lines
8.8 KiB
C
/*
|
|
* This program source code file is part of SystemK, a library in the KTag project.
|
|
*
|
|
* 🛡️ <https://ktag.clubk.club> 🃞
|
|
*
|
|
* Copyright © 2016-2025 Joseph P. Kearney and the KTag developers.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* There should be a copy of the GNU Affero General Public License in the LICENSE
|
|
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "SystemK.h"
|
|
|
|
static TimerHandle_t BLEStatusTimer = NULL;
|
|
static StaticTimer_t xBLEStatusTimerBuffer;
|
|
static TickType_t xBLEStatusTimerPeriod = 3000 / portTICK_PERIOD_MS;
|
|
|
|
static TimerHandle_t GameDurationTimer = NULL;
|
|
static StaticTimer_t xGameDurationTimerBuffer;
|
|
|
|
static BLENearby_T BLE_RXd_data;
|
|
static const uint_fast16_t MAX_TIME_TO_LIVE_in_ms = 1000;
|
|
static const int_fast8_t STATUS_RSSI_THRESHOLD = -80;
|
|
|
|
static const int_fast8_t TAG_RSSI_THRESHOLD = -75;
|
|
|
|
static void BLEStatusTimerCallback(TimerHandle_t xTimer)
|
|
{
|
|
StateMachineContext_T* context = GetContext();
|
|
BLE_UpdateStatusPacket(context->States.Current_State);
|
|
}
|
|
|
|
static void GameDurationTimerCallback(TimerHandle_t xTimer)
|
|
{
|
|
KEvent_T game_over_event = { .ID = KEVENT_GAME_OVER, .Data = (void *)0x00 };
|
|
Post_KEvent(&game_over_event);
|
|
}
|
|
|
|
//! Sets up the Playing state.
|
|
/*!
|
|
* \param context Context in which this state is being run.
|
|
*/
|
|
void Playing_Entry(StateMachineContext_T *context)
|
|
{
|
|
LOG("Entering the Playing state.");
|
|
static const char *KLOG_TAG = "STATE_PLAYING Entry";
|
|
|
|
for (uint_fast8_t slot = 0; slot < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; slot++)
|
|
{
|
|
BLE_RXd_data.neighbors[slot].TimeToLive_in_ms = 0;
|
|
}
|
|
|
|
BLE_UpdateStatusPacket(context->States.Current_State);
|
|
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
|
{
|
|
KLOG_ERROR(KLOG_TAG, "Couldn't start BLE!");
|
|
}
|
|
|
|
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TEAM_COLORS, .Data = (void *)DISPLAY_STYLE_HEARTBEAT, .Prominence = NEOPIXELS_BACKGROUND};
|
|
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
|
|
|
if (BLEStatusTimer == NULL)
|
|
{
|
|
BLEStatusTimer = xTimerCreateStatic(
|
|
"BLEStatus",
|
|
xBLEStatusTimerPeriod,
|
|
pdTRUE,
|
|
(void *)0,
|
|
BLEStatusTimerCallback,
|
|
&xBLEStatusTimerBuffer);
|
|
}
|
|
if (BLEStatusTimer != NULL)
|
|
{
|
|
if (xTimerStart(BLEStatusTimer, 0) != pdPASS)
|
|
{
|
|
KLOG_ERROR(KLOG_TAG, "Couldn't start the BLEStatusTimer!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KLOG_ERROR(KLOG_TAG, "Couldn't create the BLEStatusTimer!");
|
|
}
|
|
|
|
// The timer is only needed for timed games.
|
|
if ((Get_Time_Remaining_in_Game_in_ms() != UINT32_MAX) &&
|
|
(Get_Time_Remaining_in_Game_in_ms() != 0))
|
|
{
|
|
if (GameDurationTimer == NULL)
|
|
{
|
|
static TickType_t xGameDurationTimerPeriod;
|
|
xGameDurationTimerPeriod = Get_Time_Remaining_in_Game_in_ms() / portTICK_PERIOD_MS;
|
|
|
|
GameDurationTimer = xTimerCreateStatic(
|
|
"GameDuration",
|
|
xGameDurationTimerPeriod,
|
|
pdFALSE,
|
|
(void *)0,
|
|
GameDurationTimerCallback,
|
|
&xGameDurationTimerBuffer);
|
|
}
|
|
if (GameDurationTimer != NULL)
|
|
{
|
|
if (xTimerStart(GameDurationTimer, 0) != pdPASS)
|
|
{
|
|
KLOG_ERROR(KLOG_TAG, "Couldn't start the GameDurationTimer!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KLOG_ERROR(KLOG_TAG, "Couldn't create the GameDurationTimer!");
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Cleans up the Playing state.
|
|
/*!
|
|
* \param context Context in which this state is being run.
|
|
*/
|
|
void Playing_Exit(StateMachineContext_T *context)
|
|
{
|
|
LOG("Exiting the Playing state.");
|
|
xTimerStop(BLEStatusTimer, portMAX_DELAY);
|
|
BLE_UpdateStatusPacket(context->States.Next_State);
|
|
}
|
|
|
|
void HandleBLEStatusPacket(const BLE_StatusPacket_T *const packet)
|
|
{
|
|
bool found = false;
|
|
uint8_t first_empty_slot = 0xFF;
|
|
|
|
// Look through the neighbors, and see if this is one we already know about.
|
|
for (uint_fast8_t slot = 0; slot < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; slot++)
|
|
{
|
|
if (BLE_RXd_data.neighbors[slot].TimeToLive_in_ms > 0)
|
|
{
|
|
if (memcmp(packet->BD_ADDR, BLE_RXd_data.neighbors[slot].BD_ADDR, 6) == 0)
|
|
{
|
|
if ((packet->health > 0) && (packet->RSSI > STATUS_RSSI_THRESHOLD))
|
|
{
|
|
BLE_RXd_data.neighbors[slot].RSSI = packet->RSSI;
|
|
BLE_RXd_data.neighbors[slot].Color = packet->primary_color;
|
|
BLE_RXd_data.neighbors[slot].TimeToLive_in_ms = MAX_TIME_TO_LIVE_in_ms;
|
|
}
|
|
else
|
|
{
|
|
// Clear the slot.
|
|
BLE_RXd_data.neighbors[slot].TimeToLive_in_ms = 0;
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
else if (first_empty_slot == 0xFF)
|
|
{
|
|
first_empty_slot = slot;
|
|
}
|
|
}
|
|
|
|
if ((found == false) &&
|
|
(first_empty_slot < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL) &&
|
|
(packet->health > 0) &&
|
|
(packet->RSSI > STATUS_RSSI_THRESHOLD))
|
|
{
|
|
// Welcome the new neighbor.
|
|
memcpy(BLE_RXd_data.neighbors[first_empty_slot].BD_ADDR, packet->BD_ADDR, 6);
|
|
BLE_RXd_data.neighbors[first_empty_slot].RSSI = packet->RSSI;
|
|
BLE_RXd_data.neighbors[first_empty_slot].Color = packet->primary_color;
|
|
BLE_RXd_data.neighbors[first_empty_slot].TimeToLive_in_ms = MAX_TIME_TO_LIVE_in_ms;
|
|
}
|
|
|
|
// NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_BLE_NEARBY, .Data = (void *)&BLE_RXd_data, .Prominence = NEOPIXELS_BACKGROUND};
|
|
// xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
|
|
|
BLE_FreePacketBuffer((BLE_GenericPacketType_T *)packet);
|
|
}
|
|
|
|
void HandleBLETagPacket(const BLE_TagPacket_T *const packet)
|
|
{
|
|
static int16_t Health_In_Percent;
|
|
static color_t ReceivedTagColor;
|
|
|
|
// Use this code for tuning RSSI.
|
|
//{
|
|
// static uint8_t normalized_rssi = 0;
|
|
// normalized_rssi = (uint8_t)abs(packet->RSSI);
|
|
//
|
|
// AudioAction_T rssi_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&normalized_rssi};
|
|
// Perform_Audio_Action(&rssi_action);
|
|
//}
|
|
|
|
// Throw away quiet packets without processing them.
|
|
if (packet->RSSI > TAG_RSSI_THRESHOLD)
|
|
{
|
|
if (BLE_IsBLEPacketForMe(packet->target_BD_ADDR))
|
|
{
|
|
if (BLE_IsPacketNew(packet->BD_ADDR, BLE_PACKET_TYPE_TAG, packet->event_number))
|
|
{
|
|
Health_In_Percent = (int16_t)Get_Health();
|
|
|
|
if (packet->damage > 0)
|
|
{
|
|
if (Health_In_Percent > 0)
|
|
{
|
|
Reduce_Health(packet->damage);
|
|
|
|
ReceivedTagColor = packet->color;
|
|
|
|
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TAG_RECEIVED, .Data = (void *)0x00};
|
|
Perform_Audio_Action(&audio_action);
|
|
|
|
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TAG_RECEIVED, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&ReceivedTagColor};
|
|
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
|
}
|
|
}
|
|
else if (packet->damage < 0)
|
|
{
|
|
Health_In_Percent -= packet->damage;
|
|
if (Health_In_Percent > Get_Max_Health())
|
|
{
|
|
Health_In_Percent = Get_Max_Health();
|
|
}
|
|
|
|
Set_Health(Health_In_Percent);
|
|
AudioAction_T audio_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Health_In_Percent};
|
|
Perform_Audio_Action(&audio_action);
|
|
AudioAction_T audio_action_two = {.ID = AUDIO_PLAY_HEALTH_REMAINING, .Data = (void *)0x00};
|
|
Perform_Audio_Action(&audio_action_two);
|
|
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_HEALTH_REPORT, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&Health_In_Percent};
|
|
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BLE_FreePacketBuffer((BLE_GenericPacketType_T *)packet);
|
|
}
|