diff --git a/.gitignore b/.gitignore index 4182d65..f64b1a7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /steamworks_c_wrapper/sdk *.swp *~ +/steam_appid.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 793ee43..4815b24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,10 @@ configure_file( ) if (EXISTS "${PROJECT_SOURCE_DIR}/steamworks_c_wrapper/sdk") + MESSAGE ( STATUS "Steam SDK located, Steam build enabled") set(STEAM 1) +else () + MESSAGE ( STATUS "Steam SDK not found, Steam build disabled") endif() if (STEAM) @@ -62,22 +65,22 @@ IF ( MSVC ) MESSAGE ( STATUS "Setting MSVC MT switches") SET ( CMAKE_C_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} /MTd" + "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "MSVC MT flags " FORCE ) SET ( CMAKE_C_FLAGS_RELEASE - "${CMAKE_CXX_FLAGS_RELEASE} /MT" + "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "MSVC MT flags " FORCE ) ELSEIF ( WIN32 ) SET ( CMAKE_C_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} -mconsole" + "${CMAKE_C_FLAGS_DEBUG} -mconsole" ) SET ( CMAKE_C_FLAGS_RELEASE - "${CMAKE_CXX_FLAGS_RELEASE} -mwindows" + "${CMAKE_C_FLAGS_RELEASE} -mwindows" ) ENDIF () IF ( GCC OR CLANG ) @@ -101,6 +104,13 @@ else () ) endif () +if (STEAM) + include_directories( + ${STEAMWORKS_INCLUDE_DIR} + steamworks_c_wrapper/src + ) +endif () + include_directories( ${PROJECT_BINARY_DIR} ${LUA_INCLUDE_DIR} @@ -191,6 +201,7 @@ add_executable(breakhack src/object src/gui_util src/tooltip + src/steam/steamworks_api_wrapper ) # Sqlite has some warnings that I we don't need to see @@ -304,6 +315,7 @@ SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT "Release") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") if (WIN32) SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS + ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} ${CMAKE_SOURCE_DIR}/bin/libFLAC-8.dll ${CMAKE_SOURCE_DIR}/bin/libfreetype-6.dll ${CMAKE_SOURCE_DIR}/bin/libmodplug-1.dll @@ -321,7 +333,8 @@ if (WIN32) if (STEAM) SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - ${STEAMWORKS_LIBRARY} + steamworks_c_wrapper/sdk/redistributable_bin/steam_api.dll + build/steam/steam_appid.txt ) endif () endif (WIN32) @@ -346,11 +359,15 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${breakhack_MAJOR_VERSION}) set(CPACK_PACKAGE_VERSION_MINOR ${breakhack_MINOR_VERSION}) set(CPACK_PACKAGE_VERSION_PATCH ${breakhack_PATCH_VERSION}) set(CPACK_PACKAGE_INSTALL_DIRECTORY "BreakHack") -set(CPACK_PACKAGE_FILE_NAME "breakhack-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") +if (WIN32) + set(CPACK_PACKAGE_FILE_NAME "breakhack-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-win32") +else () + set(CPACK_PACKAGE_FILE_NAME "breakhack-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") +endif () set(CPACK_PACKAGE_CHECKSUM "MD5") if(UNIX) - set(CPACK_GENERATOR STGZ TGZ TZ) + set(CPACK_GENERATOR TGZ) set(CPACK_STRIP_FILES breakhack) set(CPACK_SOURCE_STRIP_FILES "") elseif(WIN32) diff --git a/src/defines.h b/src/defines.h index d7db9f7..c465c0f 100644 --- a/src/defines.h +++ b/src/defines.h @@ -84,6 +84,8 @@ typedef int16_t Sint16; typedef uint16_t Uint16; typedef int32_t Sint32; typedef uint32_t Uint32; +typedef int64_t Sint64; +typedef uint64_t Uint64; typedef enum Direction_t { UP, DOWN, LEFT, RIGHT diff --git a/src/main.c b/src/main.c index a6264db..34b44d6 100644 --- a/src/main.c +++ b/src/main.c @@ -22,7 +22,6 @@ #include #include #include - #include "linkedlist.h" #include "player.h" #include "screenresolution.h" @@ -54,6 +53,10 @@ #include "io_util.h" #include "tooltip.h" +#ifdef STEAM_BUILD +#include "steam/steamworks_api_wrapper.h" +#endif // STEAM_BUILD + static char *artifacts_tooltip[] = { "CONGRATULATIONS!", "", @@ -520,6 +523,10 @@ init(void) return false; } +#ifdef STEAM_BUILD + steam_init(); +#endif // STEAM_BUILD + settings_init(); hiscore_init(); initMainMenu(); @@ -933,6 +940,10 @@ run(void) pointer_handle_input(gPointer, &input); #endif // DEBUG +#ifdef STEAM_BUILD + steam_run_callbacks(); +#endif // STEAM_BUILD + switch (gGameState) { case PLAYING: case IN_GAME_MENU: @@ -1019,6 +1030,10 @@ void close(void) settings_close(); hiscore_close(); +#ifdef STEAM_BUILD + steam_shutdown(); +#endif // STEAM_BUILD + SDL_DestroyRenderer(gRenderer); SDL_DestroyWindow(gWindow); gWindow = NULL; diff --git a/src/steam/steamworks_api_wrapper.c b/src/steam/steamworks_api_wrapper.c new file mode 100644 index 0000000..022a647 --- /dev/null +++ b/src/steam/steamworks_api_wrapper.c @@ -0,0 +1,72 @@ +#include +#include "steamworks_api_wrapper.h" +#include "steamworks_c_wrapper.h" +#include "../util.h" +#include "../defines.h" + +static Achievement g_Achievements[] = { + _ACH_ID(BAD_DOG, "Bad Dog"), + _ACH_ID(THE_DOCTOR_IS_OUT, "The Doctor is Out"), + _ACH_ID(LIGHTS_ON, "Omnidirectional light"), + _ACH_ID(BACK_TO_WORK, "Back to work"), + _ACH_ID(DRAGON_SLAYER, "Platinum dragon slayer") +}; +static Uint8 numAchievements = 5; + +static bool m_Initiated = false; +static Sint64 m_AppID = 0; + +static bool +steam_request_stats() +{ + if (m_Initiated) + return c_SteamUserStats_RequestCurrentStats(); + return false; +} + +static void +stats_received(void) +{ + debug("Stats received"); + for (Uint8 i = 0; i < numAchievements; ++i) { + Achievement *a = &g_Achievements[i]; + c_SteamUserStats_GetAchievement(a->m_pchAchievementID, &a->m_bAchieved); + gui_log("You just earned the \"%s\" achievement", c_SteamUserStats_GetAchievementDisplayAttribute(a->m_pchAchievementID, "name")); + } +} + +static void +stats_stored(void) +{ + debug("Stats stored"); +} + +void +steam_init() +{ + m_AppID = c_SteamAPI_Init(); + m_Initiated = m_AppID != 0; + if (m_Initiated) { + c_SteamAPI_SetCallbacks(stats_received, stats_stored); + steam_request_stats(); + } +} + +void steam_shutdown(void) +{ + c_SteamAPI_Shutdown(); +} + +void steam_run_callbacks(void) +{ + if (m_Initiated) + c_SteamAPI_RunCallbacks(); +} + +void steam_set_achievement(EAchievement eAch) +{ + for (Uint8 i = 0; i < numAchievements; ++i) { + if (g_Achievements[i].m_eAchievementID == eAch) + c_SteamUserStats_SetAchievement(g_Achievements[i].m_pchAchievementID); + } +} diff --git a/src/steam/steamworks_api_wrapper.h b/src/steam/steamworks_api_wrapper.h new file mode 100644 index 0000000..a3ca8bc --- /dev/null +++ b/src/steam/steamworks_api_wrapper.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "../defines.h" + +typedef enum EAchievement +{ + BAD_DOG = 0, + THE_DOCTOR_IS_OUT = 1, + LIGHTS_ON = 2, + BACK_TO_WORK = 5, + DRAGON_SLAYER = 6 +} EAchievement; + + +#define _ACH_ID( id, name ) { id, #id, name, "", 0, 0 } +typedef struct Achievement { + EAchievement m_eAchievementID; + const char *m_pchAchievementID; + char m_rgchName[128]; + char m_rgchDescription[256]; + bool m_bAchieved; + int m_iIconImage; +} Achievement; + +void steam_init(void); + +void steam_shutdown(void); + +void steam_run_callbacks(void); + +void steam_set_achievement(EAchievement eAch); \ No newline at end of file diff --git a/steam/steamworks_api_wrapper.c b/steam/steamworks_api_wrapper.c deleted file mode 100644 index 21060e3..0000000 --- a/steam/steamworks_api_wrapper.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * BreakHack - A dungeone crawler RPG - * Copyright (C) 2018 Linus Probert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include "steamworks_api_wrapper.h" - diff --git a/steam/steamworks_api_wrapper.h b/steam/steamworks_api_wrapper.h deleted file mode 100644 index 121688b..0000000 --- a/steam/steamworks_api_wrapper.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * BreakHack - A dungeone crawler RPG - * Copyright (C) 2018 Linus Probert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - diff --git a/steamworks_c_wrapper/CMakeLists.txt b/steamworks_c_wrapper/CMakeLists.txt index 4d10aea..127c2ee 100644 --- a/steamworks_c_wrapper/CMakeLists.txt +++ b/steamworks_c_wrapper/CMakeLists.txt @@ -30,3 +30,17 @@ add_library(steamworks_c_wrapper ) target_link_libraries(steamworks_c_wrapper ${STEAMWORKS_LIBRARY}) + +IF ( MSVC ) + MESSAGE ( STATUS "Setting MSVC MT switches") + SET ( + CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} /MTd" + CACHE STRING "MSVC MT flags " FORCE + ) + SET ( + CMAKE_CXX_FLAGS_RELEASE + "${CMAKE_CXX_FLAGS_RELEASE} /MT" + CACHE STRING "MSVC MT flags " FORCE + ) +endif () diff --git a/steamworks_c_wrapper/src/steamworks_c_wrapper.cpp b/steamworks_c_wrapper/src/steamworks_c_wrapper.cpp index 16c0534..2596778 100644 --- a/steamworks_c_wrapper/src/steamworks_c_wrapper.cpp +++ b/steamworks_c_wrapper/src/steamworks_c_wrapper.cpp @@ -4,22 +4,105 @@ extern "C" { #include "steamworks_c_wrapper.h" } -extern "C" void c_SteamAPI_Init() +static bool m_Initiated = false; +static int64 m_AppId = 0; +static CallbackHandler *m_CallbackHandler = NULL; +static bool m_RecvCB = false; + +static void(*statsReceivedCb)(void) = NULL; +static void(*statsStoredCb)(void) = NULL; + + +extern "C" int64 c_SteamAPI_Init() { - SteamAPI_Init(); + if (SteamAPI_Init()) { + m_AppId = SteamUtils()->GetAppID(); + m_CallbackHandler = new CallbackHandler(); + m_Initiated = true; + return m_AppId; + } + return 0; +} + +extern "C" void c_SteamAPI_SetCallbacks(void(*recvCB)(void), void(*storCB)(void)) +{ + statsReceivedCb = recvCB; + statsStoredCb = storCB; +} + +extern "C" int64 c_SteamAPI_GetAppID() +{ + if (!m_Initiated) + return 0; + m_AppId = SteamUtils()->GetAppID(); + return m_AppId; +} + +void c_SteamAPI_RunCallbacks(void) +{ + if (m_Initiated) + SteamAPI_RunCallbacks(); } extern "C" void c_SteamAPI_Shutdown() { + delete m_CallbackHandler; SteamAPI_Shutdown(); } extern "C" bool c_SteamUserStats_RequestCurrentStats() { + if (NULL == SteamUserStats() || NULL == SteamUser()) + return false; + if (!SteamUser()->BLoggedOn()) + return false; + return SteamUserStats()->RequestCurrentStats(); } extern "C" bool c_SteamUserStats_SetAchievement(const char *pchName) { - return SteamUserStats()->SetAchievement(pchName); + if (!m_RecvCB) + return false; + + bool result = SteamUserStats()->SetAchievement(pchName); + if (result) + SteamUserStats()->StoreStats(); + return result; +} + +extern "C" void c_SteamUserStats_GetAchievement(const char *achId, bool *achieved) +{ + SteamUserStats()->GetAchievement(achId, achieved); +} + +extern "C" const char* c_SteamUserStats_GetAchievementDisplayAttribute(const char *achId, const char *attrName) +{ + return SteamUserStats()->GetAchievementDisplayAttribute(achId, attrName); +} + +CallbackHandler::CallbackHandler() : + m_CallbackUserStatsReceived(this, &CallbackHandler::OnUserStatsReceived), + m_CallbackUserStatsStored(this, &CallbackHandler::OnUserStatsStored) +{ +} + +void +CallbackHandler::OnUserStatsReceived(UserStatsReceived_t *pCallback) +{ + if (m_AppId != pCallback->m_nGameID) + return; + m_RecvCB = true; + if (statsReceivedCb && k_EResultOK == pCallback->m_eResult) + statsReceivedCb(); +} + +void +CallbackHandler::OnUserStatsStored(UserStatsStored_t *pCallback) +{ + if (m_AppId != pCallback->m_nGameID) + return; + + if (statsStoredCb && k_EResultOK == pCallback->m_eResult) + statsStoredCb(); } diff --git a/steamworks_c_wrapper/src/steamworks_c_wrapper.h b/steamworks_c_wrapper/src/steamworks_c_wrapper.h index afd3161..c482ef3 100644 --- a/steamworks_c_wrapper/src/steamworks_c_wrapper.h +++ b/steamworks_c_wrapper/src/steamworks_c_wrapper.h @@ -1,31 +1,40 @@ -#pragma once - -enum Achievements -{ - ACH_BAD_DOG = 0, - ACH_THE_DOCTOR_IS_OUT = 1, - ACH_LIGHTS_ON = 2, - ACH_BACK_TO_WORK = 5, -}; - -struct Achievement -{ - Achievements m_eAchievementID; - const char *m_pchAchievementID; - char m_rgchName[128]; - char m_rgchDescription[256]; - bool m_bAchieved; - int m_iIconImage; -}; - -void -c_SteamAPI_Init(); - -bool -c_steam_request_stats(); - -bool -c_steam_set_achievement(const char *id); - -void -c_SteamAPI_Shutdown(); +#pragma once + +#include + +int64_t +c_SteamAPI_Init(void); + +int64_t +c_SteamAPI_GetAppID(void); + +void +c_SteamAPI_RunCallbacks(void); + +void +c_SteamAPI_SetCallbacks(void(*)(void), void(*)(void)); + +bool +c_SteamUserStats_RequestCurrentStats(); + +bool +c_SteamUserStats_SetAchievement(const char *id); + +void +c_SteamUserStats_GetAchievement(const char *achId, bool *achieved); + +const char * +c_SteamUserStats_GetAchievementDisplayAttribute(const char *achId, const char *attrName); + +void +c_SteamAPI_Shutdown(); + +#ifdef __cplusplus +class CallbackHandler +{ +public: + CallbackHandler(); + STEAM_CALLBACK(CallbackHandler, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived); + STEAM_CALLBACK(CallbackHandler, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored); +}; +#endif // __cplusplus \ No newline at end of file