/* * This program source code file is part of SystemK, a library in the KTag project. * * 🛡️ 🃞 * * 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 . */ #ifndef GAME_H #define GAME_H #include "Weapons.h" //! Implemented according to the 2024-07-20 "KTag Teams Compatibility Matrix". typedef enum { TEAM_PURPLE = 0, BASIC_TEAMS_MINIMUM = 0, TEAM_RED = 1, TEAM_BLUE = 2, BASIC_TEAMS_MAXIMUM = 2, TEAM_WHITE = 3, COMMON_TEAM_ID_MASK = 3, TEAM_ORANGE = 4, TEAM_GREEN = 5, TEAM_YELLOW = 6, TEAM_BLACK = 7, EXTENDED_TEAM_ID_MASK = 7, MAXIMUM_TEAM_ID = 7 } TeamID_t; inline bool Is_Valid_Team_ID(uint8_t team_ID) { bool result = false; if (team_ID <= MAXIMUM_TEAM_ID) { result = true; } return result; } __attribute__((always_inline)) static inline SystemKResult_T Set_Team_With_Audio_Feedback(uint8_t team_ID) { static uint8_t Team_ID = 0; // This is static because AUDIO_PRONOUNCE_NUMBER_0_TO_100 needs a *pointer*. SystemKResult_T result = SYSTEMK_RESULT_SUCCESS; Team_ID = team_ID; if (Team_ID > MAXIMUM_TEAM_ID) { result = SYSTEMK_RESULT_OVERFLOW; } else { result = SETTINGS_set_uint8_t(SYSTEMK_SETTING_TEAMID, Team_ID); if (result == SYSTEMK_RESULT_SUCCESS) { AudioAction_T audio_action = {.ID = AUDIO_PLAY_TEAM_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00}; Perform_Audio_Action(&audio_action); AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Team_ID}; Perform_Audio_Action(&volume_action); } } return result; } __attribute__((always_inline)) inline TeamID_t Resolve_Common_Team_ID(uint8_t team_ID) { return (team_ID & COMMON_TEAM_ID_MASK); } __attribute__((always_inline)) static inline SystemKResult_T Set_Player_With_Audio_Feedback(uint8_t player_ID) { static uint8_t Player_ID = 0; // This is static because AUDIO_PRONOUNCE_NUMBER_0_TO_100 needs a *pointer*. SystemKResult_T result = SYSTEMK_RESULT_SUCCESS; Player_ID = player_ID; result = SETTINGS_set_uint8_t(SYSTEMK_SETTING_PLAYERID, Player_ID); if (result == SYSTEMK_RESULT_SUCCESS) { AudioAction_T audio_action = {.ID = AUDIO_PLAY_PLAYER_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00}; Perform_Audio_Action(&audio_action); AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Player_ID}; Perform_Audio_Action(&volume_action); } return result; } __attribute__((always_inline)) inline bool Team_Can_Tag_Me(uint8_t tagging_team_ID) { bool can_tag = false; 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 (tagging_team_ID == TEAM_PURPLE) { can_tag = true; } else if (tagging_team_ID != my_common_team_ID) { if ((tagging_team_ID == TEAM_RED) || (tagging_team_ID == TEAM_BLUE)) { can_tag = true; } } return can_tag; } typedef struct { uint8_t My_Health; WeaponID_t My_Weapon; uint8_t My_Shield_Strength; uint8_t Max_Health; uint32_t Time_Remaining_in_Game_in_ms; uint32_t Time_Remaining_Until_Countdown_in_ms; uint16_t Shots_Fired; uint16_t Tags_Received; uint16_t Times_Tagged_Out; uint8_t Bombs_Remaining; } KTag_Game_Data_T; extern KTag_Game_Data_T KTAG_Game_Data; __attribute__((always_inline)) inline uint8_t Get_Health() { return KTAG_Game_Data.My_Health; } __attribute__((always_inline)) inline void Set_Health(uint8_t health) { KTAG_Game_Data.My_Health = health; if (KTAG_Game_Data.My_Health == 0) { KEvent_T tagged_out_event = {.ID = KEVENT_TAGGED_OUT, .Data = (void *)0x00}; Post_KEvent(&tagged_out_event); } } __attribute__((always_inline)) inline uint8_t Get_Max_Health() { if (KTAG_Game_Data.Max_Health == 0) { // Initialize from settings. uint8_t max_health; if (SETTINGS_get_uint8_t(SYSTEMK_SETTING_MAX_HEALTH, &max_health) == SYSTEMK_RESULT_SUCCESS) { KTAG_Game_Data.Max_Health = max_health; } else { KTAG_Game_Data.Max_Health = 100; } } return KTAG_Game_Data.Max_Health; } __attribute__((always_inline)) inline void Set_Max_Health(uint8_t health) { KTAG_Game_Data.Max_Health = health; // Limit current health to max health. if (KTAG_Game_Data.My_Health > health) { KTAG_Game_Data.My_Health = health; } } __attribute__((always_inline)) inline void Reduce_Health(uint8_t reduction) { if (reduction < KTAG_Game_Data.My_Health) { KTAG_Game_Data.My_Health -= reduction; } else { KTAG_Game_Data.My_Health = 0; KEvent_T tagged_out_event = {.ID = KEVENT_TAGGED_OUT, .Data = (void *)0x00}; Post_KEvent(&tagged_out_event); } } __attribute__((always_inline)) inline WeaponID_t Get_Weapon() { return KTAG_Game_Data.My_Weapon; } __attribute__((always_inline)) inline void Set_Weapon(WeaponID_t weapon) { KTAG_Game_Data.My_Weapon = weapon; } __attribute__((always_inline)) inline uint16_t Get_Shield_Strength() { return KTAG_Game_Data.My_Shield_Strength; } __attribute__((always_inline)) inline void Set_Shield_Strength(uint16_t strength) { KTAG_Game_Data.My_Shield_Strength = strength; } __attribute__((always_inline)) inline void Reset_Shots_Fired() { KTAG_Game_Data.Shots_Fired = 0; } __attribute__((always_inline)) inline void Increment_Shots_Fired() { if (KTAG_Game_Data.Shots_Fired < UINT16_MAX) { KTAG_Game_Data.Shots_Fired++; } } __attribute__((always_inline)) inline void Reset_Tags_Received() { KTAG_Game_Data.Tags_Received = 0; } __attribute__((always_inline)) inline void Increment_Tags_Received() { if (KTAG_Game_Data.Tags_Received < UINT16_MAX) { KTAG_Game_Data.Tags_Received++; } } __attribute__((always_inline)) inline void Reset_Times_Tagged_Out() { KTAG_Game_Data.Times_Tagged_Out = 0; } __attribute__((always_inline)) inline void Increment_Times_Tagged_Out() { if (KTAG_Game_Data.Times_Tagged_Out < UINT16_MAX) { KTAG_Game_Data.Times_Tagged_Out++; } } __attribute__((always_inline)) inline bool Still_Playing() { return (KTAG_Game_Data.My_Health > 0); } __attribute__((always_inline)) inline bool Back_In() { return Still_Playing(); } __attribute__((always_inline)) inline void Set_Time_Remaining_in_Game(uint32_t time_in_ms) { KTAG_Game_Data.Time_Remaining_in_Game_in_ms = time_in_ms; } __attribute__((always_inline)) inline uint32_t Get_Time_Remaining_in_Game_in_ms() { return KTAG_Game_Data.Time_Remaining_in_Game_in_ms; } __attribute__((always_inline)) inline void Set_Time_Remaining_Until_Countdown(uint32_t time_in_ms) { KTAG_Game_Data.Time_Remaining_Until_Countdown_in_ms = time_in_ms; } __attribute__((always_inline)) inline uint32_t Get_Time_Remaining_Until_Countdown_in_ms() { return KTAG_Game_Data.Time_Remaining_Until_Countdown_in_ms; } __attribute__((always_inline)) inline uint16_t Get_Shots_Fired() { return KTAG_Game_Data.Shots_Fired; } __attribute__((always_inline)) inline uint16_t Get_Tags_Received() { return KTAG_Game_Data.Tags_Received; } __attribute__((always_inline)) inline uint16_t Get_Times_Tagged_Out() { return KTAG_Game_Data.Times_Tagged_Out; } __attribute__((always_inline)) inline void Set_Available_Bombs(uint8_t n_bombs) { KTAG_Game_Data.Bombs_Remaining = n_bombs; } __attribute__((always_inline)) inline bool Use_Bomb_If_Available() { bool available = false; if (KTAG_Game_Data.Bombs_Remaining > 0) { KTAG_Game_Data.Bombs_Remaining--; available = true; } return available; } #endif // GAME_H