Initial public release of SystemK.
This commit is contained in:
parent
387f57cdda
commit
6f51f5b006
129 changed files with 11654 additions and 2 deletions
239
States/Playing/State_Playing.c
Normal file
239
States/Playing/State_Playing.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
BLE_UpdateStatusPacket();
|
||||
}
|
||||
|
||||
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();
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
if (packet->damage > 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 = (uint8_t)Get_Health();
|
||||
Health_In_Percent -= packet->damage;
|
||||
if (Health_In_Percent > MAX_HEALTH)
|
||||
{
|
||||
Health_In_Percent = MAX_HEALTH;
|
||||
}
|
||||
|
||||
Health_In_Percent = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
States/Playing/State_Playing.h
Normal file
47
States/Playing/State_Playing.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_PLAYING_H
|
||||
#define STATE_PLAYING_H
|
||||
|
||||
|
||||
inline bool STATE_IsPlayingSubstate(StateID_T state)
|
||||
{
|
||||
bool is_substate = false;
|
||||
|
||||
if ((state == STATE_PLAYING__INTERACTING) ||
|
||||
(state == STATE_PLAYING__TAGGED_OUT))
|
||||
{
|
||||
is_substate = true;
|
||||
}
|
||||
return is_substate;
|
||||
}
|
||||
|
||||
void Playing_Entry(StateMachineContext_T * context);
|
||||
void Playing_Exit(StateMachineContext_T * context);
|
||||
void HandleBLEStatusPacket(const BLE_StatusPacket_T * const packet);
|
||||
void HandleBLETagPacket(const BLE_TagPacket_T * const packet);
|
||||
|
||||
/* Include Files */
|
||||
#include "States/Playing/State_Playing__Interacting.h"
|
||||
#include "States/Playing/State_Playing__Tagged_Out.h"
|
||||
|
||||
#endif // STATE_PLAYING_H
|
425
States/Playing/State_Playing__Interacting.c
Normal file
425
States/Playing/State_Playing__Interacting.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LOG_INTERACTING_SUBSTATE
|
||||
|
||||
static void Playing__Interacting_Entry(StateMachineContext_T * context);
|
||||
static void Playing__Interacting_Do(StateMachineContext_T * context);
|
||||
static void Playing__Interacting_Exit(StateMachineContext_T * context);
|
||||
static uint8_t Health_in_percent;
|
||||
|
||||
//! Activities for the **Interacting** substate of the **Playing** state.
|
||||
const StateActivity_T STATE_PLAYING__INTERACTING_Activities =
|
||||
{
|
||||
.Entry = Playing__Interacting_Entry,
|
||||
.Do = Playing__Interacting_Do,
|
||||
.Exit = Playing__Interacting_Exit
|
||||
};
|
||||
|
||||
static TickType_t Time_Of_Last_Shot = 0;
|
||||
static const uint32_t FIXED_SHOT_HOLDOFF_in_ms = 3000;
|
||||
static const uint32_t RANDOM_SHOT_HOLDOFF_in_ms = 1000;
|
||||
static TickType_t Shot_Holdoff_Time;
|
||||
static color_t ReceivedTagColor = 0x00000000;
|
||||
static const uint32_t PURPLE_TAG_INVINCIBILITY_WINDOW_in_ms = 1000;
|
||||
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
static const char *KLOG_TAG = "STATE_PLAYING__INTERACTING";
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
//! Sets up the Interacting substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Interacting_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
if (STATE_IsPlayingSubstate(context->States.Previous_State) == false)
|
||||
{
|
||||
Playing_Entry(context);
|
||||
srand(xTaskGetTickCount());
|
||||
}
|
||||
LOG("Entering the Interacting substate of the Playing state.");
|
||||
}
|
||||
|
||||
//! Executes the Interacting substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Interacting_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_TRIGGER_SWITCH_PRESSED:
|
||||
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
KLOG_INFO(KLOG_TAG, "Trigger pressed.");
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
if ((xTaskGetTickCount() - Time_Of_Last_Shot) > Shot_Holdoff_Time)
|
||||
{
|
||||
if (Send_Tag() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KEvent_T misfire_event = { .ID = KEVENT_MISFIRE, .Data = (void *)0x00 };
|
||||
Post_KEvent(&misfire_event);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KEvent_T misfire_event = { .ID = KEVENT_MISFIRE, .Data = (void *)0x00 };
|
||||
Post_KEvent(&misfire_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_TRIGGER_SWITCH_RELEASED:
|
||||
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
KLOG_INFO(KLOG_TAG, "Trigger released.");
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
{
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
|
||||
if (duration_of_press_in_ms > 10000)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_ELECTRONIC_DANCE_MUSIC, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TEST_PATTERN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
|
||||
// Was it a "long" press?
|
||||
else if (duration_of_press_in_ms > 3000)
|
||||
{
|
||||
Health_in_percent = (uint8_t)Get_Health();
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_UP_SWITCH_LONG_PRESSED:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_ELECTRONIC_DANCE_MUSIC, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TEST_PATTERN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_UP_SWITCH_RELEASED:
|
||||
{
|
||||
//AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
//xQueueSend(xQueueAudio, &audio_action, 0);
|
||||
//NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
//xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_DOWN_SWITCH_LONG_PRESSED:
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_FLASHLIGHT_ON, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_DOWN_SWITCH_RELEASED:
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_FORWARD_SWITCH_PRESSED:
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_FLAMETHROWER, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_FORWARD_SWITCH_RELEASED:
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_BACKWARD_SWITCH_PRESSED:
|
||||
{
|
||||
Health_in_percent = (uint8_t)Get_Health();
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_BACKWARD_SWITCH_RELEASED:
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_TAG_SENT:
|
||||
{
|
||||
// "Flip ya'? Double or nuthin'!"
|
||||
uint32_t holdoff_in_ms = (rand() % 2) * RANDOM_SHOT_HOLDOFF_in_ms;
|
||||
holdoff_in_ms += FIXED_SHOT_HOLDOFF_in_ms;
|
||||
Shot_Holdoff_Time = (holdoff_in_ms / portTICK_PERIOD_MS);
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
KLOG_INFO(KLOG_TAG, "Tag sent. Holdoff: %lu ms", holdoff_in_ms);
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
Time_Of_Last_Shot = xTaskGetTickCount();
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_SHOT_FIRED, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_PLAY_SHOT_FIRED, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
Increment_Shots_Fired();
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_TAG_RECEIVED:
|
||||
{
|
||||
uint8_t protocol = ((DecodedPacket_T*)Event.Data)->Tag.protocol;
|
||||
uint16_t team_ID = ((DecodedPacket_T*)Event.Data)->Tag.team_ID;
|
||||
uint16_t player_ID = ((DecodedPacket_T*)Event.Data)->Tag.player_ID;
|
||||
uint16_t damage = ((DecodedPacket_T*)Event.Data)->Tag.damage;
|
||||
ReceivedTagColor = ((DecodedPacket_T*)Event.Data)->Tag.color;
|
||||
TagSensorLocation_T receiver = ((DecodedPacket_T*)Event.Data)->Tag.receiver;
|
||||
TeamID_t rxd_common_team_ID = Resolve_Common_Team_ID(team_ID);
|
||||
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
switch (receiver)
|
||||
{
|
||||
default:
|
||||
KLOG_INFO(KLOG_TAG, "Tag from unknown sensor: Team: %u Player: %u Damage: %u", team_ID, player_ID, damage);
|
||||
break;
|
||||
|
||||
case TAG_SENSOR_FORWARD:
|
||||
KLOG_INFO(KLOG_TAG, "Tag from forward sensor: Team: %u Player: %u Damage: %u", team_ID, player_ID, damage);
|
||||
break;
|
||||
|
||||
case TAG_SENSOR_LEFT:
|
||||
KLOG_INFO(KLOG_TAG, "Tag from left sensor: Team: %u Player: %u Damage: %u", team_ID, player_ID, damage);
|
||||
break;
|
||||
|
||||
case TAG_SENSOR_RIGHT:
|
||||
KLOG_INFO(KLOG_TAG, "Tag from right sensor: Team: %u Player: %u Damage: %u", team_ID, player_ID, damage);
|
||||
break;
|
||||
|
||||
case TAG_SENSOR_REMOTE:
|
||||
KLOG_INFO(KLOG_TAG, "Tag from remote sensor: Team: %u Player: %u Damage: %u", team_ID, player_ID, damage);
|
||||
break;
|
||||
|
||||
}
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
if (Still_Playing() == true)
|
||||
{
|
||||
uint8_t my_team_ID;
|
||||
uint8_t my_player_ID;
|
||||
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &my_team_ID);
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &my_player_ID);
|
||||
|
||||
if (Team_Can_Tag_Me(rxd_common_team_ID) == true)
|
||||
{
|
||||
// Checks to make sure the tagger is not seeing its own tag.
|
||||
bool tag_might_be_mine = false;
|
||||
|
||||
if (Resolve_Common_Team_ID(team_ID) == TEAM_PURPLE)
|
||||
{
|
||||
if ((xTaskGetTickCount() - Time_Of_Last_Shot) < (PURPLE_TAG_INVINCIBILITY_WINDOW_in_ms / portTICK_PERIOD_MS))
|
||||
{
|
||||
// Special handling for receiving a Purple Tag while sending a Purple Tag using the DBQ Protocol.
|
||||
if (protocol == DUBUQUE_PROTOCOL)
|
||||
{
|
||||
if (damage <= 20)
|
||||
{
|
||||
tag_might_be_mine = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the accumulated damage is more than 20, it means I've received my own tag AND another tag.
|
||||
// Set the damage to the value of a single tag.
|
||||
damage = 10;
|
||||
}
|
||||
}
|
||||
// For protocols other than DBQ, we cannot distinguish my tag from yours. The solution is to
|
||||
// allow a period of invincibility from Purple tags after sending ours.
|
||||
// Note that this means for most protocols, simultaneous Purple vs. Purple results in no tags.
|
||||
else
|
||||
{
|
||||
tag_might_be_mine = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the tag is clearly not mine, process it.
|
||||
if (tag_might_be_mine == false)
|
||||
{
|
||||
Reduce_Health(damage);
|
||||
|
||||
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);
|
||||
|
||||
Increment_Tags_Received();
|
||||
}
|
||||
}
|
||||
else if (player_ID != my_player_ID)
|
||||
{
|
||||
// Friendly fire!
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_FRIENDLY_FIRE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeDecodedPacketBuffer(Event.Data);
|
||||
break;
|
||||
|
||||
case KEVENT_TAGGED_OUT:
|
||||
{
|
||||
context->States.Next_State = STATE_PLAYING__TAGGED_OUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MISFIRE:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_MISFIRE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_NEAR_MISS:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_NEAR_MISS, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_COMMAND_RECEIVED:
|
||||
FreeDecodedPacketBuffer(Event.Data);
|
||||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
//KLOG_INFO(KLOG_TAG, "KEVENT_BLE_PACKET_RECEIVED from %02X:%02X:%02X:%02X:%02X:%02X",
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[5],
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[4],
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[3],
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[2],
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[1],
|
||||
// ((BLE_Packet_T *)Event.Data)->Generic.BD_ADDR[0]
|
||||
//);
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_STATUS)
|
||||
{
|
||||
HandleBLEStatusPacket((BLE_StatusPacket_T *)Event.Data);
|
||||
}
|
||||
else if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_TAG)
|
||||
{
|
||||
HandleBLETagPacket((BLE_TagPacket_T *)Event.Data);
|
||||
}
|
||||
|
||||
BLE_FreePacketBuffer(Event.Data);
|
||||
break;
|
||||
|
||||
case KEVENT_ACCESSORY_SWITCH_PRESSED:
|
||||
{
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
uint32_t time_since_last_press_in_ms = (uint32_t)Event.Data;
|
||||
KLOG_INFO(KLOG_TAG, "Accessory pressed after %lu ms.", time_since_last_press_in_ms);
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
|
||||
if (Use_Bomb_If_Available() == true)
|
||||
{
|
||||
// BOOM!
|
||||
BLE_UpdateTagPacket(100, COLOR_YELLOW, (uint8_t[BD_ADDR_SIZE]){0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOMB, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BONK, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_ACCESSORY_SWITCH_RELEASED:
|
||||
{
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
KLOG_INFO(KLOG_TAG, "Accessory released after %lu ms.", duration_of_press_in_ms);
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_GAME_OVER:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_OVER, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
Transition_For_Event(context, STATE_WRAPPING_UP, &Event);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Interacting substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Interacting_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
if (STATE_IsPlayingSubstate(context->States.Next_State) == false)
|
||||
{
|
||||
Playing_Exit(context);
|
||||
}
|
||||
}
|
32
States/Playing/State_Playing__Interacting.h
Normal file
32
States/Playing/State_Playing__Interacting.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_PLAYING__INTERACTING_H
|
||||
#define STATE_PLAYING__INTERACTING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_PLAYING__INTERACTING_Activities;
|
||||
|
||||
#endif // STATE_PLAYING__INTERACTING_H
|
146
States/Playing/State_Playing__Tagged_Out.c
Normal file
146
States/Playing/State_Playing__Tagged_Out.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Playing__Tagged_Out_Entry(StateMachineContext_T * context);
|
||||
static void Playing__Tagged_Out_Do(StateMachineContext_T * context);
|
||||
static void Playing__Tagged_Out_Exit(StateMachineContext_T * context);
|
||||
|
||||
//! Activities for the **Tagged Out** substate of the **Playing** state.
|
||||
const StateActivity_T STATE_PLAYING__TAGGED_OUT_Activities =
|
||||
{
|
||||
.Entry = Playing__Tagged_Out_Entry,
|
||||
.Do = Playing__Tagged_Out_Do,
|
||||
.Exit = Playing__Tagged_Out_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Tagged Out substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Tagged_Out_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
if (STATE_IsPlayingSubstate(context->States.Previous_State) == false)
|
||||
{
|
||||
Playing_Entry(context);
|
||||
}
|
||||
|
||||
LOG("Entering the Tagged Out substate of the Playing state.");
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TAGGED_OUT, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TAGGED_OUT, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
BLE_UpdateStatusPacket();
|
||||
|
||||
Increment_Times_Tagged_Out();
|
||||
}
|
||||
|
||||
//! Executes the Tagged Out substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Tagged_Out_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_TAG)
|
||||
{
|
||||
HandleBLETagPacket((BLE_TagPacket_T *)Event.Data);
|
||||
|
||||
if (Back_In() == true)
|
||||
{
|
||||
// TODO: Get this from settings.
|
||||
Set_Available_Bombs(1);
|
||||
Transition_For_Event(context, STATE_PLAYING__INTERACTING, &Event);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BLE_FreePacketBuffer(Event.Data);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case KEVENT_TRIGGER_SWITCH_RELEASED:
|
||||
{
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
|
||||
// Was it a "long" press?
|
||||
if (duration_of_press_in_ms > 5000)
|
||||
{
|
||||
Set_Health(MAX_HEALTH);
|
||||
// TODO: Get this from settings.
|
||||
Set_Available_Bombs(1);
|
||||
Transition_For_Event(context, STATE_PLAYING__INTERACTING, &Event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_GAME_OVER:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_OVER, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
Transition_For_Event(context, STATE_WRAPPING_UP, &Event);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
Set_Health(MAX_HEALTH);
|
||||
// TODO: Get this from settings.
|
||||
Set_Available_Bombs(1);
|
||||
Transition_For_Event(context, STATE_PLAYING__INTERACTING, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Tagged Out substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Playing__Tagged_Out_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
if (STATE_IsPlayingSubstate(context->States.Next_State) == false)
|
||||
{
|
||||
Playing_Exit(context);
|
||||
}
|
||||
}
|
32
States/Playing/State_Playing__Tagged_Out.h
Normal file
32
States/Playing/State_Playing__Tagged_Out.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_PLAYING__TAGGED_OUT_H
|
||||
#define STATE_PLAYING__TAGGED_OUT_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_PLAYING__TAGGED_OUT_Activities;
|
||||
|
||||
#endif // STATE_PLAYING__TAGGED_OUT_H
|
39
States/Starting_Game/State_Starting_Game.c
Normal file
39
States/Starting_Game/State_Starting_Game.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
void Starting_Game_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
|
||||
#ifdef LOG_STATE_MACHINE
|
||||
LOG("Entering the Starting Game state.");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
#endif // LOG_STATE_MACHINE
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_STARTING_THEME, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
|
||||
void Starting_Game_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
}
|
34
States/Starting_Game/State_Starting_Game.h
Normal file
34
States/Starting_Game/State_Starting_Game.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_STARTING_GAME_H
|
||||
#define STATE_STARTING_GAME_H
|
||||
|
||||
|
||||
extern void Starting_Game_Entry(StateMachineContext_T * context);
|
||||
extern void Starting_Game_Exit(StateMachineContext_T * context);
|
||||
|
||||
/* Include Files */
|
||||
#include "States/Starting_Game/State_Starting_Game__Counting_Down.h"
|
||||
#include "States/Starting_Game/State_Starting_Game__Instigating.h"
|
||||
#include "States/Starting_Game/State_Starting_Game__Responding.h"
|
||||
|
||||
#endif // STATE_STARTING_GAME_H
|
145
States/Starting_Game/State_Starting_Game__Counting_Down.c
Normal file
145
States/Starting_Game/State_Starting_Game__Counting_Down.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Starting_Game__Counting_Down_Entry(StateMachineContext_T * context);
|
||||
static void Starting_Game__Counting_Down_Do(StateMachineContext_T * context);
|
||||
static void Starting_Game__Counting_Down_Exit(StateMachineContext_T * context);
|
||||
|
||||
static uint_fast8_t N_Lights_Lit = 0;
|
||||
|
||||
//! Activities for the **Counting Down** substate of the **Starting Game** state.
|
||||
const StateActivity_T STATE_STARTING_GAME__COUNTING_DOWN_Activities =
|
||||
{
|
||||
.Entry = Starting_Game__Counting_Down_Entry,
|
||||
.Do = Starting_Game__Counting_Down_Do,
|
||||
.Exit = Starting_Game__Counting_Down_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Counting Down substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Counting_Down_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Counting Down substate of the Starting Game state.");
|
||||
|
||||
Prepare_Tag();
|
||||
Reset_Shots_Fired();
|
||||
Reset_Tags_Received();
|
||||
Reset_Times_Tagged_Out();
|
||||
// TODO: Get this from settings.
|
||||
Set_Available_Bombs(1);
|
||||
|
||||
N_Lights_Lit = 0;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
|
||||
//! Executes the Counting Down substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Counting_Down_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
default:
|
||||
// All events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((N_Lights_Lit == 0) && (GetTimeInState_in_ms(context) >= 1000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOOP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
else if ((N_Lights_Lit == 1) && (GetTimeInState_in_ms(context) >= 2000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOOP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
else if ((N_Lights_Lit == 2) && (GetTimeInState_in_ms(context) >= 3000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOOP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
else if ((N_Lights_Lit == 3) && (GetTimeInState_in_ms(context) >= 4000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOOP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
else if ((N_Lights_Lit == 4) && (GetTimeInState_in_ms(context) >= 5000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BOOP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
else if ((N_Lights_Lit == 5) && (GetTimeInState_in_ms(context) >= 6000))
|
||||
{
|
||||
N_Lights_Lit++;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_BEEP, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
Event.ID = KEVENT_AUTOMATIC_TRANSITION;
|
||||
Event.Data = NULL;
|
||||
Transition_For_Event(context, STATE_PLAYING__INTERACTING, &Event);
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Counting Down substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Counting_Down_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
// Last of all, exit the state containing this substate.
|
||||
Starting_Game_Exit(context);
|
||||
}
|
32
States/Starting_Game/State_Starting_Game__Counting_Down.h
Normal file
32
States/Starting_Game/State_Starting_Game__Counting_Down.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_STARTING_GAME__COUNTING_DOWN_H
|
||||
#define STATE_STARTING_GAME__COUNTING_DOWN_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_STARTING_GAME__COUNTING_DOWN_Activities;
|
||||
|
||||
#endif // STATE_STARTING_GAME__COUNTING_DOWN_H
|
137
States/Starting_Game/State_Starting_Game__Instigating.c
Normal file
137
States/Starting_Game/State_Starting_Game__Instigating.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Starting_Game__Instigating_Entry(StateMachineContext_T * context);
|
||||
static void Starting_Game__Instigating_Do(StateMachineContext_T * context);
|
||||
static void Starting_Game__Instigating_Exit(StateMachineContext_T * context);
|
||||
|
||||
static TickType_t Period_Start = 0;
|
||||
static uint_fast8_t N_Lights_Lit = 0;
|
||||
|
||||
static const char *KLOG_TAG = "STATE_STARTING_GAME__INSTIGATING";
|
||||
|
||||
//! Activities for the **Instigating** substate of the **Starting Game** state.
|
||||
const StateActivity_T STATE_STARTING_GAME__INSTIGATING_Activities =
|
||||
{
|
||||
.Entry = Starting_Game__Instigating_Entry,
|
||||
.Do = Starting_Game__Instigating_Do,
|
||||
.Exit = Starting_Game__Instigating_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Instigating substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Instigating_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
// First of all, enter the state containing this substate.
|
||||
Starting_Game_Entry(context);
|
||||
|
||||
LOG("Entering the Instigating substate of the Starting Game state.");
|
||||
|
||||
Set_Time_Remaining_in_Game(UINT32_MAX);
|
||||
uint32_t start_game_time_in_ms;
|
||||
(void) SETTINGS_get_uint32_t(SYSTEMK_SETTING_T_START_GAME_in_ms, &start_game_time_in_ms);
|
||||
Set_Time_Remaining_Until_Countdown(start_game_time_in_ms);
|
||||
BLE_UpdateInstigationPacket(Get_Time_Remaining_in_Game_in_ms(), Get_Time_Remaining_Until_Countdown_in_ms());
|
||||
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't start BLE!");
|
||||
}
|
||||
|
||||
Period_Start = xTaskGetTickCount();
|
||||
}
|
||||
|
||||
//! Executes the Instigating substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Instigating_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__COUNTING_DOWN, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for T_start_game before proceeding.
|
||||
uint32_t start_game_time_in_ms;
|
||||
(void) SETTINGS_get_uint32_t(SYSTEMK_SETTING_T_START_GAME_in_ms, &start_game_time_in_ms);
|
||||
if (GetTimeInState_in_ms(context) > start_game_time_in_ms)
|
||||
{
|
||||
static KEvent_T Event;
|
||||
Event.ID = KEVENT_AUTOMATIC_TRANSITION;
|
||||
Event.Data = NULL;
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__COUNTING_DOWN, &Event);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t period_time_in_ms = (xTaskGetTickCount() - Period_Start) * portTICK_PERIOD_MS;
|
||||
uint32_t time_remaining_until_countdown_in_ms = start_game_time_in_ms - GetTimeInState_in_ms(context);
|
||||
Set_Time_Remaining_Until_Countdown(time_remaining_until_countdown_in_ms);
|
||||
|
||||
if (period_time_in_ms > 150)
|
||||
{
|
||||
BLE_UpdateInstigationPacket(Get_Time_Remaining_in_Game_in_ms(), Get_Time_Remaining_Until_Countdown_in_ms());
|
||||
|
||||
// Toggle the lights.
|
||||
if (N_Lights_Lit == 0)
|
||||
{
|
||||
N_Lights_Lit = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
N_Lights_Lit = 0;
|
||||
}
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
// Start a new period.
|
||||
Period_Start = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Instigating substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Instigating_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
32
States/Starting_Game/State_Starting_Game__Instigating.h
Normal file
32
States/Starting_Game/State_Starting_Game__Instigating.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_STARTING_GAME__INSTIGATING_H
|
||||
#define STATE_STARTING_GAME__INSTIGATING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_STARTING_GAME__INSTIGATING_Activities;
|
||||
|
||||
#endif // STATE_STARTING_GAME__INSTIGATING_H
|
134
States/Starting_Game/State_Starting_Game__Responding.c
Normal file
134
States/Starting_Game/State_Starting_Game__Responding.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Starting_Game__Responding_Entry(StateMachineContext_T * context);
|
||||
static void Starting_Game__Responding_Do(StateMachineContext_T * context);
|
||||
static void Starting_Game__Responding_Exit(StateMachineContext_T * context);
|
||||
|
||||
static TickType_t Period_Start = 0;
|
||||
static uint_fast8_t N_Lights_Lit = 0;
|
||||
|
||||
static const char *KLOG_TAG = "STATE_STARTING_GAME__RESPONDING";
|
||||
|
||||
//! Activities for the **Responding** substate of the **Starting Game** state.
|
||||
const StateActivity_T STATE_STARTING_GAME__RESPONDING_Activities =
|
||||
{
|
||||
.Entry = Starting_Game__Responding_Entry,
|
||||
.Do = Starting_Game__Responding_Do,
|
||||
.Exit = Starting_Game__Responding_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Responding substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Responding_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
// First of all, enter the state containing this substate.
|
||||
Starting_Game_Entry(context);
|
||||
|
||||
LOG("Entering the Responding substate of the Starting Game state.");
|
||||
|
||||
if (context->Cause_Of_Transition->ID == KEVENT_BLE_PACKET_RECEIVED)
|
||||
{
|
||||
if (((BLE_Packet_T *)context->Cause_Of_Transition->Data)->Generic.type == BLE_PACKET_TYPE_INSTIGATE_GAME)
|
||||
{
|
||||
uint32_t game_length_in_ms = ((BLE_Packet_T *)context->Cause_Of_Transition->Data)->Instigation.game_length_in_ms;
|
||||
Set_Time_Remaining_in_Game(game_length_in_ms);
|
||||
|
||||
uint32_t time_remaining_until_countdown_in_ms = ((BLE_Packet_T *)context->Cause_Of_Transition->Data)->Instigation.time_remaining_until_countdown_in_ms;
|
||||
Set_Time_Remaining_Until_Countdown(time_remaining_until_countdown_in_ms);
|
||||
|
||||
#ifdef LOG_STATE_MACHINE
|
||||
KLOG_TRACE(KLOG_TAG, "Game length: %lu ms", game_length_in_ms);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
KLOG_TRACE(KLOG_TAG, "Time until countdown: %lu ms", time_remaining_until_countdown_in_ms);
|
||||
#endif // LOG_STATE_MACHINE
|
||||
}
|
||||
|
||||
BLE_FreePacketBuffer(context->Cause_Of_Transition->Data);
|
||||
}
|
||||
}
|
||||
|
||||
//! Executes the Responding substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Responding_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
default:
|
||||
// All events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for (T_start_game - T_instigation) before proceeding.
|
||||
if (GetTimeInState_in_ms(context) > Get_Time_Remaining_Until_Countdown_in_ms())
|
||||
{
|
||||
static KEvent_T Event;
|
||||
Event.ID = KEVENT_AUTOMATIC_TRANSITION;
|
||||
Event.Data = NULL;
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__COUNTING_DOWN, &Event);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t period_time_in_ms = (xTaskGetTickCount() - Period_Start) * portTICK_PERIOD_MS;
|
||||
|
||||
if (period_time_in_ms > 150)
|
||||
{
|
||||
// Toggle the lights.
|
||||
if (N_Lights_Lit == 0)
|
||||
{
|
||||
N_Lights_Lit = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
N_Lights_Lit = 0;
|
||||
}
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_COUNTDOWN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&N_Lights_Lit};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
// Start a new period.
|
||||
Period_Start = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Responding substate.
|
||||
/*!
|
||||
* \param context Context in which this substate is being run.
|
||||
*/
|
||||
static void Starting_Game__Responding_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
}
|
32
States/Starting_Game/State_Starting_Game__Responding.h
Normal file
32
States/Starting_Game/State_Starting_Game__Responding.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_STARTING_GAME__RESPONDING_H
|
||||
#define STATE_STARTING_GAME__RESPONDING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_STARTING_GAME__RESPONDING_Activities;
|
||||
|
||||
#endif // STATE_STARTING_GAME__RESPONDING_H
|
197
States/State_Configuring.c
Normal file
197
States/State_Configuring.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Configuring_Entry(StateMachineContext_T * context);
|
||||
static void Configuring_Do(StateMachineContext_T * context);
|
||||
static void Configuring_Exit(StateMachineContext_T * context);
|
||||
|
||||
#define MAX_MENU_DEPTH 10
|
||||
static MenuItem_T const * CurrentMenuItem;
|
||||
static uint8_t MenuItemHistoryIndex = 0;
|
||||
static MenuItem_T const * MenuItemHistory[MAX_MENU_DEPTH];
|
||||
|
||||
static const uint16_t LONG_PRESS_FOR_READY_in_ms = 5000;
|
||||
|
||||
static const char *KLOG_TAG = "STATE_CONFIGURING";
|
||||
|
||||
//! Activities for the **Configuring** state.
|
||||
const StateActivity_T STATE_CONFIGURING_Activities =
|
||||
{
|
||||
.Entry = Configuring_Entry,
|
||||
.Do = Configuring_Do,
|
||||
.Exit = Configuring_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Configuring state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Configuring_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Configuring state.");
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_MENU, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
|
||||
BLE_UpdateStatusPacket();
|
||||
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't start BLE!");
|
||||
}
|
||||
|
||||
// Reset the menu.
|
||||
CurrentMenuItem = RootMenu;
|
||||
MenuItemHistory[MenuItemHistoryIndex] = CurrentMenuItem;
|
||||
}
|
||||
|
||||
//! Executes the Configuring state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Configuring_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_MENU_ENTER:
|
||||
if (CurrentMenuItem->OnFocus != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnFocus(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_SELECT:
|
||||
{
|
||||
if (CurrentMenuItem->OnSelect != NULL)
|
||||
{
|
||||
MenuItem_T const * menuItem = CurrentMenuItem->OnSelect();
|
||||
|
||||
// Check to see if we have entered a submenu.
|
||||
if (menuItem != NULL)
|
||||
{
|
||||
// Save off the old menu item, so we can get back to it later.
|
||||
MenuItemHistoryIndex++;
|
||||
MenuItemHistory[MenuItemHistoryIndex] = menuItem;
|
||||
CurrentMenuItem = menuItem;
|
||||
|
||||
if (CurrentMenuItem->OnFocus != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnFocus(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_BACK:
|
||||
if (MenuItemHistoryIndex > 0)
|
||||
{
|
||||
// Go up a menu.
|
||||
MenuItemHistoryIndex--;
|
||||
CurrentMenuItem = MenuItemHistory[MenuItemHistoryIndex];
|
||||
if (CurrentMenuItem->OnFocus != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnFocus(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_UP:
|
||||
{
|
||||
if (CurrentMenuItem->OnIncrement != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnIncrement();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_DOWN:
|
||||
{
|
||||
if (CurrentMenuItem->OnDecrement != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnDecrement();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_EXIT:
|
||||
while (MenuItemHistoryIndex > 0)
|
||||
{
|
||||
// Go up a menu.
|
||||
MenuItemHistoryIndex--;
|
||||
CurrentMenuItem = MenuItemHistory[MenuItemHistoryIndex];
|
||||
if (CurrentMenuItem->OnFocus != NULL)
|
||||
{
|
||||
CurrentMenuItem->OnFocus(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_TRIGGER_SWITCH_RELEASED:
|
||||
{
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
|
||||
if (duration_of_press_in_ms > LONG_PRESS_FOR_READY_in_ms)
|
||||
{
|
||||
Transition_For_Event(context, STATE_READY, &Event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
Transition_For_Event(context, STATE_READY, &Event);
|
||||
break;
|
||||
|
||||
case KEVENT_REPROGRAM:
|
||||
Transition_For_Event(context, STATE_REPROGRAMMING, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Configuring state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Configuring_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
// Save any changes that were made to NVM.
|
||||
if (SETTINGS_Save() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't save the settings to NVM!");
|
||||
}
|
||||
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
32
States/State_Configuring.h
Normal file
32
States/State_Configuring.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_CONFIGURING_H
|
||||
#define STATE_CONFIGURING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_CONFIGURING_Activities;
|
||||
|
||||
#endif // STATE_CONFIGURING_H
|
126
States/State_Initializing.c
Normal file
126
States/State_Initializing.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Initializing_Entry(StateMachineContext_T * context);
|
||||
static void Initializing_Do(StateMachineContext_T * context);
|
||||
static void Initializing_Exit(StateMachineContext_T * context);
|
||||
|
||||
static const char *KLOG_TAG = "STATE_INITIALIZING";
|
||||
|
||||
static bool Startup_Sound_Complete = false;
|
||||
|
||||
//! Activities for the **Initializing** state.
|
||||
const StateActivity_T STATE_INITIALIZING_Activities =
|
||||
{
|
||||
.Entry = Initializing_Entry,
|
||||
.Do = Initializing_Do,
|
||||
.Exit = Initializing_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Initializing state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Initializing_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Initializing state.");
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TEST_PATTERN, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_STARTUP_SOUND, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
Startup_Sound_Complete = false;
|
||||
}
|
||||
|
||||
//! Executes the Initializing state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Initializing_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_TRIGGER_SWITCH_PRESSED:
|
||||
Transition_For_Event(context, STATE_REPROGRAMMING, &Event);
|
||||
break;
|
||||
|
||||
case KEVENT_AUDIO_COMPLETED:
|
||||
if (((AudioActionID_T)Event.Data) == AUDIO_PLAY_STARTUP_SOUND)
|
||||
{
|
||||
Startup_Sound_Complete = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the STARTUP_SOUND to complete, but at least three seconds.
|
||||
if ((xTaskGetTickCount() - context->Time_At_State_Entry_In_Ticks) > (3 * 1000 / portTICK_PERIOD_MS))
|
||||
{
|
||||
if (Startup_Sound_Complete == true)
|
||||
{
|
||||
Event.ID = KEVENT_AUTOMATIC_TRANSITION;
|
||||
Event.Data = NULL;
|
||||
Transition_For_Event(context, STATE_CONFIGURING, &Event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Initializing state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Initializing_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
//! \todo Add a menu item to change the weapon.
|
||||
KLOG_WARN(KLOG_TAG, "Weapon hardcoded to the Nerf Laser Strike Blaster!");
|
||||
uint8_t weapon_ID = NERF_LASER_STRIKE_BLASTER_ID;
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_WEAPONID, weapon_ID);
|
||||
(void) SETTINGS_Save();
|
||||
|
||||
//! \todo TEAM_PURPLE is always one-hit kill.
|
||||
uint8_t team_ID;
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &team_ID);
|
||||
TeamID_t my_common_team_ID = Resolve_Common_Team_ID(team_ID);
|
||||
|
||||
if (my_common_team_ID == TEAM_PURPLE)
|
||||
{
|
||||
Set_Health(10);
|
||||
}
|
||||
}
|
32
States/State_Initializing.h
Normal file
32
States/State_Initializing.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_INITIALIZING_H
|
||||
#define STATE_INITIALIZING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_INITIALIZING_Activities;
|
||||
|
||||
#endif // STATE_INITIALIZING_H
|
133
States/State_Machine.c
Normal file
133
States/State_Machine.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
const StateActivity_T STATE_DEFAULT_Activities =
|
||||
{
|
||||
.Entry = NULL,
|
||||
.Do = NULL,
|
||||
.Exit = NULL
|
||||
};
|
||||
|
||||
//! Activities for the top-level state machine.
|
||||
/*!
|
||||
* This array is indexed by #StateID_T (except there is no entry for
|
||||
* #STATE_OUT_OF_RANGE or #STATE_NONE), and the order of activities
|
||||
* must match the order of the enumeration.
|
||||
*/
|
||||
static const StateActivity_T * Activities[] =
|
||||
{
|
||||
&STATE_DEFAULT_Activities,
|
||||
&STATE_INITIALIZING_Activities,
|
||||
&STATE_REPROGRAMMING_Activities,
|
||||
&STATE_CONFIGURING_Activities,
|
||||
&STATE_READY_Activities,
|
||||
&STATE_STARTING_GAME__INSTIGATING_Activities,
|
||||
&STATE_STARTING_GAME__RESPONDING_Activities,
|
||||
&STATE_STARTING_GAME__COUNTING_DOWN_Activities,
|
||||
&STATE_PLAYING__INTERACTING_Activities,
|
||||
&STATE_PLAYING__TAGGED_OUT_Activities,
|
||||
&STATE_WRAPPING_UP_Activities
|
||||
};
|
||||
|
||||
//! Context in which the top-level state machine is run.
|
||||
static StateMachineContext_T Context =
|
||||
{
|
||||
.States =
|
||||
{
|
||||
.Previous_State = STATE_NONE,
|
||||
.Current_State = STATE_NONE,
|
||||
.Next_State = STATE_INITIALIZING
|
||||
},
|
||||
.Time_At_State_Entry_In_Ticks = 0,
|
||||
};
|
||||
|
||||
TaskHandle_t State_Machine_Task_Handle;
|
||||
|
||||
void State_Machine_Task(void * pvParameters)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Handle state changes.
|
||||
if (Context.States.Next_State != Context.States.Current_State)
|
||||
{
|
||||
// Exit the current state.
|
||||
if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE)
|
||||
{
|
||||
if (Activities[Context.States.Current_State]->Exit != NULL)
|
||||
{
|
||||
Activities[Context.States.Current_State]->Exit(&Context);
|
||||
}
|
||||
}
|
||||
|
||||
// Save off the time of the state change.
|
||||
Context.Time_At_State_Entry_In_Ticks = xTaskGetTickCount();
|
||||
|
||||
// Update the current state.
|
||||
Context.States.Previous_State = Context.States.Current_State;
|
||||
Context.States.Current_State = Context.States.Next_State;
|
||||
|
||||
if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE)
|
||||
{
|
||||
// Enter the current state.
|
||||
if (Activities[Context.States.Current_State]->Entry != NULL)
|
||||
{
|
||||
Activities[Context.States.Current_State]->Entry(&Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE)
|
||||
{
|
||||
// Execute the current state.
|
||||
if (Activities[Context.States.Current_State]->Do != NULL)
|
||||
{
|
||||
Activities[Context.States.Current_State]->Do(&Context);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessUnhandledEvent(KEvent_T * Event)
|
||||
{
|
||||
switch (Event->ID)
|
||||
{
|
||||
case KEVENT_TAG_RECEIVED:
|
||||
FreeDecodedPacketBuffer(Event->Data);
|
||||
break;
|
||||
|
||||
case KEVENT_COMMAND_RECEIVED:
|
||||
FreeDecodedPacketBuffer(Event->Data);
|
||||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
BLE_FreePacketBuffer(Event->Data);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events can be safely ignored.
|
||||
break;
|
||||
}
|
||||
}
|
131
States/State_Machine.h
Normal file
131
States/State_Machine.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_MACHINE_H
|
||||
#define STATE_MACHINE_H
|
||||
|
||||
#define LOG_STATE_MACHINE TRUE
|
||||
|
||||
#ifdef LOG_STATE_MACHINE
|
||||
#define LOG(message) KLOG_INFO("STATE", message)
|
||||
#else // LOG_STATE_MACHINE
|
||||
#define LOG(message)
|
||||
#endif // LOG_STATE_MACHINE
|
||||
|
||||
//! Names of the states and substates of the top-level state machine.
|
||||
/*! The enumerators are explicitly specified for (some of) these states
|
||||
* to aid in tracing and debugging (\see #LOG_STATE_MACHINE).
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
STATE_DEFAULT = 0,
|
||||
STATE_INITIALIZING = 1,
|
||||
STATE_REPROGRAMMING = 2,
|
||||
STATE_CONFIGURING = 3,
|
||||
STATE_READY = 4,
|
||||
|
||||
// Substates of STATE_STARTING_GAME
|
||||
STATE_STARTING_GAME__INSTIGATING = 5,
|
||||
STATE_STARTING_GAME__RESPONDING = 6,
|
||||
STATE_STARTING_GAME__COUNTING_DOWN = 7,
|
||||
|
||||
// Substates of STATE_PLAYING
|
||||
STATE_PLAYING__INTERACTING = 8,
|
||||
STATE_PLAYING__TAGGED_OUT = 9,
|
||||
|
||||
STATE_WRAPPING_UP = 10,
|
||||
|
||||
// STATE_IS_OUT_OF_RANGE is one more than the last valid state.
|
||||
STATE_IS_OUT_OF_RANGE,
|
||||
STATE_NONE = 255
|
||||
} StateID_T;
|
||||
|
||||
//! State information used by the top-level state machine.
|
||||
typedef struct
|
||||
{
|
||||
//! Most recent state of the top-level state machine not currently executing.
|
||||
StateID_T Previous_State;
|
||||
|
||||
//! Currently executing state of the top-level state machine.
|
||||
StateID_T Current_State;
|
||||
|
||||
//! Requested state of the top-level state machine.
|
||||
StateID_T Next_State;
|
||||
} State_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//! State information used by the top-level state machine.
|
||||
State_T States;
|
||||
|
||||
//! The most recent event to have caused a state transition.
|
||||
/*!
|
||||
* This is used by state entry logic to determine, for example,
|
||||
* which substate should be entered.
|
||||
*/
|
||||
KEvent_T* Cause_Of_Transition;
|
||||
|
||||
// Time (in RTOS ticks) when the current state was entered.
|
||||
TickType_t Time_At_State_Entry_In_Ticks;
|
||||
} StateMachineContext_T;
|
||||
|
||||
//! Activity actions for a given state, following UML 2.5.1 State Diagrams.
|
||||
/*!
|
||||
* \see https://www.omg.org/spec/UML/2.5.1/PDF
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
//! The function called when a state is entered.
|
||||
void (* const Entry)(StateMachineContext_T * context);
|
||||
|
||||
//! The function called periodically while in a state.
|
||||
void (* const Do)(StateMachineContext_T * context);
|
||||
|
||||
//! The function called when a state is exited.
|
||||
void (* const Exit)(StateMachineContext_T * context);
|
||||
} StateActivity_T;
|
||||
|
||||
extern TaskHandle_t State_Machine_Task_Handle;
|
||||
|
||||
void State_Machine_Task(void * pvParameters);
|
||||
void ProcessUnhandledEvent(KEvent_T * Event);
|
||||
|
||||
inline void Transition_For_Event(StateMachineContext_T* context, StateID_T next_state, KEvent_T* event)
|
||||
{
|
||||
context->States.Next_State = next_state;
|
||||
context->Cause_Of_Transition = event;
|
||||
}
|
||||
|
||||
inline uint32_t GetTimeInState_in_ms(StateMachineContext_T * context)
|
||||
{
|
||||
uint32_t result = (xTaskGetTickCount() - context->Time_At_State_Entry_In_Ticks) * portTICK_PERIOD_MS;
|
||||
return result;
|
||||
}
|
||||
|
||||
#include "State_Configuring.h"
|
||||
#include "State_Ready.h"
|
||||
#include "State_Initializing.h"
|
||||
#include "Playing/State_Playing.h"
|
||||
#include "Starting_Game/State_Starting_Game.h"
|
||||
#include "State_Reprogramming.h"
|
||||
#include "State_Wrapping_Up.h"
|
||||
|
||||
#endif // STATE_MACHINE_H
|
126
States/State_Ready.c
Normal file
126
States/State_Ready.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static const char *KLOG_TAG = "STATE_READY";
|
||||
|
||||
static const uint16_t LONG_PRESS_TO_INITIATE_GAME_in_ms = 5000;
|
||||
|
||||
static void Ready_Entry(StateMachineContext_T * context);
|
||||
static void Ready_Do(StateMachineContext_T * context);
|
||||
static void Ready_Exit(StateMachineContext_T * context);
|
||||
|
||||
//! Activities for the **Ready** state.
|
||||
const StateActivity_T STATE_READY_Activities =
|
||||
{
|
||||
.Entry = Ready_Entry,
|
||||
.Do = Ready_Do,
|
||||
.Exit = Ready_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Ready state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Ready_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Ready state.");
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_IDLE, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
BLE_UpdateStatusPacket();
|
||||
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't start BLE!");
|
||||
}
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_ON, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
|
||||
//! Executes the Ready state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Ready_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_TRIGGER_SWITCH_PRESSED:
|
||||
break;
|
||||
|
||||
case KEVENT_TRIGGER_SWITCH_RELEASED:
|
||||
{
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
|
||||
if (duration_of_press_in_ms > LONG_PRESS_TO_INITIATE_GAME_in_ms)
|
||||
{
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__INSTIGATING, &Event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_INSTIGATE_GAME)
|
||||
{
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__RESPONDING, &Event);
|
||||
}
|
||||
else
|
||||
{
|
||||
BLE_FreePacketBuffer(Event.Data);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_ENTER:
|
||||
case KEVENT_BACKWARD_SWITCH_LONG_PRESSED:
|
||||
Transition_For_Event(context, STATE_CONFIGURING, &Event);
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__INSTIGATING, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Ready state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Ready_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
32
States/State_Ready.h
Normal file
32
States/State_Ready.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_READY_H
|
||||
#define STATE_READY_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_READY_Activities;
|
||||
|
||||
#endif // STATE_READY_H
|
107
States/State_Reprogramming.c
Normal file
107
States/State_Reprogramming.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Reprogramming_Entry(StateMachineContext_T * context);
|
||||
static void Reprogramming_Do(StateMachineContext_T * context);
|
||||
static void Reprogramming_Exit(StateMachineContext_T * context);
|
||||
static All_On_Data_T Data;
|
||||
static bool Reprogramming_Sound_Complete = false;
|
||||
|
||||
//! Activities for the **Reprogramming** state.
|
||||
const StateActivity_T STATE_REPROGRAMMING_Activities =
|
||||
{
|
||||
.Entry = Reprogramming_Entry,
|
||||
.Do = Reprogramming_Do,
|
||||
.Exit = Reprogramming_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Reprogramming state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Reprogramming_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Reprogramming state.");
|
||||
Data.color = ApplyMask(COLOR_YELLOW, 0x70);
|
||||
Data.style = DISPLAY_STYLE_ALTERNATE;
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_ON, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&Data};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
Reprogramming_Sound_Complete = false;
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_REPROGRAMMING, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
|
||||
//! Executes the Reprogramming state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Reprogramming_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
static bool reprogramming_requested = false;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_AUDIO_COMPLETED:
|
||||
if (((AudioActionID_T)Event.Data) == AUDIO_PLAY_REPROGRAMMING)
|
||||
{
|
||||
Reprogramming_Sound_Complete = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// All events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait one second to initiate reprogramming, to allow the UI to update.
|
||||
// Note that the `reprogram` command behaves differently on different platforms--see the documentation.
|
||||
if ((xTaskGetTickCount() - context->Time_At_State_Entry_In_Ticks) > (1 * 1000 / portTICK_PERIOD_MS))
|
||||
{
|
||||
if ((reprogramming_requested == false) && (Reprogramming_Sound_Complete == true))
|
||||
{
|
||||
(void) HW_Execute_Console_Command((uint8_t *)"reprogram");
|
||||
reprogramming_requested = true;
|
||||
Data.color = ApplyMask(COLOR_ORANGE, 0x70);
|
||||
Data.style = DISPLAY_STYLE_ALTERNATE;
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_ON, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&Data};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Reprogramming state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Reprogramming_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
}
|
32
States/State_Reprogramming.h
Normal file
32
States/State_Reprogramming.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_REPROGRAMMING_H
|
||||
#define STATE_REPROGRAMMING_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_REPROGRAMMING_Activities;
|
||||
|
||||
#endif // STATE_REPROGRAMMING_H
|
109
States/State_Wrapping_Up.c
Normal file
109
States/State_Wrapping_Up.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 Files */
|
||||
#include "SystemK.h"
|
||||
|
||||
static const char *KLOG_TAG = "STATE_WRAPPING_UP";
|
||||
|
||||
static const uint16_t LONG_PRESS_FOR_CONFIGURING_in_ms = 5000;
|
||||
|
||||
static void Wrapping_Up_Entry(StateMachineContext_T * context);
|
||||
static void Wrapping_Up_Do(StateMachineContext_T * context);
|
||||
static void Wrapping_Up_Exit(StateMachineContext_T * context);
|
||||
|
||||
//! Activities for the **Wrapping Up** state.
|
||||
const StateActivity_T STATE_WRAPPING_UP_Activities =
|
||||
{
|
||||
.Entry = Wrapping_Up_Entry,
|
||||
.Do = Wrapping_Up_Do,
|
||||
.Exit = Wrapping_Up_Exit
|
||||
};
|
||||
|
||||
//! Sets up the Wrapping Up state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Wrapping_Up_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Wrapping Up state.");
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_IDLE, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
BLE_UpdateStatusPacket();
|
||||
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't start BLE!");
|
||||
}
|
||||
}
|
||||
|
||||
//! Executes the Wrapping Up state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Wrapping_Up_Do(StateMachineContext_T * context)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_TRIGGER_SWITCH_PRESSED:
|
||||
break;
|
||||
|
||||
case KEVENT_TRIGGER_SWITCH_RELEASED:
|
||||
{
|
||||
uint32_t duration_of_press_in_ms = (uint32_t)Event.Data;
|
||||
|
||||
if (duration_of_press_in_ms > LONG_PRESS_FOR_CONFIGURING_in_ms)
|
||||
{
|
||||
Transition_For_Event(context, STATE_CONFIGURING, &Event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
// TODO
|
||||
BLE_FreePacketBuffer(Event.Data);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
ProcessUnhandledEvent(&Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Cleans up the Wrapping Up state.
|
||||
/*!
|
||||
* \param context Context in which this state is being run.
|
||||
*/
|
||||
static void Wrapping_Up_Exit(StateMachineContext_T * context)
|
||||
{
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_OFF, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_SILENCE, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
32
States/State_Wrapping_Up.h
Normal file
32
States/State_Wrapping_Up.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef STATE_WRAPPING_UP_H
|
||||
#define STATE_WRAPPING_UP_H
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/* External Data */
|
||||
extern const StateActivity_T STATE_WRAPPING_UP_Activities;
|
||||
|
||||
#endif // STATE_WRAPPING_UP_H
|
Loading…
Add table
Add a link
Reference in a new issue