├── .vscode ├── c_cpp_properties.json └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── bin ├── Data │ ├── img.archt │ └── maps │ │ ├── devmap.tmap │ │ ├── el.tmap │ │ ├── lvl1.tmap │ │ ├── lvl2.tmap │ │ ├── lvl3.tmap │ │ ├── lvl4.tmap │ │ ├── lvl5.tmap │ │ ├── lvl6.tmap │ │ └── thefrozenend.tmap └── SDL2.dll └── src ├── Engine ├── A_Application.c ├── A_Application.h ├── D_AssetsManager.c ├── D_AssetsManager.h ├── D_ObjectsCallbacks.c ├── D_ObjectsCallbacks.h ├── G_AI.c ├── G_AI.h ├── G_AIBehaviour.c ├── G_AIBehaviour.h ├── G_Game.c ├── G_Game.h ├── G_MainMenu.c ├── G_MainMenu.h ├── G_Pathfinding.c ├── G_Pathfinding.h ├── G_Player.c ├── G_Player.h ├── G_Skills.c ├── G_Skills.h ├── I_InputHandling.c ├── I_InputHandling.h ├── M_Map.c ├── M_Map.h ├── P_Physics.c ├── P_Physics.h ├── R_Rendering.c ├── R_Rendering.h ├── T_TextRendering.c ├── T_TextRendering.h ├── U_DataTypes.h ├── U_Timer.c ├── U_Timer.h ├── U_Utilities.c └── U_Utilities.h ├── Network ├── net.c ├── netdef.h ├── packet.c ├── packet.h ├── replication.c └── replication.h ├── Online ├── O_Game.c ├── O_Game.h ├── O_GameDef.h ├── O_Lobby.c └── O_Lobby.h ├── main.c ├── toment.ico ├── toment.o ├── toment.rc └── toment.res /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "C:\\Lib\\SDL2-2.30.1\\include" 8 | ], 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE" 13 | ], 14 | "windowsSdkVersion": "10.0.19041.0", 15 | "compilerPath": "C:/MinGW/bin/gcc.exe", 16 | "cStandard": "c17", 17 | "cppStandard": "c++17", 18 | "intelliSenseMode": "gcc-x64" 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "cmath": "c", 4 | "netdef.h": "c", 5 | "d_assetsmanager.h": "c", 6 | "packet.h": "c", 7 | "r_rendering.h": "c", 8 | "g_player.h": "c", 9 | "o_game.h": "c", 10 | "replication.h": "c", 11 | "t_textrendering.h": "c", 12 | "o_lobby.h": "c", 13 | "u_timer.h": "c" 14 | } 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mattia Silvestro (silvematt) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #OBJS specifies which files to compile as part of the project 2 | OBJS = src/Engine/A_Application.c src/Engine/I_InputHandling.c src/Engine/M_Map.c src/Engine/R_Rendering.c src/Engine/G_Player.c src/Engine/G_Game.c src/main.c src/Engine/D_AssetsManager.c src/Engine/P_Physics.c src/Engine/U_Timer.c src/Engine/U_Utilities.c src/Engine/T_TextRendering.c src/Engine/G_MainMenu.c src/Engine/D_ObjectsCallbacks.c src/Engine/G_Pathfinding.c src/Engine/G_AI.c src/Engine/G_AIBehaviour.c src/Network/net.c src/Network/packet.c src/Online/O_Lobby.c src/Online/O_Game.c src/Network/replication.c src/Engine/G_Skills.c src/toment.o 3 | 4 | #CC specifies which compiler we're using 5 | CC = gcc 6 | 7 | #INCLUDE_PATHS specifies the additional include paths we'll need 8 | INCLUDE_PATHS = -IC:\Lib\SDL2-2.30.1\include 9 | 10 | #LIBRARY_PATHS specifies the additional library paths we'll need 11 | LIBRARY_PATHS = -LC:\Lib\SDL2-2.30.1\lib\x86 12 | 13 | #COMPILER_FLAGS specifies the additional compilation options we're using 14 | COMPILER_FLAGS = -O2 -Wl,--subsystem,windows -mwindows 15 | 16 | #LINKER_FLAGS specifies the libraries we're linking against 17 | LINKER_FLAGS = -lmingw32 -lSDL2main -lSDL2 -lws2_32 18 | 19 | #OBJ_NAME specifies the name of our exectuable 20 | OBJ_NAME = bin/TomentOnline 21 | 22 | #This is the target that compiles our executable 23 | all : $(OBJS) 24 | $(CC) $(OBJS) $(INCLUDE_PATHS) $(LIBRARY_PATHS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TomentOnline 2 | A Software Rendering Raycaster Engine with multiplayer written in C and SDL2. 3 | 4 | ![123](https://github.com/silvematt/TomentOnline/assets/20478938/c8d513da-2b94-4704-a8d6-87cac39fc87c) 5 | 6 | 7 | Video: https://youtu.be/1Z0X3V8cNl4 8 | 9 | TomentOnline is an upgraded version of the [TomentRaycaster](https://github.com/silvematt/TomentRaycaster) engine, that adds multiplayer capabilities and more. 10 | For the Online part of it, I've read through many online resouces and the book "Multiplayer Game Programming: Architecting Networked Games" has been of great help. 11 | 12 | This was another pilgrimage to my God John Carmack and my starting point for multiplayer game programming. It took me about 1 month. 13 | 14 | 15 |

Features, on top of TomentRaycaster:

16 | 17 | Rendering: 18 | - Better performances. 19 | - Maps can be as big as 128x128 (but smaller are also supported) 20 | - Linear fog rendering 21 | - 8 angled sprites rendering 22 | 23 | Multiplayer: 24 | - TCP socket connections (using Winsock), P2P architecture with the host being responsabile of running the AI 25 | - Structured packets with a max size of 1300 bytes for data (1302 in total) 26 | - Buffered send/receive to allow many packets per frame to be sent/received (while accounting for short receive/send - thanks to [skeeto](https://github.com/skeeto) for pointing me in the right direction) 27 | 28 | Gameplay: 29 | - Two players per dungeon 30 | - Lobby and class selection (Tank, Healer, DPS) 31 | - Player skills with cooldowns for each class 32 | - Damage puddles 33 | - 2 new bosses (3 in total) with new mechanics 34 | 35 | 36 |

Other:

37 | The players connect to each other using the host/join buttons in the main menu, from there the host inserts its username, while the joiner needs to insert the IP Address, Port (61530) and username. 38 |

39 | If you open the port 61530 on your router you can let any other player join your matches, otherwise you can play locally by insert the local IPs of your devices. 40 | As for now the communication is 100% trusted, there are no counter measure or bad-input checking, so if you wish to test it make sure to play with someone who you trust will not cheat. 41 |

42 | With "The Frozen End" dungeon I've probably squeezed the engine as much as possible, especially with the bosses behaviors and mechanics, this will likely be the last "Toment" project. 43 |

44 | The game data is packed in custom files (.archt) format [GENERATED WITH: [TomentARCH](https://github.com/silvematt/TomentARCH)] 45 | -------------------------------------------------------------------------------- /bin/Data/img.archt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentOnline/e10fa5fecb1dc8a1004a47194cabeb3438da0d09/bin/Data/img.archt -------------------------------------------------------------------------------- /bin/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentOnline/e10fa5fecb1dc8a1004a47194cabeb3438da0d09/bin/SDL2.dll -------------------------------------------------------------------------------- /src/Engine/A_Application.c: -------------------------------------------------------------------------------- 1 | #include "A_Application.h" 2 | 3 | #include "R_Rendering.h" 4 | 5 | // Definitions 6 | app_t application; 7 | 8 | // Surface of the Window 9 | SDL_Surface* win_surface; 10 | int win_width; 11 | unsigned int* pixels; 12 | 13 | // Surface of the rendered raycasting image, scaled appropiately 14 | SDL_Surface* raycast_surface; 15 | unsigned int* raycast_pixels; 16 | 17 | // Game State 18 | bool isInMenu; // True if the player is in a menu 19 | bool isInGame; // True if the player is in game 20 | 21 | // Look at .h for explaination of data 22 | int cpuCount; 23 | SDL_Thread** threads; 24 | thread_data_t** threadsdata; 25 | bool** threadDone; 26 | int extingThreads = 0; 27 | bool threadIterationFinished; 28 | 29 | thread_job_e currentThreadJob = TS_RAYCAST; 30 | 31 | SDL_mutex* threadDataMutex; 32 | SDL_cond* condThreadWait; 33 | SDL_sem* renderingThreadSemaphore; 34 | SDL_sem* mainThreadWaitSem; 35 | SDL_sem* renderingThreadSemaphore; 36 | 37 | SDL_mutex* rayDataMutex; 38 | 39 | //------------------------------------- 40 | // Initializes the application and subsystems 41 | //------------------------------------- 42 | void A_InitApplication(void) 43 | { 44 | application.quit = false; 45 | 46 | printf("Booting up...\n"); 47 | 48 | SDL_Init(SDL_INIT_EVERYTHING); 49 | 50 | uint32_t winFlags = 0; 51 | //uint32_t winFlags = SDL_WINDOW_FULLSCREEN; 52 | 53 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 54 | 55 | application.win = SDL_CreateWindow("Toment", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, winFlags); 56 | win_surface = SDL_GetWindowSurface(application.win); 57 | 58 | win_width = win_surface->w; 59 | pixels = win_surface->pixels; 60 | 61 | // Initialize CPU Count 62 | cpuCount = SDL_GetCPUCount(); 63 | 64 | printf("CPU Count: %d\n", cpuCount); 65 | 66 | printf("Creating multithread structures...\n"); 67 | threadIterationFinished = false; 68 | 69 | mainThreadWaitSem = SDL_CreateSemaphore(0); 70 | renderingThreadSemaphore = SDL_CreateSemaphore(0); 71 | threadDataMutex = SDL_CreateMutex(); 72 | condThreadWait = SDL_CreateCond(); 73 | 74 | rayDataMutex = SDL_CreateMutex(); 75 | 76 | printf("Creating threads...\n"); 77 | threads = malloc(cpuCount * sizeof(SDL_Thread*)); 78 | threadsdata = malloc(cpuCount * sizeof(thread_data_t*)); 79 | threadDone = malloc(cpuCount * sizeof(bool*)); 80 | 81 | for(int i = 0; i < cpuCount; i++) 82 | { 83 | threadsdata[i] = malloc(sizeof(thread_data_t)); 84 | threadsdata[i]->localID = i; 85 | 86 | threadDone[i] = malloc(sizeof(bool)); 87 | 88 | threads[i] = SDL_CreateThread(R_ThreadRoutine, "THREAD", threadsdata[i]); 89 | } 90 | 91 | currentThreadJob = TS_RAYCAST; 92 | 93 | // Init Renderer 94 | R_InitRendering(); 95 | 96 | srand(time(NULL)); 97 | 98 | // Define entry state for the application 99 | A_ChangeState(GSTATE_MENU); 100 | } 101 | 102 | //------------------------------------- 103 | // Quit Applicaiton 104 | //------------------------------------- 105 | void A_QuitApplication(void) 106 | { 107 | SDL_Quit(); 108 | } 109 | 110 | //------------------------------------- 111 | // Change State 112 | //------------------------------------- 113 | void A_ChangeState(gamestate_e newState) 114 | { 115 | application.gamestate = newState; 116 | 117 | switch(application.gamestate) 118 | { 119 | case GSTATE_MENU: 120 | SDL_SetRelativeMouseMode(SDL_FALSE); 121 | break; 122 | 123 | case GSTATE_GAME: 124 | SDL_SetRelativeMouseMode(SDL_TRUE); 125 | break; 126 | 127 | default: 128 | SDL_SetRelativeMouseMode(SDL_FALSE); 129 | break; 130 | } 131 | } -------------------------------------------------------------------------------- /src/Engine/A_Application.h: -------------------------------------------------------------------------------- 1 | #ifndef APPLICATION_H_INCLUDED 2 | #define APPLICATION_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "SDL.h" 10 | #include "SDL_thread.h" 11 | 12 | // Defines 13 | #define SCREEN_WIDTH 800 14 | #define SCREEN_HEIGHT 600 15 | 16 | #define MAX_STRLEN 255 // Max lenght of a string to read 17 | 18 | #define MAX_FILEPATH_L 255 // Max lenght of a filepath 19 | #define MAX_STRL_R 8192 // Max lenght of a fgets while reading 20 | 21 | 22 | // Holds the current game state 23 | typedef enum gamestate_e 24 | { 25 | dev = 0, 26 | GSTATE_MENU, 27 | GSTATE_GAME 28 | } gamestate_e; 29 | 30 | // Fundamental information about the application 31 | typedef struct app_s 32 | { 33 | SDL_Window* win; 34 | bool quit; 35 | gamestate_e gamestate; 36 | } app_t; 37 | 38 | // Declarations 39 | extern app_t application; 40 | 41 | extern SDL_Surface* win_surface; // the surface of the window 42 | extern int win_width; // win_surface->w 43 | extern unsigned int* pixels; // pixels of the surface 44 | 45 | extern SDL_Surface* raycast_surface; // the surface the raycasting is written to 46 | extern unsigned int* raycast_pixels; // pixels of the surface 47 | 48 | // Multithreading rendering 49 | extern int cpuCount; // Number of threads to spawn 50 | extern SDL_Thread** threads; // all threads 51 | 52 | // Per-thread data 53 | typedef struct thread_data_s 54 | { 55 | int localID; 56 | } thread_data_t; 57 | 58 | typedef enum thread_job_e 59 | { 60 | TS_RAYCAST = 0, 61 | TS_FLOORCAST, 62 | TS_CEILINGCAST 63 | } thread_job_e; 64 | 65 | extern bool** threadDone; // array of booleans, threadDone[i] tellls if the i-th thread has finished the computation (but doesn't mean it ended the loop) 66 | extern bool threadIterationFinished; // if true, the current iteration is finished 67 | extern int extingThreads; // how many threads finished and exited the current iteration and are ready for the next 68 | 69 | extern thread_job_e currentThreadJob; 70 | 71 | extern SDL_mutex* threadDataMutex; // Mutex to access and modify the data defined above (threadDone, threadIterationFinished, extingThreads) 72 | extern SDL_cond* condThreadWait; // When one thread is done (but not all of them), he will wait on this condition 73 | extern SDL_sem* renderingThreadSemaphore; // When a thread is ready to work, he will wait on this semaphore before going inside the function, when the main thread wants to start the work of the threads, he will post on this semaphore 74 | extern SDL_sem* mainThreadWaitSem; // When all threads are done and extingThreads = cpuCount, means the iteration is definitely finished, so we can wake up the main thread and continue 75 | 76 | extern SDL_mutex* rayDataMutex; // Mutex to access and modify the data filled while raycasting (AddToThinWall, AddDynamicSprites, etc) 77 | 78 | //------------------------------------- 79 | // Initializes the application 80 | //------------------------------------- 81 | void A_InitApplication(void); 82 | 83 | //------------------------------------- 84 | // Quit Application 85 | //------------------------------------- 86 | void A_QuitApplication(void); 87 | 88 | //------------------------------------- 89 | // Change State 90 | //------------------------------------- 91 | void A_ChangeState(gamestate_e newState); 92 | 93 | #endif -------------------------------------------------------------------------------- /src/Engine/D_AssetsManager.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSETS_MANAGER_H_INCLUDED 2 | #define ASSETS_MANAGER_H_INCLUDED 3 | 4 | #include "stdio.h" 5 | 6 | #include "SDL.h" 7 | #include "U_DataTypes.h" 8 | 9 | // -------------------------------------------- 10 | // DEFINES 11 | // -------------------------------------------- 12 | #define OBJECTARRAY_DEFAULT_SIZE 256 13 | #define OBJECTARRAY_DEFAULT_SIZE_HIGH 2048 14 | 15 | // Table of Content 16 | #define MAX_TOC_LENGTH 256 17 | 18 | // Default Fallback Objects 19 | typedef enum enginesDefaultsID_e 20 | { 21 | EDEFAULT_1 = 0 22 | } enginesDefaultsID_t; 23 | 24 | // All Walls and doors 25 | typedef enum wallObjectID_e 26 | { 27 | // 0 = Empty 28 | W_Wall = 1, 29 | W_ThinWallHor, 30 | W_ThinWallVer, 31 | W_DoorHor, 32 | W_DoorVer, 33 | W_WallTriggerChangeMap, 34 | W_WallLadder, 35 | W_WallLadderDown, 36 | W_WallInvisible 37 | } wallObjectID_t; 38 | 39 | // All Textures IDs 40 | typedef enum textureID_e 41 | { 42 | // 0 = Empty 43 | TEXTURE_WallBrick1 = 1, 44 | TEXTURE_WallBrick1Dark, 45 | TEXTURE_FloorBrick1, 46 | TEXTURE_CeilingWood1, 47 | TEXTURE_Wall2, 48 | TEXTURE_Gate1, 49 | TEXTURE_Gate1Alt, 50 | TEXTURE_CastleDoor, 51 | TEXTURE_Wall1Ladder, 52 | TEXTURE_FloorBrick2, 53 | TEXTURE_FloorDirt1, 54 | TEXTURE_Ice, 55 | TEXTURE_IceConsacrated, 56 | TEXTURE_VioletVoid, 57 | TEXTURE_IcyGround 58 | } textureID_e; 59 | 60 | // All sprites 61 | typedef enum spritesObjectID_e 62 | { 63 | // 0 = Empty 64 | S_Barrel1 = 1, 65 | S_Campfire, 66 | DS_Skeleton, 67 | S_Fireball1, 68 | S_PickupAxe, 69 | S_PickupHealthPotion, 70 | S_PickupManaPotion, 71 | S_IceDart1, 72 | S_TomeFireball1, 73 | S_TomeIceDart1, 74 | S_Table1, 75 | S_SkullStatic, 76 | DS_SkeletonElite, 77 | S_AltarEmpty, 78 | S_AltarHealth, 79 | S_AltarMana, 80 | DS_SkeletonBurnt, 81 | S_PickupGreatsword, 82 | DS_SkeletonLord, 83 | S_Achtung, 84 | DS_PlayerTank, 85 | DS_PlayerHealer, 86 | DS_PlayerDPS, 87 | S_ConcentratedHeal, 88 | S_SwordProjectile, 89 | DS_MorgathulTheKeeper, 90 | S_MorgathulOrb, 91 | DS_Kroganar, 92 | DS_MorgathulCopy, 93 | S_AltarSpellPower, 94 | DS_TheFrozenLord, 95 | S_PickupChangeEnvironmentTheFrozenEnd, // changes skyid and fog settings before frozen end final boss 96 | S_IceBlast, 97 | DS_FrozenLordsCaster 98 | } spritesObjectID_t; 99 | 100 | // All skies textures 101 | typedef enum skiesObjectID_e 102 | { 103 | // 0 = Empty 104 | SKY_Default1 = 1, 105 | SKY_Red1, 106 | SKY_Night 107 | } skiesObjectID_e; 108 | 109 | // Animations of an Object 110 | typedef enum objectanimationsID_e 111 | { 112 | ANIM_IDLE = 0, 113 | ANIM_ATTACK1, 114 | ANIM_DIE, 115 | ANIM_CAST_SPELL, 116 | ANIM_SPECIAL1, 117 | ANIM_SPECIAL2, 118 | ANIM_SPECIAL3 119 | } objectanimationsID_e; 120 | 121 | // Specific object data for AI 122 | typedef struct objectAnimations_s 123 | { 124 | struct object_s* belongsTo; 125 | 126 | SDL_Surface* animIdle; // The image 127 | unsigned animIdleSheetLength; // The sheet lenght 128 | unsigned animIdleActionFrame; // The frame that has to be reached in order to perform the animation action (like casting a spell) 129 | int animIdleSpeedModifier; 130 | 131 | SDL_Surface* animDie; 132 | unsigned animDieSheetLength; 133 | unsigned animDieActionFrame; 134 | int animDieSpeedModifier; 135 | 136 | SDL_Surface* animAttack; 137 | unsigned animAttackSheetLength; 138 | unsigned animAttackActionFrame; 139 | int animAttackSpeedModifier; 140 | 141 | SDL_Surface* animCastSpell; 142 | unsigned animCastSpellSheetLength; 143 | unsigned animCastSpellActionFrame; 144 | int animCastSpellkSpeedModifier; 145 | 146 | // Extra Animations 147 | SDL_Surface* animSpecial1; 148 | unsigned animSpecial1SheetLength; 149 | unsigned animSpecial1ActionFrame; 150 | int animSpecial1SpeedModifier; 151 | 152 | SDL_Surface* animSpecial2; 153 | unsigned animSpecial2SheetLength; 154 | unsigned animSpecial2ActionFrame; 155 | int animSpecial2SpeedModifier; 156 | 157 | SDL_Surface* animSpecial3; 158 | unsigned animSpecial3SheetLength; 159 | unsigned animSpecial3ActionFrame; 160 | int animSpecial3SpeedModifier; 161 | } objectanimations_t; 162 | 163 | 164 | 165 | // Data type for Walls 166 | typedef struct wallAsset_s 167 | { 168 | int ID; 169 | 170 | byte flags; // Flags to diversify types of objects 171 | 172 | // Callbacks 173 | void (*Callback)(char* data); 174 | } wallAsset_t; 175 | 176 | // Identify the faces of the walls 177 | #define TEXTURE_ARRAY_TOP 0 178 | #define TEXTURE_ARRAY_BOTTOM 1 179 | #define TEXTURE_ARRAY_LEFT 2 180 | #define TEXTURE_ARRAY_RIGHT 3 181 | #define TEXTURE_ARRAY_UP 4 182 | #define TEXTURE_ARRAY_DOWN 5 183 | 184 | // Wall Objects are dynamic wall data loaded from the map file 185 | typedef struct wallObject_s 186 | { 187 | // The related wallAsset_t 188 | int assetID; 189 | 190 | // Texture IDs for each face, used for cube walls, after being init they point to texture 191 | // TOP BOTTOM LEFT RIGHT UP DOWN 192 | // (0, 1, 2, 3, 4, 5 ) 193 | // Top texture (index 0) is the default texture, used for non cubed wall (like doors and thin walls) 194 | int texturesArray[6]; 195 | 196 | // Data of the callback (i.e. the level to load for the wallTrigger asset) 197 | char data[OBJECTARRAY_DEFAULT_SIZE]; 198 | } wallObject_t; 199 | 200 | 201 | /* object_t Flags 202 | 203 | // ============ 204 | // For walls 205 | // ============ 206 | // 0000000 0 207 | // \ 208 | // 1 = Is Ladder 209 | */ 210 | // The common Object data 211 | typedef struct object_s 212 | { 213 | int ID; 214 | SDL_Surface* texture; 215 | struct object_s* alt; 216 | byte flags; // Flags to diversify types of objects 217 | 218 | objectanimations_t* animations; 219 | 220 | // Callbacks 221 | char* data; 222 | void (*Callback)(char* data); 223 | } object_t; 224 | 225 | // Lightweight version of object_t suited for textures 226 | typedef struct textureObject_s 227 | { 228 | int ID; 229 | SDL_Surface* texture; 230 | byte flags; 231 | } textureObject_t; 232 | 233 | 234 | // The Text rendering is not hardcoded to use 16x6 elements font sheets, but the translation map is, 235 | // if you wish to use more character or a different map of the characters, you'll have to edit the translation code, 236 | // but the system's code should work just fine 237 | #define FONT_MAX_ELEMENTS_WIDTH 16 238 | #define FONT_MAX_ELEMENTS_HEIGHT 6 239 | 240 | // Font data 241 | typedef struct fontSheet_s 242 | { 243 | unsigned int width; // Width of the tiles that compose the font sheet 244 | int numHorElements; // How many horizontal items this sheet has 245 | int numVerElements; // How many vertical items this sheet has 246 | SDL_Surface* texture; // The whole fontsheet as an SDL_Surface 247 | int glyphsWidth[FONT_MAX_ELEMENTS_HEIGHT][FONT_MAX_ELEMENTS_WIDTH]; // The actual used width of each glyph, used for text-spacing 248 | } fontsheet_t; 249 | 250 | // Lightweight version of object_t suited for UI Assets 251 | typedef struct uiAssets_s 252 | { 253 | SDL_Surface* texture; 254 | } uiAssets_t; 255 | 256 | // All Fonts IDS 257 | typedef enum fontsID_e 258 | { 259 | FONT_BLKCRY = 0, 260 | FONT_BLKCRY_GREEN, 261 | } fontsID_t; 262 | 263 | // Menu Assets IDS 264 | typedef enum uiAssetsID_e 265 | { 266 | M_ASSET_SELECT_CURSOR = 0, 267 | M_ASSET_TITLE, 268 | G_ASSET_HEALTHBAR_EMPTY, 269 | G_ASSET_HEALTHBAR_FILL, 270 | G_ASSET_MANABAR_EMPTY, 271 | G_ASSET_MANABAR_FILL, 272 | G_ASSET_ICON_FISTS, 273 | G_ASSET_ICON_AXE, 274 | G_ASSET_ICON_SPELL_FIREBALL1, 275 | G_ASSET_UI_CROSSHAIR, 276 | G_ASSET_ICON_SPELL_ICEDART1, 277 | G_ASSET_UI_CROSSHAIR_HIT, 278 | G_ASSET_BOSS_HEALTHBAR_EMPTY, 279 | G_ASSET_BOSS_HEALTHBAR_FILL, 280 | G_ASSET_ICON_GREATSWORD, 281 | G_ASSET_CLASS_POTRAIT_TANK, 282 | G_ASSET_CLASS_POTRAIT_HEALER, 283 | G_ASSET_CLASS_POTRAIT_DPS, 284 | G_ASSET_CLASS_ICON_TANK, 285 | G_ASSET_CLASS_ICON_HEALER, 286 | G_ASSET_CLASS_ICON_DPS, 287 | G_ASSET_CLASS_ICON_SELECTION, 288 | G_ASSET_CLASS_ICON_SELECTION_DISABLED, 289 | G_ASSET_ICON_READY, 290 | G_ASSET_ICON_NOTREADY, 291 | G_ASSET_ICON_MACE, 292 | G_ASSET_SKILL_ICON_TANK_SHIELD_SLAM, 293 | G_ASSET_SKILL_ICON_TANK_SHIELD_BLOCK, 294 | G_ASSET_SKILL_ICON_TANK_CONSACRATION, 295 | G_ASSET_SKILL_ICON_HEALER_SELF_HEAL, 296 | G_ASSET_SKILL_ICON_HEALER_CONCENTRATED_HEAL, 297 | G_ASSET_SKILL_ICON_HEALER_DIVINE_INTERVENTION, 298 | G_ASSET_SKILL_ICON_DPS_CHEAPSHOT, 299 | G_ASSET_SKILL_ICON_DPS_OBLITERATE, 300 | G_ASSET_SKILL_ICON_DPS_SPLIT, 301 | G_ASSET_SKILL_ICON_EMPTY, 302 | M_ASSET_INPUTFIELD_01, 303 | M_ASSET_INPUTFIELD_01_ACTIVE 304 | } uiAssetsID_e; 305 | 306 | // FP Player IDs 307 | typedef enum playerFPID_e 308 | { 309 | PLAYER_FP_HANDS = 0, 310 | PLAYER_FP_AXE, 311 | PLAYER_FP_GREATSWORD, 312 | PLAYER_FP_MACE 313 | } playerFPID_e; 314 | 315 | 316 | /* object_t Flags 317 | 318 | // ============ 319 | // For walls 320 | // ============ 321 | // 0000000 0 322 | // \ 323 | // 1 = Is Thin Wall 324 | 325 | // 000000 0 0 326 | // \ 327 | // 1 = Is Vertical (used with thin Wall) 328 | 329 | // 00000 0 00 330 | // \ 331 | // 1 = Is Door 332 | 333 | // 0000 0 000 334 | // \ 335 | // 1 = Is Trigger 336 | 337 | // 000 0 0000 338 | // \ 339 | // 1 = Is Invisible (Skipped by non-occlusive raycast) 340 | 341 | // ============ 342 | // For sprites 343 | // ============ 344 | For sprites 345 | // 0000000 0 346 | // \ 347 | // 1 = solid (used for collision checking) 348 | 349 | // 000000 0 0 350 | // \ 351 | // 1 = Animated sprite (uses horizontal sheet) 352 | 353 | // 00000 0 00 354 | // \ 355 | // 1 = Dynamic sprite: A dynamic sprite is a sprite that can move in the world and update the sprites maps, used for AI 356 | 357 | // 0000 0 000 358 | // \ 359 | // 1 = Auto call callback (if present) upon player's collision 360 | 361 | // 000 0 0000 362 | // \ 363 | // 1 = Is 8 angled sprite 364 | 365 | 366 | */ 367 | 368 | // Table of Content elements for opening archives (MUST BE IN SYNCH WITH ARCH) 369 | typedef struct tocElement_s 370 | { 371 | uint32_t id; 372 | uint32_t startingOffset; 373 | uint32_t size; 374 | } tocElement_t; 375 | 376 | // IDs of the Images in the IMGArchive 377 | // MUST BE IN SYNCH WITH ARCH application (Files.txt needs to have the same elements in the same order as this enum) https://github.com/silvematt/TomentARCH 378 | typedef enum imgIDs_e 379 | { 380 | IMG_ID_EDEFAULT_1 = 0, 381 | IMG_ID_W_1, 382 | IMG_ID_W_1Alt, 383 | IMG_ID_W_2, 384 | IMG_ID_WD_Gate1, 385 | IMG_ID_WD_Gate1Alt, 386 | IMG_ID_F_1, 387 | IMG_ID_C_1, 388 | IMG_ID_S_Barrel1, 389 | IMG_ID_S_Campfire, 390 | IMG_ID_BLKCRY_TEXT_SHEET, 391 | IMG_ID_MENU_SELECT_CURSOR, 392 | IMG_ID_MENU_TITLE, 393 | IMG_ID_SKY_DEFAULT, 394 | IMG_ID_P_HANDS_IDLE, 395 | IMG_ID_WT_CASTLE_DOORS, 396 | IMG_ID_AI_SKELETON, 397 | IMG_ID_AI_SKELETON_DEATH, 398 | IMG_ID_P_HANDS_ATTACK, 399 | IMG_ID_P_HANDS_CASTSPELL, 400 | IMG_ID_SPELL_FIREBALL1, 401 | IMG_ID_SPELL_FIREBALL1_EXPLOSION, 402 | IMG_ID_HEALTHBAR_EMPTY, 403 | IMG_ID_HEALTHBAR_FILL, 404 | IMG_ID_MANABAR_EMPTY, 405 | IMG_ID_MANABAR_FILL, 406 | IMG_ID_AI_SKELETON_ATTACK, 407 | IMG_ID_PICKUP_AXE, 408 | IMG_ID_PICKUP_HEALTH_POTION, 409 | IMG_ID_PICKUP_MANA_POTION, 410 | IMG_ID_P_AXE_IDLE, 411 | IMG_ID_P_AXE_ATTACK1, 412 | IMG_ID_ICON_FISTS, 413 | IMG_ID_ICON_AXE, 414 | IMG_ID_ICON_SPELL_FIREBALL1, 415 | IMG_ID_UI_CROSSHAIR, 416 | IMG_ID_SPELL_ICEDART1, 417 | IMG_ID_SPELL_ICEDART_EXPLOSION, 418 | IMG_ID_ICON_SPELL_ICEDART1, 419 | IMG_ID_UI_CROSSHAIR_HIT, 420 | IMG_ID_TOME_FIREBALL01, 421 | IMG_ID_TOME_ICEDART01, 422 | IMG_ID_WALL1_LADDER, 423 | IMG_ID_S_Table1, 424 | IMG_ID_S_SKULL_STATIC, 425 | IMG_ID_AI_SKELETON_ELITE, 426 | IMG_ID_AI_SKELETON_ELITE_ATTACK, 427 | IMG_ID_AI_SKELETON_ELITE_DEATH, 428 | IMG_ID_BOSS_HEALTHBAR_EMPTY, 429 | IMG_ID_BOSS_HEALTHBAR_FILL, 430 | IMG_ID_S_ALTAR_EMPTY, 431 | IMG_ID_S_ALTAR_HEALTH, 432 | IMG_ID_S_ALTAR_MANA, 433 | IMG_ID_AI_SKELETON_BURNT, 434 | IMG_ID_AI_SKELETON_BURNT_ATTACK, 435 | IMG_ID_AI_SKELETON_BURNT_DEATH, 436 | IMG_ID_F_2, 437 | IMG_ID_F_DIRT, 438 | IMG_ID_SKY_RED, 439 | IMG_ID_PIKCUP_GREATSWORD, 440 | IMG_ID_P_GREATSWORD_IDLE, 441 | IMG_ID_P_GREATSWORD_ATTACK, 442 | IMG_ID_ICON_GREATSWORD, 443 | IMG_ID_SKELETON_LORD, 444 | IMG_ID_SKELETON_LORD_ATTACK_MELEE, 445 | IMG_ID_SKELETON_LORD_ATTACK_FIREBALL, 446 | IMG_ID_SKELETON_LORD_DEATH, 447 | IMG_ID_SKELETON_LORD_SPELL_HELL, 448 | IMG_ID_SKELETON_LORD_SPELL_RESURRECTION, 449 | IMG_ID_AI_SKELETON_RESURRECTION, 450 | IMG_ID_ACHTUNG, 451 | IMG_ID_TANK_CLASS_POTRAIT, 452 | IMG_ID_HEALER_CLASS_POTRAIT, 453 | IMG_ID_DPS_CLASS_POTRAIT, 454 | IMG_ID_TANK_CLASS_ICON, 455 | IMG_ID_HEALER_CLASS_ICON, 456 | IMG_ID_DPS_CLASS_ICON, 457 | IMG_ID_CLASS_ICON_SELECTION, 458 | IMG_ID_CLASS_ICON_SELECTION_DISABLED, 459 | IMG_ID_ICON_READY, 460 | IMG_ID_ICON_NOTREADY, 461 | IMG_ID_BLKCRY_GREEN_TEXT_SHEET, 462 | IMG_ID_PLAYER_TANK_CHARACTER, 463 | IMG_ID_PLAYER_HEALER_CHARACTER, 464 | IMG_ID_PLAYER_DPS_CHARACTER, 465 | IMG_ID_P_MACE_IDLE, 466 | IMG_ID_P_MACE_ATTACK1, 467 | IMG_ID_ICON_MACE, 468 | IMG_ID_SKILL_ICON_TANK_SHIELD_SLAM, 469 | IMG_ID_SKILL_ICON_TANK_SHIELD_BLOCK, 470 | IMG_ID_SKILL_ICON_TANK_CONSACRATION, 471 | IMG_ID_SKILL_ICON_HEALER_SELF_HEAL, 472 | IMG_ID_SKILL_ICON_HEALER_CONCENTRATED_HEAL, 473 | IMG_ID_SKILL_ICON_HEALER_DIVINE_INTERVENTION, 474 | IMG_ID_SKILL_ICON_DPS_CHEAPSHOT, 475 | IMG_ID_SKILL_ICON_DPS_OBLITERATE, 476 | IMG_ID_SKILL_ICON_DPS_SPLIT, 477 | IMG_ID_SKILL_ANIM_TANK_SHIELD_SLAM, 478 | IMG_ID_SKILL_ANIM_TANK_SHIELD_BLOCK, 479 | IMG_ID_SKILL_ICON_EMPTY, 480 | IMG_ID_ICE, 481 | IMG_ID_ICE_CONSACRATED, 482 | IMG_ID_SKILL_ANIM_HEALER_SELF_HEAL, 483 | IMG_ID_SPELL_CONCENTRATED_HEAL, 484 | IMG_ID_SPELL_CONCENTRATED_HEAL_EXPLOSION, 485 | IMG_ID_SKILL_ANIM_DPS_CHEAPSHOT, 486 | IMG_ID_SKILL_ANIM_DPS_SPLIT, 487 | IMG_ID_SWORD_PROJECTILE, 488 | IMG_ID_SWORD_PROJECTILE_EXPLOSION, 489 | IMG_ID_SKELETON_ELITE_RESURRECTION, 490 | IMG_ID_SKY_NIGHT, 491 | IMG_ID_MORGATHUL_IDLE, 492 | IMG_ID_MORGATHUL_CAST1, 493 | IMG_ID_MORGATHUL_CAST2, 494 | IMG_ID_MORGATHUL_COPY_DEATH, 495 | IMG_ID_MORGATHUL_DEATH, 496 | IMG_ID_SPELL_MORGATHUL_ORB, 497 | IMG_ID_SPELL_MORGATHUL_ORB_EXPLOSION, 498 | IMG_ID_KROGANAR_IDLE, 499 | IMG_ID_KROGANAR_ATTACK, 500 | IMG_ID_KROGANAR_DEATH, 501 | IMG_ID_KROGANAR_RESURRECTION, 502 | IMG_ID_VIOLET_VOID, 503 | IMG_ID_MORGATHUL_COPY_RESURRECTION, 504 | IMG_ID_S_ALTAR_SPELL_POWER, 505 | IMG_ID_S_THEFROZENLORD_IDLE, 506 | IMG_ID_S_THEFROZENLORD_DEATH, 507 | IMG_ID_S_THEFROZENLORD_ATTACK1, 508 | IMG_ID_S_THEFROZENLORD_ATTACK2, 509 | IMG_ID_S_THEFROZENLORD_ATTACK3, 510 | IMG_ID_EMPTY_IMG, 511 | IMG_ID_TEXTURE_ICYGROUND, 512 | IMG_ID_SPELL_ICE_BLAST, 513 | IMG_ID_SPELL_ICE_BLAST_EXPLOSION, 514 | IMG_ID_AI_SKELETON_BURNT_ATTACK2, 515 | IMG_ID_INPUTFIELD_01, 516 | IMG_ID_INPUTFIELD_01_ACTIVE 517 | } imgIDs_e; 518 | 519 | // Archt Data 520 | typedef struct archt_s 521 | { 522 | FILE* file; 523 | uint32_t fileLength; 524 | tocElement_t toc[MAX_TOC_LENGTH]; 525 | uint32_t tocSize; 526 | uint32_t tocElementsLenght; 527 | uint32_t tocOffset; // how many bytes (fileLength included) to skip the ToC and access the data 528 | byte* buffer; 529 | } archt_t; 530 | 531 | // The whole datapack of the game 532 | typedef struct tomentdatapack_s 533 | { 534 | // ------------------------------- 535 | // Archives 536 | // ------------------------------- 537 | 538 | // img.archt 539 | archt_t IMGArch; 540 | 541 | // Texture database 542 | textureObject_t* textures[OBJECTARRAY_DEFAULT_SIZE]; 543 | unsigned texturesLength; 544 | 545 | // Font databse 546 | fontsheet_t* fontsheets[OBJECTARRAY_DEFAULT_SIZE]; // All fonts 547 | unsigned fontsheetsLength; 548 | 549 | // ------------------------------- 550 | // UI 551 | // ------------------------------- 552 | uiAssets_t* uiAssets[OBJECTARRAY_DEFAULT_SIZE]; 553 | unsigned uiAssetsLenght; 554 | 555 | // ------------------------------- 556 | // In Game Assets 557 | // ------------------------------- 558 | 559 | // Default Engine's Objects 560 | object_t* enginesDefaults[OBJECTARRAY_DEFAULT_SIZE]; 561 | unsigned enginesDefaultsLength; 562 | 563 | // Object in the game 564 | object_t* skies[OBJECTARRAY_DEFAULT_SIZE]; 565 | unsigned skiesLength; 566 | 567 | wallAsset_t* walls[OBJECTARRAY_DEFAULT_SIZE]; 568 | unsigned wallsLength; 569 | 570 | object_t* sprites[OBJECTARRAY_DEFAULT_SIZE]; 571 | unsigned spritesLength; 572 | 573 | // Contains the value of the length of the spreadsheet for each sprite delcared 574 | // Access by spritesObjectID_e 575 | int spritesSheetsLenghtTable[OBJECTARRAY_DEFAULT_SIZE]; 576 | 577 | object_t* playersFP[OBJECTARRAY_DEFAULT_SIZE]; 578 | unsigned playersFPLength; 579 | } tomentdatapack_t; 580 | 581 | extern tomentdatapack_t tomentdatapack; 582 | 583 | //------------------------------------- 584 | // Returns true if the texture has correctly loaded, otherwise false and an error 585 | //------------------------------------- 586 | bool D_CheckTextureLoaded(SDL_Surface* ptr, int ID); 587 | 588 | //------------------------------------- 589 | // Initializes defauls for an object_t 590 | //------------------------------------- 591 | void D_InitObject(object_t* obj); 592 | 593 | //------------------------------------- 594 | // Initializes defauls for a wallAsset_t 595 | //------------------------------------- 596 | void D_InitWallAsset(wallAsset_t* obj); 597 | 598 | //------------------------------------- 599 | // Initializes defauls for a textureObject_t 600 | //------------------------------------- 601 | void D_InitTextureAsset(textureObject_t* obj); 602 | 603 | //------------------------------------- 604 | // Initializes the Asset Manager 605 | //------------------------------------- 606 | void D_InitAssetManager(void); 607 | 608 | //------------------------------------- 609 | // Opens the archives to allow objects initializations 610 | //------------------------------------- 611 | void D_OpenArchs(void); 612 | 613 | //------------------------------------- 614 | // Closes the archives to and frees buffers 615 | //------------------------------------- 616 | void D_CloseArchs(void); 617 | 618 | 619 | //------------------------------------- 620 | // Initialize the TomentDataPack elements 621 | //------------------------------------- 622 | 623 | void D_InitEnginesDefaults(void); 624 | void D_InitLoadTextures(void); 625 | void D_InitFontSheets(void); 626 | void D_InitUIAssets(void); 627 | void D_InitLoadWalls(void); 628 | void D_InitLoadSprites(void); 629 | void D_InitLoadSkies(void); 630 | void D_InitLoadPlayersFP(void); 631 | 632 | //------------------------------------- 633 | // Sets the object for the given parameters 634 | //------------------------------------- 635 | void D_SetObject(object_t* obj, int id, SDL_Surface* texture, object_t* alt); 636 | 637 | #endif -------------------------------------------------------------------------------- /src/Engine/D_ObjectsCallbacks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "G_Game.h" 4 | #include "D_ObjectsCallbacks.h" 5 | 6 | void D_CallbackChangeMap(char* data) 7 | { 8 | G_ChangeMap(data); 9 | } 10 | 11 | void D_CallbackPickup(char* data) 12 | { 13 | if(strcmp(data, "WEAPON_AXE") == 0) 14 | { 15 | printf("Adding Weapon: Axe \n"); 16 | 17 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 18 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X, ALERT_MESSAGE_DEF_Y, "You picked up an Axe", 2.0f, 1.0f); 19 | 20 | player.hasAxe = true; 21 | G_PlayerSetWeapon(PLAYER_FP_AXE); 22 | } 23 | else if(strcmp(data, "WEAPON_GREATSWORD") == 0) 24 | { 25 | printf("Adding Weapon: Greatsword \n"); 26 | 27 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 28 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-50, ALERT_MESSAGE_DEF_Y, "You picked up a Greatsword", 2.0f, 1.0f); 29 | 30 | player.hasGreatsword = true; 31 | G_PlayerSetWeapon(PLAYER_FP_GREATSWORD); 32 | } 33 | else if(strcmp(data, "PICKUP_HEALTH") == 0) 34 | { 35 | printf("Adding player's health\n"); 36 | 37 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 38 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-50, ALERT_MESSAGE_DEF_Y, "You drink an Health Potion", 2.0f, 1.0f); 39 | 40 | G_PlayerGainHealth(30.0f); 41 | } 42 | else if(strcmp(data, "PICKUP_MANA") == 0) 43 | { 44 | printf("Adding player's mana\n"); 45 | 46 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 47 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-50, ALERT_MESSAGE_DEF_Y, "You drink a Mana Potion", 2.0f, 1.0f); 48 | G_PlayerGainMana(30.0f); 49 | } 50 | else if(strcmp(data, "TOME_FIREBALL") == 0) 51 | { 52 | printf("Adding Spell: Fireball\n"); 53 | 54 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 55 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-50, ALERT_MESSAGE_DEF_Y, "You learn: Fireball!", 2.0f, 1.0f); 56 | player.hasFireball = true; 57 | G_PlayerSetSpell(SPELL_FIREBALL1); 58 | } 59 | else if(strcmp(data, "TOME_ICEDART") == 0) 60 | { 61 | printf("Adding Spell: Ice Dart\n"); 62 | 63 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 64 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-50, ALERT_MESSAGE_DEF_Y, "You learn: Ice Dart!", 2.0f, 1.0f); 65 | player.hasIceDart = true; 66 | G_PlayerSetSpell(SPELL_ICEDART1); 67 | } 68 | } 69 | 70 | void D_CallbackLadder(char* data) 71 | { 72 | printf("Climbing Ladder\n"); 73 | wallObject_t* wallObj = R_GetWallObjectFromMap(player.level, player.inFrontGridPosition.y, player.inFrontGridPosition.x); 74 | 75 | // Check if he player is facing a ladder 76 | 77 | // If player is looking at the up face 78 | if(player.angle < (5*M_PI)/6 && player.angle > M_PI/6) 79 | { 80 | // Check if the texture at that point is the ladder 81 | if(U_GetBit(&tomentdatapack.textures[wallObj->texturesArray[TEXTURE_ARRAY_UP]]->flags, 0)) 82 | { 83 | // Climb!! 84 | player.climbingPosX = player.position.x; 85 | player.climbingPosY = player.position.y + TILE_SIZE; 86 | player.climbingPosZ = player.z + TILE_SIZE; 87 | player.climbingUp = true; 88 | player.hasToClimb = true; 89 | } 90 | } 91 | // If player is looking at the bottom face 92 | else if(player.angle > (7*M_PI)/6 && player.angle < (11*M_PI)/6) 93 | { 94 | // Check if the texture at that point is the ladder 95 | if(U_GetBit(&tomentdatapack.textures[wallObj->texturesArray[TEXTURE_ARRAY_DOWN]]->flags, 0)) 96 | { 97 | // Climb!! 98 | player.climbingPosX = player.position.x; 99 | player.climbingPosY = player.position.y - TILE_SIZE; 100 | player.climbingPosZ = player.z + TILE_SIZE; 101 | player.climbingUp = true; 102 | player.hasToClimb = true; 103 | } 104 | } 105 | // If the player is looking at the left face 106 | else if(player.angle < M_PI/3 || player.angle > (5*M_PI) / 3) 107 | { 108 | // Check if the texture at that point is the ladder 109 | if(U_GetBit(&tomentdatapack.textures[wallObj->texturesArray[TEXTURE_ARRAY_LEFT]]->flags, 0)) 110 | { 111 | // Climb!! 112 | player.climbingPosX = player.position.x + TILE_SIZE; 113 | player.climbingPosY = player.position.y; 114 | player.climbingPosZ = player.z + TILE_SIZE; 115 | player.climbingUp = true; 116 | player.hasToClimb = true; 117 | } 118 | } 119 | // If the player is looking at the right face 120 | else if(player.angle > (2*M_PI)/3 && player.angle < (4*M_PI)/3) 121 | { 122 | // Check if the texture at that point is the ladder 123 | if(U_GetBit(&tomentdatapack.textures[wallObj->texturesArray[TEXTURE_ARRAY_RIGHT]]->flags, 0)) 124 | { 125 | // Climb!! 126 | player.climbingPosX = player.position.x - TILE_SIZE; 127 | player.climbingPosY = player.position.y; 128 | player.climbingPosZ = player.z + TILE_SIZE; 129 | player.climbingUp = true; 130 | player.hasToClimb = true; 131 | } 132 | } 133 | } 134 | 135 | void D_CallbackLadderDown(char* data) 136 | { 137 | printf("Climbing Ladder Down\n"); 138 | wallObject_t* wallObj = R_GetWallObjectFromMap(player.level, player.inFrontGridPosition.y, player.inFrontGridPosition.x); 139 | 140 | // Check if he player is facing a ladder 141 | 142 | // If player is looking at the up face 143 | if(player.angle < (5*M_PI)/6 && player.angle > M_PI/6) 144 | { 145 | // Check if the texture at that point is the ladder 146 | // Climb!! 147 | player.climbingPosX = player.position.x; 148 | player.climbingPosY = player.position.y + TILE_SIZE; 149 | player.climbingPosZ = player.z - TILE_SIZE; 150 | player.climbingUp = false; 151 | player.hasToClimb = true; 152 | } 153 | // If player is looking at the bottom face 154 | else if(player.angle > (7*M_PI)/6 && player.angle < (11*M_PI)/6) 155 | { 156 | // Climb!! 157 | player.climbingPosX = player.position.x; 158 | player.climbingPosY = player.position.y - TILE_SIZE; 159 | player.climbingPosZ = player.z - TILE_SIZE; 160 | player.climbingUp = false; 161 | player.hasToClimb = true; 162 | } 163 | // If the player is looking at the left face 164 | else if(player.angle < M_PI/3 || player.angle > (5*M_PI) / 3) 165 | { 166 | // Check if the texture at that point is the ladder 167 | // Climb!! 168 | player.climbingPosX = player.position.x + TILE_SIZE; 169 | player.climbingPosY = player.position.y; 170 | player.climbingPosZ = player.z - TILE_SIZE; 171 | player.climbingUp = false; 172 | player.hasToClimb = true; 173 | } 174 | // If the player is looking at the right face 175 | else if(player.angle > (2*M_PI)/3 && player.angle < (4*M_PI)/3) 176 | { 177 | // Climb!! 178 | player.climbingPosX = player.position.x - TILE_SIZE; 179 | player.climbingPosY = player.position.y; 180 | player.climbingPosZ = player.z - TILE_SIZE; 181 | player.climbingUp = false; 182 | player.hasToClimb = true; 183 | } 184 | } 185 | 186 | void D_CallbackUseAltar(char* data) 187 | { 188 | printf("Used Altar\n"); 189 | if(strcmp(data, "HEALTH") == 0) 190 | { 191 | printf("Praying to an Health Altar...\n"); 192 | player.attributes.maxHealth += 100; 193 | player.attributes.curHealth = player.attributes.maxHealth; 194 | 195 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 196 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-30, ALERT_MESSAGE_DEF_Y, "You pray to the Altar\nand your health increases.", 3.0f, 1.0f); 197 | } 198 | else if (strcmp(data, "MANA") == 0) 199 | { 200 | printf("Praying to a Mana Altar...\n"); 201 | player.attributes.maxMana += 100; 202 | player.attributes.curMana = player.attributes.maxMana; 203 | 204 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 205 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-30, ALERT_MESSAGE_DEF_Y, "You pray to the Altar\nand your mana increases.", 3.0f, 1.0f); 206 | } 207 | else if (strcmp(data, "SPELLPOWER") == 0) 208 | { 209 | printf("Praying to a Spell Power Altar...\n"); 210 | player.attributes.spellPower += 0.5f; 211 | 212 | alertMessage_t* mess = (alertMessage_t*)malloc(sizeof(alertMessage_t)); 213 | R_QueueAlertMessage(mess, ALERT_MESSAGE_DEF_X-30, ALERT_MESSAGE_DEF_Y, "You pray to the Altar\nand your spell power increases.", 3.0f, 1.0f); 214 | } 215 | } 216 | 217 | //------------------------------------- 218 | // Changes the SkyID 219 | //------------------------------------- 220 | void D_CallbackTheFrozenEndFinalEncounter(char* data) 221 | { 222 | currentMap.skyID = 2; 223 | currentMap.fogColorR = 0; 224 | currentMap.fogColorG = 0; 225 | currentMap.fogColorB = 0; 226 | currentMap.fogColor.r = 0; 227 | currentMap.fogColor.g = 0; 228 | currentMap.fogColor.b = 0; 229 | } -------------------------------------------------------------------------------- /src/Engine/D_ObjectsCallbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECTS_CALLBACKS_H_INCLUDED 2 | #define OBJECTS_CALLBACKS_H_INCLUDED 3 | 4 | //------------------------------------- 5 | // Changes the map to the parameter 6 | //------------------------------------- 7 | void D_CallbackChangeMap(char* data); 8 | 9 | //------------------------------------- 10 | // Picks the passed object 11 | //------------------------------------- 12 | void D_CallbackPickup(char* data); 13 | 14 | //------------------------------------- 15 | // Climbs the ladder up 16 | //------------------------------------- 17 | void D_CallbackLadder(char* data); 18 | 19 | //------------------------------------- 20 | // Climbs the ladder down 21 | //------------------------------------- 22 | void D_CallbackLadderDown(char* data); 23 | 24 | //------------------------------------- 25 | // Activates an altar 26 | //------------------------------------- 27 | void D_CallbackUseAltar(char* data); 28 | 29 | //------------------------------------- 30 | // Changes the SkyID and Fog Settings for Frozen End 31 | //------------------------------------- 32 | void D_CallbackTheFrozenEndFinalEncounter(char* data); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/Engine/G_AI.h: -------------------------------------------------------------------------------- 1 | #ifndef AI_H_INCLUDED 2 | #define AI_H_INCLUDED 3 | 4 | #include "D_AssetsManager.h" 5 | #include "U_DataTypes.h" 6 | 7 | #define AI_STOP_DISTANCE 90.0f 8 | #define AI_MELEE_ATTACK_DISTANCE 100.0f 9 | 10 | #define AI_SPELL_ATTACK_DISTANCE 600.0f 11 | 12 | 13 | // Dynamic AI list 14 | extern dynamicSprite_t* allDynamicSprites[OBJECTARRAY_DEFAULT_SIZE_HIGH]; 15 | extern unsigned int allDynamicSpritesLength; 16 | 17 | //------------------------------------- 18 | // Initializes the AI 19 | //------------------------------------- 20 | void G_AIInitialize(dynamicSprite_t* cur, int level, int spriteID, int x, int y); 21 | 22 | //------------------------------------- 23 | // Updates all AI entities 24 | //------------------------------------- 25 | void G_AIUpdate(void); 26 | 27 | //------------------------------------- 28 | // Sets an AI to be dead 29 | //------------------------------------- 30 | void G_AIDie(dynamicSprite_t* cur); 31 | 32 | //------------------------------------- 33 | // Plays an animation only once 34 | //------------------------------------- 35 | void G_AIPlayAnimationOnce(dynamicSprite_t* cur, objectanimationsID_e animID); 36 | 37 | //------------------------------------- 38 | // Sets an animation to be played in loop 39 | //------------------------------------- 40 | void G_AIPlayAnimationLoop(dynamicSprite_t* cur, objectanimationsID_e animID); 41 | 42 | //------------------------------------- 43 | // Damages the AI entity 44 | //------------------------------------- 45 | void G_AITakeDamage(dynamicSprite_t* cur, float amount, bool setAggro); 46 | 47 | //------------------------------------- 48 | // True if the AI can attack 49 | //------------------------------------- 50 | bool G_AICanAttack(dynamicSprite_t* cur); 51 | 52 | //------------------------------------- 53 | // Damages the player 54 | //------------------------------------- 55 | void G_AIAttackPlayer(dynamicSprite_t* cur); 56 | 57 | 58 | void G_AICheckPuddleDamage(dynamicSprite_t* ai); 59 | 60 | void G_FreeDynamicSprite(dynamicSprite_t* ai); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/Engine/G_AIBehaviour.h: -------------------------------------------------------------------------------- 1 | #ifndef AIBEHAVIOUR_H_INCLUDED 2 | #define AIBEHAVIOUR_H_INCLUDED 3 | 4 | #include "D_AssetsManager.h" 5 | #include "U_DataTypes.h" 6 | 7 | #define DISPLAY_POS_SMOOTH_SPEED 1.0f 8 | 9 | // AI Specific, allows to modify the AI Behaviour for each enemy 10 | void G_AI_BehaviourMeeleEnemy(dynamicSprite_t* cur); 11 | void G_AI_BehaviourCasterEnemy(dynamicSprite_t* cur); 12 | void G_AI_BehaviourSkeletonLord(dynamicSprite_t* cur); 13 | void G_AI_BehaviourMorgathulTheKeeper(dynamicSprite_t* cur); 14 | void G_AI_BehaviourMorgathulCopy(dynamicSprite_t* cur); 15 | void G_AI_BehaviourTheFrozenLord(dynamicSprite_t* cur); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/Engine/G_Game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H_INCLUDED 2 | #define GAME_H_INCLUDED 3 | 4 | #include "G_Player.h" 5 | #include "R_Rendering.h" 6 | #include "U_Timer.h" 7 | #include "T_TextRendering.h" 8 | #include "../Network/packet.h" 9 | 10 | // DOOR DEFINE 11 | #define DOOR_FULLY_CLOSED 64.0f 12 | #define DOOR_FULLY_OPENED 2.0f 13 | #define DOOR_OPEN_SPEED 100.0f 14 | #define DOOR_CLOSE_SPEED 100.0f 15 | 16 | #define N_FRAMES_SKIP_FOR_DISPLAY 20 17 | 18 | // Game Timer 19 | extern Timer* gameTimer; 20 | extern Timer* playerUpdatePacketsTimer; // times the send of player's updates 21 | 22 | // Current Game Time 23 | extern double curTime; 24 | 25 | // Game Time at last tick 26 | extern double oldTime; 27 | 28 | // Doors 29 | extern int doorstateLevel0[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 30 | extern int doorstateLevel1[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 31 | extern int doorstateLevel2[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 32 | 33 | extern float doorpositionsLevel0[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 34 | extern float doorpositionsLevel1[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 35 | extern float doorpositionsLevel2[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 36 | 37 | extern projectileNode_t* projectilesHead; 38 | extern projectileNode_t* explodingProjectilesHead; 39 | 40 | extern mappudlle_t* activeMapPuddlesHead; 41 | 42 | // Chat 43 | extern incomingchatmessage_t* chatMsgsHead; 44 | extern textfield_t chatField; 45 | 46 | extern bool showFPS; 47 | extern float readOnlyFPS; // to write FPS on screen 48 | extern char fpsText[16]; 49 | extern int frameCountForFPSDisplay; // to update readOnlyFPS not everyframe, but every x frames 50 | 51 | 52 | //------------------------------------- 53 | // Initialize game related stuff 54 | //------------------------------------- 55 | void G_InitGame(void); 56 | 57 | //------------------------------------- 58 | // Loop of the application 59 | //------------------------------------- 60 | void G_GameLoop(void); 61 | 62 | //------------------------------------- 63 | // Loop of the game while in menu (GSTATE_MENU) 64 | //------------------------------------- 65 | void G_StateMenuLoop(void); 66 | 67 | //------------------------------------- 68 | // Loop of the game while in game (GSTATE_GAME) 69 | //------------------------------------- 70 | void G_StateGameLoop(void); 71 | 72 | //------------------------------------- 73 | // Update Doors by moving them in base of their timer 74 | //------------------------------------- 75 | void G_UpdateDoors(void); 76 | 77 | //------------------------------------- 78 | // Loads the map with ID mapID 79 | //------------------------------------- 80 | void G_ChangeMap(char* mapID); 81 | 82 | //------------------------------------- 83 | // Updates the spawned projectiles 84 | //------------------------------------- 85 | void G_UpdateProjectiles(void); 86 | 87 | //------------------------------------- 88 | // Spawns a new projectile 89 | //------------------------------------- 90 | void G_SpawnProjectile(uint32_t networkID, int id, float angle, int level, float posx, float posy, float posz, float verticalAngle, bool isOfPlayer, dynamicSprite_t* aiOwner, bool isNetworkInstance); 91 | 92 | //------------------------------------- 93 | // Updates the Puddles 94 | //------------------------------------- 95 | void G_UpdateMapPuddles(void); 96 | 97 | //------------------------------------- 98 | // Spawns a new Puddle 99 | //------------------------------------- 100 | packedpuddle_t G_SpawnMapPuddle(int networkID, int gridX, int gridY, bool damagesAI, bool damagesPlayer, float damage, int duration, int level, int newFloorID, bool isNetworkInstance); 101 | 102 | 103 | //------------------------------------- 104 | // Spawns a new incoming chat message 105 | //------------------------------------- 106 | void G_SpawnIncomingChatMessage(int fontID, int x, int y, char* msg, float duration, float scale); 107 | 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/Engine/G_MainMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_MENU_H_INCLUDED 2 | #define MAIN_MENU_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "SDL.h" 7 | #include "T_TextRendering.h" 8 | 9 | // How off the text the cursor should be 10 | #define CURSOR_X_OFFSET 35 11 | 12 | typedef enum 13 | { 14 | MENU_START = 0, 15 | MENU_DEATH, 16 | MENU_OPTIONS, 17 | MENU_END_GAME, 18 | MENU_ABOUT, 19 | MENU_HOSTGAME, 20 | MENU_JOINGAME, 21 | MENU_INLOBBY, 22 | MENU_DISCONNECTED, 23 | MENU_SETUPHOSTGAME, 24 | MENU_SETUPJOINGAME 25 | } menusIDs_e; 26 | 27 | 28 | typedef struct menuelement_s 29 | { 30 | char* text; 31 | SDL_Rect box; 32 | void (*OnClick)(void); 33 | } menuelement_t; 34 | 35 | typedef struct menu_s 36 | { 37 | int ID; 38 | menuelement_t* elements; 39 | int elementsLength; 40 | menuelement_t* selectedElement; 41 | 42 | // Text fields 43 | int textFieldsLength; 44 | textfield_t* textFields; 45 | } menu_t; 46 | 47 | 48 | extern menu_t* currentMenu; 49 | 50 | // Menus are defined at compile-time 51 | extern menuelement_t MainMenuElements[]; 52 | extern menu_t MainMenu; 53 | 54 | extern menuelement_t DeathMenuElements[]; 55 | extern menu_t DeathMenu; 56 | 57 | extern menuelement_t OptionsMenuElements[]; 58 | extern menu_t OptionsMenu; 59 | 60 | extern menuelement_t EndGameMenuElements[]; 61 | extern menu_t EndGameMenu; 62 | 63 | extern menuelement_t AboutMenuElements[]; 64 | extern menu_t AboutMenu; 65 | 66 | // TomentOnline 67 | extern menuelement_t HostGameMenuElements[]; 68 | extern menu_t HostGameMenu; 69 | 70 | extern menuelement_t JoinGameMenuElements[]; 71 | extern menu_t JoinGameMenu; 72 | 73 | extern menuelement_t InLobbyMenuElements[]; 74 | extern menu_t InLobbyMenu; 75 | 76 | extern menuelement_t DisconnectedMenuElements[]; 77 | extern menu_t DisconnectedMenu; 78 | 79 | extern bool isEditingTextField; 80 | extern textfield_t * textFieldEditing; 81 | 82 | //------------------------------------- 83 | // Initialize the Main Menu state 84 | //------------------------------------- 85 | void G_InitMainMenu(); 86 | 87 | //------------------------------------- 88 | // Set the MenuState 89 | //------------------------------------- 90 | void G_SetMenu(menu_t* menu); 91 | 92 | //------------------------------------- 93 | // Renders what's gonna be behind the current menu 94 | //------------------------------------- 95 | void G_RenderCurrentMenuBackground(void); 96 | 97 | //------------------------------------- 98 | // Render menu routine, renders the elements of any menu 99 | //------------------------------------- 100 | void G_RenderCurrentMenu(void); 101 | 102 | //------------------------------------- 103 | // Manages Menu Input routine 104 | //------------------------------------- 105 | void G_InMenuInputHandling(SDL_Event* e); 106 | 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/Engine/G_Pathfinding.c: -------------------------------------------------------------------------------- 1 | #include "G_Pathfinding.h" 2 | #include "R_Rendering.h" 3 | #include "SDL.h" 4 | #include "G_Player.h" 5 | #include "G_AI.h" 6 | #include "../Online/O_Game.h" 7 | 8 | pathnode_t frontier[MAP_HEIGHT*MAP_WIDTH+2]; 9 | unsigned int frontierLength = 0; 10 | bool visited[MAP_HEIGHT][MAP_WIDTH]; 11 | 12 | // Adds a Node to be added to the frontier 13 | static void I_InsertNode(int level, pathnode_t* node, int gridx, int gridy, int f, int g, int h, pathnode_t* parent, bool debug, dynamicSprite_t* entity); 14 | 15 | // Adds the nodes adiacent to a Node to be added to the frontier 16 | static void I_AddAdiacentNodes(int level, int oGridX, int oGridY, pathnode_t* parent, bool debug, dynamicSprite_t* entity); 17 | 18 | path_t G_PerformPathfinding(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos, dynamicSprite_t* entity) //entity performing pathfinding 19 | { 20 | // Path to return 21 | path_t path; 22 | 23 | bool pathExists = false; 24 | 25 | // Reset state 26 | memset(visited, false, MAP_HEIGHT*MAP_WIDTH*sizeof(bool)); 27 | frontierLength = 0; 28 | 29 | // Insert the start node and its adiacents 30 | I_InsertNode(level, &frontier[0], gridPos.x, gridPos.y, 0, 0, 0, NULL, false, entity); 31 | I_AddAdiacentNodes(level, gridPos.x, gridPos.y, &frontier[0], false, entity); 32 | 33 | // Check element keeps track of the elements that have been checked 34 | int checkElement = 1; 35 | 36 | // While there are elements that haven't been checked 37 | while(checkElement < frontierLength) 38 | { 39 | // Get that element 40 | pathnode_t* cur = &frontier[checkElement]; 41 | 42 | // Check If the target was found 43 | if(cur->gridPos.x == gridTargetPos.x && cur->gridPos.y == gridTargetPos.y) 44 | { 45 | // If so, path exists 46 | pathExists = true; 47 | break; 48 | } 49 | 50 | // Otherwise we'll check all the other adiacent nodes 51 | I_AddAdiacentNodes(level, cur->gridPos.x, cur->gridPos.y, cur, false, entity); 52 | checkElement++; 53 | } 54 | 55 | // Once the BFS ended, if the target was found 56 | if(pathExists) 57 | { 58 | // Create the path 59 | path.isValid = true; 60 | pathnode_t* cur; 61 | 62 | // Last element of frontier is the goal, which is the first element of the path (path is reversed) 63 | cur = &frontier[checkElement]; 64 | path.nodes[0] = cur; 65 | path.nodesLength = 1; 66 | 67 | // Track back to the entity who performed the pathfinding 68 | while((cur = cur->parent) != &frontier[0]) 69 | { 70 | path.nodes[path.nodesLength] = cur; 71 | path.nodesLength++; 72 | } 73 | 74 | // Return it 75 | return path; 76 | } 77 | else 78 | { 79 | // Path is invalid 80 | path.isValid = false; 81 | return path; 82 | } 83 | } 84 | 85 | path_t G_PerformPathfindingDebug(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos) 86 | { 87 | // Path to return 88 | path_t path; 89 | 90 | bool pathExists = false; 91 | 92 | // Reset state 93 | memset(visited, false, MAP_HEIGHT*MAP_WIDTH*sizeof(bool)); 94 | frontierLength = 0; 95 | 96 | // Insert the start node and its adiacents 97 | I_InsertNode(level, &frontier[0], gridPos.x, gridPos.y, 0, 0, 0, NULL, true, NULL); 98 | I_AddAdiacentNodes(level, gridPos.x, gridPos.y, &frontier[0], true, NULL); 99 | 100 | // Draw start 101 | SDL_Rect curRect; 102 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 103 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 104 | curRect.x = gridPos.x * TILE_SIZE / MINIMAP_DIVIDER; 105 | curRect.y = gridPos.y * TILE_SIZE / MINIMAP_DIVIDER; 106 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 0, 0, 255), &curRect); 107 | 108 | // Check element keeps track of the elements that have been checked 109 | int checkElement = 1; 110 | 111 | // While there are elements that haven't been checked 112 | while(checkElement < frontierLength) 113 | { 114 | // Get that element 115 | pathnode_t* cur = &frontier[checkElement]; 116 | 117 | // Check If the target was found 118 | if(cur->gridPos.x == gridTargetPos.x && cur->gridPos.y == gridTargetPos.y) 119 | { 120 | // If so, path exists 121 | pathExists = true; 122 | 123 | // Draw the path 124 | // Set X and Y 125 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 126 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 127 | curRect.x = gridTargetPos.x * TILE_SIZE / MINIMAP_DIVIDER; 128 | curRect.y = gridTargetPos.y * TILE_SIZE / MINIMAP_DIVIDER; 129 | // If it is an empty space 130 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 255, 255, 0), &curRect); 131 | SDL_UpdateWindowSurface(application.win); 132 | SDL_Delay(2000); 133 | 134 | // Stop looking 135 | break; 136 | } 137 | 138 | // Otherwise we'll check all the other adiacent nodes 139 | I_AddAdiacentNodes(level, cur->gridPos.x, cur->gridPos.y, cur, true, NULL); 140 | checkElement++; 141 | } 142 | 143 | // Once the BFS ended, if the target was found 144 | if(pathExists) 145 | { 146 | // Create the path 147 | path.isValid = true; 148 | pathnode_t* cur; 149 | 150 | // Last element of frontier is the goal, which is the first element of the path (path is reversed) 151 | cur = &frontier[checkElement]; 152 | path.nodes[0] = cur; 153 | int pathLength = 1; 154 | 155 | // Track back to the entity who performed the pathfinding 156 | while((cur = cur->parent) != &frontier[0]) 157 | { 158 | path.nodes[pathLength] = cur; 159 | pathLength++; 160 | } 161 | 162 | // There you have it 163 | printf("Path length: %d\n", pathLength); 164 | 165 | // Draw path 166 | for(int i = 0; i < pathLength; i++) 167 | { 168 | // Set X and Y 169 | curRect.w = (HALF_TILE_SIZE) / MINIMAP_DIVIDER; 170 | curRect.h = (HALF_TILE_SIZE) / MINIMAP_DIVIDER; 171 | curRect.x = (path.nodes[i]->gridPos.x) * TILE_SIZE / MINIMAP_DIVIDER +1; 172 | curRect.y = (path.nodes[i]->gridPos.y) * TILE_SIZE / MINIMAP_DIVIDER +1; 173 | 174 | 175 | // If it is an empty space 176 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 255, 0, 255), &curRect); 177 | SDL_UpdateWindowSurface(application.win); 178 | SDL_Delay(20); 179 | } 180 | 181 | SDL_Delay(4000); 182 | 183 | // Return it 184 | return path; 185 | } 186 | else 187 | { 188 | // Path is invalid 189 | path.isValid = false; 190 | return path; 191 | } 192 | } 193 | 194 | void I_InsertNode(int level, pathnode_t* node, int gridx, int gridy, int f, int g, int h, pathnode_t* parent, bool debug, dynamicSprite_t* entity) 195 | { 196 | dynamicSprite_t* entityAtNode = G_GetFromDynamicSpriteMap(level, gridy, gridx); 197 | 198 | if(visited[gridy][gridx] || G_CheckCollisionMap(level, gridy, gridx) > 0 || G_CheckDynamicSpriteMap(level, gridy, gridx) && G_GetFromDynamicSpriteMap(level, gridy, gridx) != entity && G_GetFromDynamicSpriteMap(level, gridy, gridx) != &otherPlayerObject) 199 | return; 200 | 201 | node->gridPos.x = gridx; 202 | node->gridPos.y = gridy; 203 | node->f = f; 204 | node->g = g; 205 | node->h = h; 206 | node->parent = parent; 207 | 208 | visited[gridy][gridx] = true; 209 | 210 | frontierLength++; 211 | 212 | if(debug) 213 | { 214 | SDL_Rect curRect; 215 | // Set X and Y 216 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 217 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 218 | curRect.x = gridx * TILE_SIZE / MINIMAP_DIVIDER; 219 | curRect.y = gridy * TILE_SIZE / MINIMAP_DIVIDER; 220 | 221 | // If it is an empty space 222 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 0, 255, 0), &curRect); 223 | SDL_Delay(5); 224 | SDL_UpdateWindowSurface(application.win); 225 | } 226 | } 227 | 228 | void I_AddAdiacentNodes(int level, int oGridX, int oGridY, pathnode_t* parent, bool debug, dynamicSprite_t* entity) 229 | { 230 | // Top 231 | if(oGridY-1 >= 0 && !visited[oGridY-1][oGridX]) 232 | I_InsertNode(level, &frontier[frontierLength], oGridX, oGridY-1, 0,0,0, parent, debug, entity); 233 | 234 | // Bottom 235 | if(oGridY+1 < MAP_HEIGHT && !visited[oGridY+1][oGridX]) 236 | I_InsertNode(level, &frontier[frontierLength], oGridX, oGridY+1, 0,0,0, parent, debug, entity); 237 | 238 | // Left 239 | if(oGridX-1 >= 0 && !visited[oGridY][oGridX-1]) 240 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY, 0,0,0, parent, debug, entity); 241 | 242 | // Right 243 | if(oGridX+1 < MAP_WIDTH && !visited[oGridY][oGridX+1]) 244 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY, 0,0,0, parent, debug, entity); 245 | 246 | 247 | // Allow Diagonal movement and prevent corner cutting 248 | // Top Left 249 | if(oGridY-1 >= 0 && oGridX-1 >= 0 && !visited[oGridY-1][oGridX-1]) 250 | if(G_CheckCollisionMap(level, oGridY-1, oGridX-1+1) == 0 && // check right 251 | G_CheckCollisionMap(level, oGridY-1+1, oGridX-1) == 0) // check under 252 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY-1, 0,0,0, parent, debug, entity); 253 | 254 | // Top Right 255 | if(oGridY-1 >= 0 && oGridX+1 < MAP_WIDTH && !visited[oGridY-1][oGridX+1]) 256 | if(G_CheckCollisionMap(level, oGridY-1+1, oGridX+1) == 0 && // check under 257 | G_CheckCollisionMap(level, oGridY-1, oGridX+1-1) == 0) // left 258 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY-1, 0,0,0, parent, debug, entity); 259 | 260 | // Bottom Left 261 | if(oGridY+1 < MAP_HEIGHT && oGridX-1 >= 0 && !visited[oGridY+1][oGridX-1]) 262 | if(G_CheckCollisionMap(level, oGridY+1, oGridX-1+1) == 0 && // check right 263 | G_CheckCollisionMap(level, oGridY+1-1, oGridX-1) == 0) // check up 264 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY+1, 0,0,0, parent, debug, entity); 265 | 266 | // Bottom Right 267 | if(oGridY+1 < MAP_HEIGHT && oGridX+1 < MAP_WIDTH && !visited[oGridY+1][oGridX+1]) 268 | if(G_CheckCollisionMap(level, oGridY+1-1, oGridX+1) == 0 && // check under 269 | G_CheckCollisionMap(level, oGridY+1, oGridX+1-1) == 0) // check left 270 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY+1, 0,0,0, parent, debug, entity); 271 | } 272 | 273 | bool G_CheckDynamicSpriteMap(int level, int y, int x) 274 | { 275 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 276 | { 277 | switch(level) 278 | { 279 | case 0: 280 | return (currentMap.dynamicSpritesLevel0[y][x]) != NULL ? currentMap.dynamicSpritesLevel0[y][x] : NULL; 281 | 282 | case 1: 283 | return (currentMap.dynamicSpritesLevel1[y][x]) != NULL ? currentMap.dynamicSpritesLevel1[y][x] : NULL; 284 | 285 | case 2: 286 | return (currentMap.dynamicSpritesLevel2[y][x]) != NULL ? currentMap.dynamicSpritesLevel2[y][x] : NULL; 287 | 288 | default: 289 | //printf("WARNING! Level get was out of max/min level size\n"); 290 | return false; 291 | } 292 | } 293 | else 294 | { 295 | return false; 296 | } 297 | } 298 | 299 | dynamicSprite_t* G_GetFromDynamicSpriteMap(int level, int y, int x) 300 | { 301 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 302 | { 303 | switch(level) 304 | { 305 | case 0: 306 | return (currentMap.dynamicSpritesLevel0[y][x]); 307 | 308 | case 1: 309 | return (currentMap.dynamicSpritesLevel1[y][x]); 310 | 311 | case 2: 312 | return (currentMap.dynamicSpritesLevel2[y][x]); 313 | 314 | default: 315 | //printf("WARNING! Level get was out of max/min level size\n"); 316 | return NULL; 317 | } 318 | } 319 | else 320 | { 321 | return NULL; 322 | } 323 | } 324 | 325 | 326 | void G_ClearFromDynamicSpriteMap(int level, int y, int x) 327 | { 328 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 329 | { 330 | switch(level) 331 | { 332 | case 0: 333 | (currentMap.dynamicSpritesLevel0[y][x]) = NULL; 334 | break; 335 | 336 | case 1: 337 | (currentMap.dynamicSpritesLevel1[y][x]) = NULL; 338 | break; 339 | 340 | case 2: 341 | (currentMap.dynamicSpritesLevel2[y][x]) = NULL; 342 | break; 343 | 344 | default: 345 | //printf("WARNING! Level get was out of max/min level size\n"); 346 | break; 347 | } 348 | } 349 | } 350 | 351 | bool G_CheckDeadDynamicSpriteMap(int level, int y, int x) 352 | { 353 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 354 | { 355 | switch(level) 356 | { 357 | case 0: 358 | return (currentMap.deadDynamicSpritesLevel0[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel0[y][x] : NULL; 359 | 360 | case 1: 361 | return (currentMap.deadDynamicSpritesLevel1[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel1[y][x] : NULL; 362 | 363 | case 2: 364 | return (currentMap.deadDynamicSpritesLevel2[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel2[y][x] : NULL; 365 | 366 | default: 367 | //printf("WARNING! Level get was out of max/min level size\n"); 368 | return false; 369 | } 370 | } 371 | else 372 | { 373 | return false; 374 | } 375 | } 376 | 377 | dynamicSprite_t* G_GetFromDeadDynamicSpriteMap(int level, int y, int x) 378 | { 379 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 380 | { 381 | switch(level) 382 | { 383 | case 0: 384 | return (currentMap.deadDynamicSpritesLevel0[y][x]); 385 | 386 | case 1: 387 | return (currentMap.deadDynamicSpritesLevel1[y][x]); 388 | 389 | case 2: 390 | return (currentMap.deadDynamicSpritesLevel2[y][x]); 391 | 392 | default: 393 | //printf("WARNING! Level get was out of max/min level size\n"); 394 | return NULL; 395 | } 396 | } 397 | else 398 | { 399 | return NULL; 400 | } 401 | } 402 | 403 | 404 | void G_ClearFromDeadDynamicSpriteMap(int level, int y, int x) 405 | { 406 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 407 | { 408 | switch(level) 409 | { 410 | case 0: 411 | (currentMap.deadDynamicSpritesLevel0[y][x]) = NULL; 412 | break; 413 | 414 | case 1: 415 | (currentMap.deadDynamicSpritesLevel1[y][x]) = NULL; 416 | break; 417 | 418 | case 2: 419 | (currentMap.deadDynamicSpritesLevel2[y][x]) = NULL; 420 | break; 421 | 422 | default: 423 | //printf("WARNING! Level get was out of max/min level size\n"); 424 | break; 425 | } 426 | } 427 | } 428 | 429 | void G_AddToDeadDynamicSpriteMap(dynamicSprite_t* cur, int level, int y, int x) 430 | { 431 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT && cur != NULL) 432 | { 433 | switch(level) 434 | { 435 | case 0: 436 | (currentMap.deadDynamicSpritesLevel0[y][x]) = cur; 437 | break; 438 | 439 | case 1: 440 | (currentMap.deadDynamicSpritesLevel1[y][x]) = cur; 441 | break; 442 | 443 | case 2: 444 | (currentMap.deadDynamicSpritesLevel2[y][x]) = cur; 445 | break; 446 | 447 | default: 448 | //printf("WARNING! Level get was out of max/min level size\n"); 449 | break; 450 | } 451 | } 452 | } -------------------------------------------------------------------------------- /src/Engine/G_Pathfinding.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHFINDING_H_INCLUDED 2 | #define PATHFINDING_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "M_Map.h" 7 | #include "U_DataTypes.h" 8 | 9 | // Pathfinding Related data 10 | extern pathnode_t frontier[MAP_HEIGHT*MAP_WIDTH+2]; 11 | extern unsigned int frontierLength; 12 | extern bool visited[MAP_HEIGHT][MAP_WIDTH]; 13 | 14 | //---------------------------------- 15 | // Returns if the space identified by the parameters is empty or not in the dynamic sprite maps 16 | //---------------------------------- 17 | extern bool G_CheckDynamicSpriteMap(int level, int y, int x); 18 | 19 | //---------------------------------- 20 | // Gets the dynamicSprite identified by the parameter 21 | //---------------------------------- 22 | extern dynamicSprite_t* G_GetFromDynamicSpriteMap(int level, int y, int x); 23 | 24 | //---------------------------------- 25 | // Clears from the Dynamic Sprite Maps 26 | //---------------------------------- 27 | extern void G_ClearFromDynamicSpriteMap(int level, int y, int x); 28 | 29 | //---------------------------------- 30 | // Adds an entity in the Dead Dynamic Sprites Map 31 | //---------------------------------- 32 | extern void G_AddToDeadDynamicSpriteMap(dynamicSprite_t* cur, int level, int y, int x); 33 | 34 | //---------------------------------- 35 | // Returns if the space identified by the parameters is empty or not in the dynamic sprite maps 36 | //---------------------------------- 37 | extern bool G_CheckDeadDynamicSpriteMap(int level, int y, int x); 38 | 39 | //---------------------------------- 40 | // Gets the dynamicSprite identified by the parameter 41 | //---------------------------------- 42 | extern dynamicSprite_t* G_GetFromDeadDynamicSpriteMap(int level, int y, int x); 43 | 44 | //---------------------------------- 45 | // Clears from the Dead Dynamic Sprite Maps 46 | //---------------------------------- 47 | extern void G_ClearFromDeadDynamicSpriteMap(int level, int y, int x); 48 | 49 | //---------------------------------- 50 | // Performs pathfinding and returns a path 51 | //---------------------------------- 52 | extern path_t G_PerformPathfinding(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos, dynamicSprite_t* entity); 53 | 54 | //---------------------------------- 55 | // Performs the pathfinding between the first dynamic sprite and the player and debugs the info in the minimap, called via F2 (Player.c) 56 | //---------------------------------- 57 | extern path_t G_PerformPathfindingDebug(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos); 58 | 59 | #endif -------------------------------------------------------------------------------- /src/Engine/G_Player.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H_INCLUDED 2 | #define PLAYER_H_INCLUDED 3 | 4 | #include "A_Application.h" 5 | #include "G_Game.h" 6 | #include "U_DataTypes.h" 7 | #include "D_AssetsManager.h" 8 | #include "G_Skills.h" 9 | 10 | // Minimap Player 11 | #define PLAYER_WIDTH 16 12 | #define PLAYER_HEIGHT 16 13 | 14 | // To fix the player position to be at the center instead of the upper corner, this is WIDTH/2 15 | #define PLAYER_CENTER_FIX 8 16 | 17 | // Speed of the player 18 | #define PLAYER_SPEED 250.0f 19 | 20 | // Rotation speed of the player 21 | #define PLAYER_ROT_SPEED 0.5f 22 | 23 | // Speed of the vertical head motion 24 | #define PLAYER_VERTICAL_HEAD_MOVEMENT_SPEED 120.0f 25 | 26 | // Default Player's starting rotation 27 | #define PLAYER_STARTING_ROT M_PI 28 | 29 | // The minimum distance the player can get to the wall 30 | #define PLAYER_MIN_DIST_TO_WALL 30 31 | 32 | // The light that the player will always carry, no matter the current map 33 | #define PLAYER_POINT_LIGHT_INTENSITY 10 34 | 35 | // The attack cone (in grids) of the player 36 | #define ATTACK_CONE_SIZE 7 37 | 38 | // The Max thresold of difference of angle (used to calculate if the AI we're trying to attack is in front of us) 39 | #define ATTACK_CONE_MAX_DIFF 25 40 | 41 | // Amount of seconds the crosshair will turn red when the player hits an AI 42 | #define CROSSHAIR_HIT_TIME_SECONDS 0.35f 43 | 44 | // Climbing speed 45 | #define PLAYER_CLIMBING_LADDER_UP_SPEED 24 46 | #define PLAYER_CLIMBING_LADDER_DOWN_SPEED 48 47 | 48 | // State of the player 49 | typedef enum playerState_e 50 | { 51 | PSTATE_IDLE = 0, 52 | PSTATE_ATTACKING1, 53 | PSTATE_CASTSPELL, 54 | PSTATE_CLIMBING_LADDER, 55 | PSTATE_DOINGSKILL1, 56 | PSTATE_DOINGSKILL2, 57 | PSTATE_DOINGSKILL3 58 | } playerState_e; 59 | 60 | // Attacks the player can perform 61 | typedef enum playerAttacks_e 62 | { 63 | P_ATTACK_MELEE_1 = 0, 64 | P_ATTACK_CAST_FIREBALL1 65 | } playerAttacks_e; 66 | 67 | // Spells (shared with AI) 68 | typedef enum playerSpells_e 69 | { 70 | SPELL_NULL = 0, 71 | SPELL_FIREBALL1 = 4, // Needs to be the same as the corresponding sprite that will be instantiated as the projectile 72 | SPELL_ICEDART1 = 8, 73 | SPELL_CONCENTRATED_HEAL = 24, 74 | SPELL_SWORD_PROJECTILE = 25, 75 | SPELL_MORGATHUL_ORB = 27, 76 | SPELL_ICE_BLAST = 33 77 | } playerSpells_e; 78 | 79 | typedef struct player_s 80 | { 81 | vector2_t position; // abs SDL position 82 | vector2_t centeredPos; // position centered for the player's width and height 83 | float z; // z pos 84 | int level; // The current floor level the player is at 85 | 86 | circle_t collisionCircle; 87 | 88 | vector2Int_t gridPosition; // position of the player in the tilemap 89 | vector2Int_t inFrontGridPosition; // The grid pos of the cell in front of the player 90 | vector2Int_t attackConeGridPos[ATTACK_CONE_SIZE]; // The grid pos of the cells in front of the player that represents the cone of attack, AI that will be in these cells will be checked for hit 91 | 92 | vector2_t deltaPos; // used to calculate movements and collision 93 | 94 | // Minimap rapresentaton of the player 95 | SDL_Surface* surface; 96 | SDL_Rect surfaceRect; 97 | 98 | // Rot of the player in radians 99 | float angle; 100 | float verticalHeadMovement; 101 | 102 | // Loaded at runtime 103 | bool hasBeenInitialized; 104 | 105 | // The grid the player starts in (loaded from map) 106 | int startingGridX; 107 | int startingGridY; 108 | float startingRot; 109 | 110 | playerState_e state; 111 | playerFPID_e curWeapon; 112 | playerSpells_e curSpell; 113 | 114 | entityattributes_t attributes; 115 | 116 | // Animation 117 | Timer* animTimer; 118 | bool animPlay; 119 | int animFrame; 120 | bool animPlayOnce; 121 | int playedFrames; 122 | 123 | // Has to attack/cast a spell, used to not attack more than once while playing the animation 124 | bool hasToCast; 125 | bool hasCasted; 126 | 127 | // Timer used by the crosshair to change its color when the player hits an enemy 128 | bool crosshairHit; 129 | Timer* crosshairTimer; 130 | 131 | // Weapons 132 | float weaponDistance; 133 | bool hasAxe; 134 | bool hasGreatsword; 135 | bool hasMace; 136 | 137 | // Spells 138 | bool hasFireball; 139 | bool hasIceDart; 140 | 141 | // Ladder 142 | bool hasToClimb; 143 | bool climbingUp; 144 | 145 | float climbingPosX; 146 | float climbingPosY; 147 | float climbingPosZ; 148 | 149 | bool isFightingBoss; 150 | dynamicSprite_t* bossFighting; 151 | 152 | // Skills 153 | skill_t skills[MAX_SKILLS_PER_CLASS]; 154 | bool isInvulnerable; // if true player cannot be damage (tank skill 2) 155 | } player_t; 156 | 157 | 158 | extern player_t player; 159 | 160 | //------------------------------------- 161 | // Initializes Player 162 | //------------------------------------- 163 | void G_InitPlayer(void); 164 | 165 | //------------------------------------- 166 | // Player's Tick 167 | //------------------------------------- 168 | void G_PlayerTick(void); 169 | 170 | //------------------------------------- 171 | // Handles Input from the player while reading the keyboard state 172 | //------------------------------------- 173 | void G_InGameInputHandling(const uint8_t* keyboardState); 174 | 175 | //------------------------------------- 176 | // Handles Input from the player while doing the Event Input Handling 177 | //------------------------------------- 178 | void G_InGameInputHandlingEvent(SDL_Event* e); 179 | 180 | //------------------------------------- 181 | // Checks the collision map at player's level and returns what found 182 | //------------------------------------- 183 | int G_CheckCollisionMap(int level, int y, int x); 184 | 185 | //------------------------------------- 186 | // Checks door state map at player's level and returns what found 187 | //------------------------------------- 188 | int G_GetDoorState(int level, int y, int x); 189 | 190 | //------------------------------------- 191 | // Sets door state map at player's level 192 | //------------------------------------- 193 | int G_SetDoorState(int level, int y, int x, doorstate_e state); 194 | 195 | //------------------------------------- 196 | // Checks door position map at player's level and returns what found 197 | //------------------------------------- 198 | float G_GetDoorPosition(int level, int y, int x); 199 | 200 | //------------------------------------- 201 | // Checks object T map at player's level and returns what found 202 | //------------------------------------- 203 | int G_GetFromObjectTMap(int level, int y, int x); 204 | 205 | //------------------------------------- 206 | // Sets a value of the object T map 207 | //------------------------------------- 208 | void G_SetObjectTMap(int level, int y, int x, int value); 209 | 210 | //------------------------------------- 211 | // Checks players collisions 212 | //------------------------------------- 213 | void G_PlayerCollisionCheck(); 214 | 215 | //------------------------------------- 216 | // Renders the player 217 | //------------------------------------- 218 | void G_PlayerRender(void); 219 | 220 | //------------------------------------- 221 | // Renders the player's UI 222 | //------------------------------------- 223 | void G_PlayerUIRender(void); 224 | 225 | //------------------------------------- 226 | // Plays an animation once (FP hands) 227 | //------------------------------------- 228 | void G_PlayerPlayAnimationOnce(objectanimationsID_e animID); 229 | 230 | //------------------------------------- 231 | // Returns if the player can attack 232 | //------------------------------------- 233 | bool G_PlayerCanAttack(void); 234 | 235 | //------------------------------------- 236 | // Makes the player take damage 237 | //------------------------------------- 238 | void G_PlayerTakeDamage(float dmg); 239 | 240 | //------------------------------------- 241 | // Drains player's mana 242 | //------------------------------------- 243 | void G_PlayerDrainMana(float amount); 244 | 245 | //------------------------------------- 246 | // Adds player's health 247 | //------------------------------------- 248 | void G_PlayerGainHealth(float amount); 249 | 250 | //------------------------------------- 251 | // Adds player's mana 252 | //------------------------------------- 253 | void G_PlayerGainMana(float amount); 254 | 255 | //------------------------------------- 256 | // Sets Player's Weapon 257 | //------------------------------------- 258 | void G_PlayerSetWeapon(playerFPID_e weaponID); 259 | 260 | //------------------------------------- 261 | // Sets Player's Spell 262 | //------------------------------------- 263 | void G_PlayerSetSpell(playerSpells_e spellID); 264 | 265 | //------------------------------------- 266 | // Makes player die 267 | //------------------------------------- 268 | void G_PlayerDeath(); 269 | 270 | void G_PlayerCheckPuddleDamage(void); 271 | 272 | #endif 273 | -------------------------------------------------------------------------------- /src/Engine/G_Skills.c: -------------------------------------------------------------------------------- 1 | #include "G_Skills.h" 2 | #include "G_Player.h" 3 | #include "../Network/netdef.h" 4 | #include "../Network/replication.h" 5 | #include "../Online/O_Game.h" 6 | 7 | void G_InitializeSkills(void) 8 | { 9 | switch(thisPlayer.selectedClass) 10 | { 11 | case CLASS_TANK: 12 | { 13 | // Shield slam 14 | player.skills[0].skillID = TANK_SKILL_SHIELD_SLAM; 15 | player.skills[0].cooldown = 5500; 16 | player.skills[0].timer = U_TimerCreateNew(); 17 | player.skills[0].manaConsumption = 15.0f; 18 | player.skills[0].timer->Start(player.skills[0].timer); 19 | 20 | // Shield block 21 | player.skills[1].skillID = TANK_SKILL_SHIELD_BLOCK; 22 | player.skills[1].cooldown = 20000; 23 | player.skills[1].timer = U_TimerCreateNew(); 24 | player.skills[1].manaConsumption = 60.0f; 25 | player.skills[1].timer->Start(player.skills[1].timer); 26 | 27 | // Consacration 28 | player.skills[2].skillID = TANK_SKILL_CONSACRATION; 29 | player.skills[2].cooldown = 10000; 30 | player.skills[2].timer = U_TimerCreateNew(); 31 | player.skills[2].manaConsumption = 40.0f; 32 | player.skills[2].timer->Start(player.skills[2].timer); 33 | break; 34 | } 35 | 36 | case CLASS_HEALER: 37 | { 38 | // Self heal 39 | player.skills[0].skillID = HEALER_SKILL_SELF_HEAL; 40 | player.skills[0].cooldown = 2500; 41 | player.skills[0].timer = U_TimerCreateNew(); 42 | player.skills[0].manaConsumption = 15.0f; 43 | player.skills[0].timer->Start(player.skills[0].timer); 44 | 45 | // Concentrated heal 46 | player.skills[1].skillID = HEALER_SKILL_CONCENTRATED_HEAL; 47 | player.skills[1].cooldown = 3000; 48 | player.skills[1].timer = U_TimerCreateNew(); 49 | player.skills[1].manaConsumption = 40.0f; 50 | player.skills[1].timer->Start(player.skills[1].timer); 51 | 52 | // Divine intervention 53 | player.skills[2].skillID = HEALER_SKILL_DIVINE_INTERVENTION; 54 | player.skills[2].cooldown = 20000; 55 | player.skills[2].timer = U_TimerCreateNew(); 56 | player.skills[2].manaConsumption = 100.0f; 57 | player.skills[2].timer->Start(player.skills[2].timer); 58 | 59 | break; 60 | } 61 | 62 | case CLASS_DPS: 63 | { 64 | // CheapShot slash 65 | player.skills[0].skillID = DPS_SKILL_CHEAPSHOT; 66 | player.skills[0].cooldown = 6000; 67 | player.skills[0].timer = U_TimerCreateNew(); 68 | player.skills[0].manaConsumption = 40.0f; 69 | player.skills[0].timer->Start(player.skills[0].timer); 70 | 71 | // Obliterate 72 | player.skills[1].skillID = DPS_SKILL_OBLITERATE; 73 | player.skills[1].cooldown = 10000; 74 | player.skills[1].timer = U_TimerCreateNew(); 75 | player.skills[1].manaConsumption = 20.0f; 76 | player.skills[1].timer->Start(player.skills[1].timer); 77 | 78 | // Split 79 | player.skills[2].skillID = DPS_SKILL_SPLIT; 80 | player.skills[2].cooldown = 20000; 81 | player.skills[2].timer = U_TimerCreateNew(); 82 | player.skills[2].manaConsumption = 80.0f; 83 | player.skills[2].timer->Start(player.skills[2].timer); 84 | break; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Engine/G_Skills.h: -------------------------------------------------------------------------------- 1 | #ifndef SKILLS_H_INCLUDED 2 | #define SKILLS_H_INCLUDED 3 | 4 | #include "U_DataTypes.h" 5 | 6 | #define MAX_SKILLS_PER_CLASS 3 7 | 8 | // Skills (class specific) 9 | typedef enum skillIDs_e 10 | { 11 | TANK_SKILL_SHIELD_SLAM, 12 | TANK_SKILL_SHIELD_BLOCK, 13 | TANK_SKILL_CONSACRATION, 14 | 15 | HEALER_SKILL_SELF_HEAL, 16 | HEALER_SKILL_CONCENTRATED_HEAL, 17 | HEALER_SKILL_DIVINE_INTERVENTION, 18 | 19 | DPS_SKILL_CHEAPSHOT, 20 | DPS_SKILL_OBLITERATE, 21 | DPS_SKILL_SPLIT 22 | } skillIDs_e; 23 | 24 | typedef struct skill_t 25 | { 26 | skillIDs_e skillID; 27 | float manaConsumption; 28 | 29 | int cooldown; // in ms 30 | Timer* timer; 31 | } skill_t; 32 | 33 | 34 | void G_InitializeSkills(void); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/Engine/I_InputHandling.c: -------------------------------------------------------------------------------- 1 | #include "I_InputHandling.h" 2 | #include "../Online/O_Lobby.h" 3 | #include "../Online/O_Lobby.h" 4 | 5 | playerinput_t playerinput; 6 | 7 | // ------------------------------- 8 | // Handles SDL Events and input 9 | // ------------------------------- 10 | void I_HandleInputGame(void) 11 | { 12 | const Uint8* keyboard_state = SDL_GetKeyboardState(NULL); 13 | const Uint32 mouse_state = SDL_GetMouseState(NULL, NULL); 14 | 15 | SDL_Event e; 16 | 17 | while(SDL_PollEvent(&e)) 18 | { 19 | switch (e.type) 20 | { 21 | case SDL_QUIT: 22 | application.quit = true; 23 | break; 24 | 25 | default: 26 | break; 27 | } 28 | 29 | G_InGameInputHandlingEvent(&e); 30 | } 31 | 32 | // Send Input event to subsystems 33 | if(application.gamestate == GSTATE_GAME) 34 | { 35 | G_InGameInputHandling(keyboard_state); 36 | } 37 | } 38 | 39 | void I_HandleInputMenu(void) 40 | { 41 | const Uint8* keyboard_state = SDL_GetKeyboardState(NULL); 42 | const Uint32 mouse_state = SDL_GetMouseState(NULL, NULL); 43 | 44 | SDL_Event e; 45 | 46 | while(SDL_PollEvent(&e)) 47 | { 48 | switch (e.type) 49 | { 50 | case SDL_QUIT: 51 | application.quit = true; 52 | break; 53 | 54 | case SDL_KEYDOWN: 55 | if(e.key.keysym.sym == SDLK_BACKSPACE && isEditingTextField && textFieldEditing != NULL) 56 | { 57 | int len = strlen(textFieldEditing->text); 58 | if(len > 0) 59 | textFieldEditing->text[len - 1] = '\0'; // Set the last character to null terminator 60 | } 61 | break; 62 | 63 | case SDL_TEXTINPUT: 64 | if(isEditingTextField && textFieldEditing != NULL) 65 | { 66 | int len = strlen(textFieldEditing->text); 67 | 68 | if(len < TEXTFIELD_MAX_LENGTH && len < textFieldEditing->textLimit) 69 | strcat(textFieldEditing->text, e.text.text); 70 | } 71 | break; 72 | 73 | default: 74 | break; 75 | } 76 | 77 | G_InMenuInputHandling(&e); 78 | 79 | // Check if user is in lobby 80 | if(application.gamestate == GSTATE_MENU && currentMenu == &InLobbyMenu) 81 | O_LobbyInputHandling(&e); 82 | } 83 | } -------------------------------------------------------------------------------- /src/Engine/I_InputHandling.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_HANDLING_H_INCLUDED 2 | #define INPUT_HANDLING_H_INCLUDED 3 | 4 | #include "SDL.h" 5 | 6 | #include "A_Application.h" 7 | #include "G_Player.h" 8 | #include "G_MainMenu.h" 9 | 10 | // ------------------------------- 11 | // Holds the player's input 12 | // ------------------------------- 13 | typedef struct playerinput_s 14 | { 15 | vector2_t input; 16 | vector2_t mouseInput; 17 | vector2_t dir; 18 | vector2_t strafe; // 1 = right -1 = left 19 | } playerinput_t; 20 | 21 | extern playerinput_t playerinput; 22 | 23 | // ------------------------------- 24 | // Handles SDL Events and input While in Menu 25 | // ------------------------------- 26 | void I_HandleInputMenu(void); 27 | 28 | // ------------------------------- 29 | // Handles SDL Events and input While in Game 30 | // ------------------------------- 31 | void I_HandleInputGame(void); 32 | 33 | #endif -------------------------------------------------------------------------------- /src/Engine/M_Map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H_INCLUDED 2 | #define MAP_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "A_Application.h" 9 | #include "D_AssetsManager.h" 10 | #include "U_Utilities.h" 11 | 12 | // Max Map Width/Height 13 | #define MAP_WIDTH 128 14 | #define MAP_HEIGHT 128 15 | #define TILE_SIZE 64 16 | #define HALF_TILE_SIZE 32 17 | 18 | #define UNIT_SIZE 64 19 | 20 | // Number of levels a scene has 21 | #define MAX_N_LEVELS 3 22 | 23 | #define MAX_MAP_NAME_LENGTH 16 24 | 25 | // Types of object 26 | typedef enum objectType_e 27 | { 28 | ObjT_Empty = 0, 29 | ObjT_Wall, 30 | ObjT_Door, 31 | ObjT_Sprite, 32 | ObjT_Trigger // Triggers the callback 33 | } objectType_e; 34 | 35 | // Map info 36 | typedef struct map_s 37 | { 38 | // Map static 39 | char id[MAX_STRLEN]; 40 | char name[MAX_STRLEN]; 41 | 42 | int playerStartingLevel; 43 | int playerStartingGridX; 44 | int playerStartingGridY; 45 | float playerStartingRot; 46 | 47 | // Walls on levels 48 | wallObject_t level0[MAP_HEIGHT][MAP_WIDTH]; 49 | wallObject_t level1[MAP_HEIGHT][MAP_WIDTH]; 50 | wallObject_t level2[MAP_HEIGHT][MAP_WIDTH]; 51 | 52 | int floorMap[MAP_HEIGHT][MAP_WIDTH]; 53 | int ceilingMap[MAP_HEIGHT][MAP_WIDTH]; 54 | 55 | int spritesMapLevel0[MAP_HEIGHT][MAP_WIDTH]; 56 | int spritesMapLevel1[MAP_HEIGHT][MAP_WIDTH]; 57 | int spritesMapLevel2[MAP_HEIGHT][MAP_WIDTH]; 58 | 59 | float wallLight; 60 | float floorLight; 61 | int skyID; 62 | 63 | // A level with Abs Ceiling has ceiling for the whole map 64 | bool hasAbsCeiling; 65 | // At which level we have abs ceiling 66 | int absCeilingLevel; 67 | 68 | // Fog 69 | bool hasFog; 70 | 71 | int fogColorR; 72 | int fogColorG; 73 | int fogColorB; 74 | 75 | SDL_Color fogColor; 76 | float wallFogMinDist; 77 | float wallFogMaxDist; 78 | float floorFogMinDist; 79 | float floorFogMaxDist; 80 | 81 | // Map Dynamics, values are cached in multiple matrixes to avoid having to calculate them each time 82 | // These values can change during runtime 83 | 84 | int objectTMapLevel0[MAP_HEIGHT][MAP_WIDTH]; // Contains the objectType_e currently placed in that cell, mainly used to differientate input and to know which kind of object is where 85 | int objectTMapLevel1[MAP_HEIGHT][MAP_WIDTH]; // Contains the objectType_e currently placed in that cell, mainly used to differientate input and to know which kind of object is where 86 | int objectTMapLevel2[MAP_HEIGHT][MAP_WIDTH]; // Contains the objectType_e currently placed in that cell, mainly used to differientate input and to know which kind of object is where 87 | 88 | int collisionMapLevel0[MAP_HEIGHT][MAP_WIDTH]; // Collision data 89 | int collisionMapLevel1[MAP_HEIGHT][MAP_WIDTH]; // Collision data 90 | int collisionMapLevel2[MAP_HEIGHT][MAP_WIDTH]; // Collision data 91 | 92 | // Dynamic sprites 93 | dynamicSprite_t* dynamicSpritesLevel0[MAP_HEIGHT][MAP_WIDTH]; 94 | dynamicSprite_t* dynamicSpritesLevel1[MAP_HEIGHT][MAP_WIDTH]; 95 | dynamicSprite_t* dynamicSpritesLevel2[MAP_HEIGHT][MAP_WIDTH]; 96 | 97 | // Death sprites 98 | dynamicSprite_t* deadDynamicSpritesLevel0[MAP_HEIGHT][MAP_WIDTH]; 99 | dynamicSprite_t* deadDynamicSpritesLevel1[MAP_HEIGHT][MAP_WIDTH]; 100 | dynamicSprite_t* deadDynamicSpritesLevel2[MAP_HEIGHT][MAP_WIDTH]; 101 | 102 | } map_t; 103 | 104 | // The currently loaded map 105 | extern map_t currentMap; 106 | 107 | // ------------------------------- 108 | // Loads the map from the file named mapID 109 | // ------------------------------- 110 | void M_LoadMapAsCurrent(char* mapID); 111 | 112 | // ------------------------------- 113 | // Loads the object map 114 | // ------------------------------- 115 | void M_LoadObjectTMap(void); 116 | 117 | // ------------------------------- 118 | // Loads the collision map 119 | // ------------------------------- 120 | void M_LoadCollisionMaps(void); 121 | 122 | #endif -------------------------------------------------------------------------------- /src/Engine/P_Physics.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "P_Physics.h" 4 | 5 | Uint32 current; 6 | Uint32 lastUpdate; 7 | 8 | // Delta time 9 | float deltaTime; 10 | 11 | //------------------------------------- 12 | // Initializes the physics 13 | //------------------------------------- 14 | void P_PhysicsInit(void) 15 | { 16 | current = 0; 17 | lastUpdate = 0; 18 | } 19 | 20 | 21 | //------------------------------------- 22 | // Physics tick 23 | //------------------------------------- 24 | void P_PhysicsTick(void) 25 | { 26 | current = SDL_GetTicks(); 27 | 28 | deltaTime = (current - lastUpdate) / 1000.0f; 29 | } 30 | 31 | //------------------------------------- 32 | // End the physics tick 33 | //------------------------------------- 34 | void P_PhysicsEndTick(void) 35 | { 36 | lastUpdate = current; 37 | } 38 | 39 | float P_GetDistance(float x1, float y1, float x2, float y2) 40 | { 41 | float dx = x1 - x2; 42 | float dy = y1 - y2; 43 | 44 | return sqrt(dx*dx + dy*dy); 45 | } 46 | 47 | float P_GetDistanceSquared(float x1, float y1, float x2, float y2) 48 | { 49 | float dx = x1 - x2; 50 | float dy = y1 - y2; 51 | 52 | return (dx*dx + dy*dy); 53 | } 54 | 55 | //------------------------------------- 56 | // Checks the collision between two circles 57 | //------------------------------------- 58 | float P_CheckCircleCollision(circle_t* c1, circle_t* c2) 59 | { 60 | // Check for null 61 | if(c1->r <= 0.0f || c2->r <= 0.0f) 62 | return -1.0f; 63 | 64 | float totalRadius = c1->r + c2->r; 65 | totalRadius = totalRadius*totalRadius; 66 | 67 | float dist = P_GetDistanceSquared(c1->pos.x, c1->pos.y, c2->pos.x, c2->pos.y); 68 | if(dist < totalRadius) 69 | return dist; 70 | 71 | // Negative values are false 72 | return -1.0f; 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/Engine/P_Physics.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYSICS_H_INCLUDED 2 | #define PHYSICS_H_INCLUDED 3 | 4 | #include "P_Physics.h" 5 | #include "U_Timer.h" 6 | #include "U_DataTypes.h" 7 | 8 | // Timer needed to calculate delta time 9 | extern Timer* stepTimer; 10 | 11 | // Delta time 12 | extern float deltaTime; 13 | 14 | //------------------------------------- 15 | // Initializes the physics 16 | //------------------------------------- 17 | void P_PhysicsInit(void); 18 | 19 | //------------------------------------- 20 | // Physics tick 21 | //------------------------------------- 22 | void P_PhysicsTick(void); 23 | 24 | //------------------------------------- 25 | // End the physics tick 26 | //------------------------------------- 27 | void P_PhysicsEndTick(void); 28 | 29 | //------------------------------------- 30 | // Checks the collision between two circles 31 | //------------------------------------- 32 | float P_CheckCircleCollision(circle_t* c1, circle_t* c2); 33 | 34 | 35 | float P_GetDistance(float x1, float y1, float x2, float y2); 36 | float P_GetDistanceSquared(float x1, float y1, float x2, float y2); 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/Engine/R_Rendering.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERING_H_INCLUDED 2 | #define RENDERING_H_INCLUDED 3 | 4 | #include "SDL.h" 5 | 6 | #include "M_Map.h" 7 | #include "U_DataTypes.h" 8 | #include 9 | 10 | // ========================================= 11 | // Raycasting 12 | // ========================================= 13 | #define PLAYER_FOV 60 // FOV of the player for raycasting 14 | #define PLAYER_FOV_F 60.0f // FOV of the player for raycasting (as float) 15 | 16 | 17 | // Max values the projection plane can be 18 | #define MAX_PROJECTION_PLANE_WIDTH 800 19 | #define MAX_PROJECTION_PLANE_HEIGHT 600 20 | #define MAX_VIEWEABLE_SPRITE_ANGLES 8 21 | 22 | // Runtime Graphichs 23 | typedef enum GraphicsOptions_e 24 | { 25 | GRAPHICS_LOW = 0, 26 | GRAPHICS_MEDIUM, 27 | GRAPHICS_HIGH 28 | } GraphicsOptions_e; 29 | 30 | extern GraphicsOptions_e r_CurrentGraphicsSetting; 31 | 32 | // Projection Plane 33 | extern int PROJECTION_PLANE_WIDTH; 34 | extern int PROJECTION_PLANE_HEIGHT; 35 | extern int PROJECTION_PLANE_CENTER; 36 | 37 | //#define DISTANCE_TO_PROJECTION ((PROJECTION_PLANE_WIDTH / 2) / tan(PLAYER_FOV /2)) 38 | extern int DISTANCE_TO_PROJECTION; 39 | 40 | // Player's look up/down 41 | extern int MAX_VERTICAL_HEAD_MOV; 42 | extern int MIN_VERTICAL_HEAD_MOV; 43 | 44 | // ========================================= 45 | // Minimap 46 | // ========================================= 47 | #define MINIMAP_DIVIDER 16 // Divider for making the minimap smaller 48 | #define MINIMAP_PLAYER_WIDTH 4 // Dividers for making the player in the minimap smaller 49 | #define MINIMAP_PLAYER_HEIGHT 4 50 | 51 | // ========================================= 52 | // Sprites 53 | // ========================================= 54 | #define MAXVISABLE 50 // * MAX_FLOORS, the visibleSprites array and visibleTiles are reused each time 55 | #define MAX_SPRITE_HEIGHT 2000 56 | #define ANIMATION_SPEED_DIVIDER 200 57 | #define ANIMATION_ATTACK_SPEED_DIVIDER 300 58 | 59 | // Visible Sprite Determination 60 | extern bool visibleTiles[MAP_HEIGHT][MAP_WIDTH]; 61 | extern sprite_t visibleSprites[MAXVISABLE]; 62 | extern int visibleSpritesLength; 63 | 64 | // ========================================= 65 | // Thin wall Transparency 66 | // ========================================= 67 | 68 | // Max amounts of times the ray can ignore and save a thin wall (see through a see through wall) 69 | #define MAX_THIN_WALL_TRANSPARENCY_RECURSION 4 70 | 71 | // Found thin walls to draw 72 | extern walldata_t currentThinWalls[MAX_PROJECTION_PLANE_WIDTH * MAX_THIN_WALL_TRANSPARENCY_RECURSION]; 73 | extern unsigned visibleThinWallsLength; 74 | 75 | // Alert message 76 | #define ALERT_MESSAGE_DEF_X 540 77 | #define ALERT_MESSAGE_DEF_Y 10 78 | 79 | extern alertMessage_t* alertMessagesHead; 80 | 81 | 82 | // ========================================= 83 | // Debug 84 | // ========================================= 85 | #define DEBUG_RAYCAST_MINIMAP 0 86 | #define DEBUG_VISIBLE_TILES_MINIMAP 1 87 | #define DEBUG_DYNAMIC_SPRITES 0 88 | #define DEBUG_VISIBLE_SPRITES_MINIMAP 0 89 | 90 | extern uint32_t r_blankColor; // Color shown when nothing else is in the renderer 91 | extern uint32_t r_transparencyColor; // Color marked as "transparency", rendering of this color will be skipped for surfaces 92 | 93 | // Wall heights, saved for each x for each level 94 | extern float zBuffer[MAX_PROJECTION_PLANE_HEIGHT][MAX_PROJECTION_PLANE_WIDTH]; 95 | 96 | extern float floorcastLookUp[MAX_N_LEVELS][MAX_PROJECTION_PLANE_HEIGHT]; 97 | extern float ceilingcastLookUp[MAX_N_LEVELS][MAX_PROJECTION_PLANE_HEIGHT]; 98 | 99 | // Drawables 100 | #define MAX_DRAWABLES MAX_PROJECTION_PLANE_WIDTH * MAX_THIN_WALL_TRANSPARENCY_RECURSION + MAXVISABLE 101 | extern drawabledata_t allDrawables[MAX_DRAWABLES]; 102 | extern int allDrawablesLength; 103 | 104 | extern bool debugRendering; 105 | extern bool r_debugPathfinding; 106 | 107 | 108 | 109 | //------------------------------------- 110 | // Initializes the rendering 111 | //------------------------------------- 112 | void R_InitRendering(void); 113 | 114 | void R_SetRenderingGraphics(GraphicsOptions_e setting); 115 | 116 | //------------------------------------- 117 | // Fill buffers and put framebuffers on top of each other 118 | //------------------------------------- 119 | void R_ComposeFrame(void); 120 | 121 | 122 | //------------------------------------- 123 | // Updates the screen to the win_surface 124 | //------------------------------------- 125 | void R_FinishUpdate(void); 126 | 127 | //------------------------------------- 128 | // Gets a pixel from a surface 129 | //------------------------------------- 130 | Uint32 R_GetPixelFromSurface(SDL_Surface *surface, int x, int y); 131 | 132 | //------------------------------------- 133 | // Given an SDL_Surface, extracts the pixels of it and puts them in the selected framebuffer 134 | //------------------------------------- 135 | void R_BlitIntoScreen(SDL_Rect* size, SDL_Surface* sur, SDL_Rect* pos); 136 | 137 | //------------------------------------- 138 | // Given an SDL_Surface, extracts the pixels of it and puts them in the selected framebuffer 139 | //------------------------------------- 140 | void R_BlitIntoScreenScaled(SDL_Rect* size, SDL_Surface* sur, SDL_Rect* pos); 141 | 142 | //------------------------------------- 143 | // Given a color, extracts draws it in the selected framebuffer 144 | //------------------------------------- 145 | void R_BlitColorIntoScreen(int color, SDL_Rect* pos); 146 | 147 | //------------------------------------- 148 | // Draw lines using Bresenham's 149 | //------------------------------------- 150 | void R_DrawLine(int x0, int y0, int x1, int y1, int color); 151 | 152 | //------------------------------------- 153 | // Draw a single pixel 154 | //------------------------------------- 155 | void R_DrawPixel(int x, int y, int color); 156 | 157 | //------------------------------------- 158 | // Draw a single pixel with shading 159 | //------------------------------------- 160 | void R_DrawPixelShaded(int x, int y, int color, float intensity, float dist, bool usesFog, float fogBlendingFactor); 161 | 162 | //------------------------------------- 163 | // Draw a column of pixels with shading 164 | //------------------------------------- 165 | void R_DrawColumnOfPixelShaded(int x, int y, int endY, int color, float intensity, float distance, bool usesFog, float fogBlendingFactor); 166 | 167 | //------------------------------------- 168 | // Draw a column of pixel 169 | //------------------------------------- 170 | void R_DrawColumn(int x, int y, int endY, int color); 171 | 172 | //------------------------------------- 173 | // Sets the screen to r_blankColor 174 | //------------------------------------- 175 | void R_ClearRendering(void); 176 | 177 | //------------------------------------- 178 | // Draws the minimap 179 | //------------------------------------- 180 | void R_DrawMinimap(void); 181 | 182 | //------------------------------------- 183 | // Draws the background of the game view 184 | //------------------------------------- 185 | void R_DrawBackground(void); 186 | 187 | 188 | //------------------------------------- 189 | // Calls the the Raycast routines and draws the walls 190 | //------------------------------------- 191 | void R_Raycast(void); 192 | 193 | //------------------------------------- 194 | // Raycasts the camera's level, therefore with occlusion 195 | //------------------------------------- 196 | void R_RaycastPlayersLevel(int level, int x, float _rayAngle); 197 | 198 | //------------------------------------- 199 | // Raycasts levels above and below the camera's level, therefore without occlusion 200 | //------------------------------------- 201 | void R_RaycastLevelNoOcclusion(int level, int x, float _rayAngle); 202 | 203 | //------------------------------------- 204 | // Draws the bottom face of a cube that's located above camera's head 205 | //------------------------------------- 206 | void R_DrawWallBottom(walldata_t* wall, float height, float screenZ, bool isInFront); 207 | 208 | //------------------------------------- 209 | // Draws the bottom top of a cube that's located below camera's head 210 | //------------------------------------- 211 | void R_DrawWallTop(walldata_t* wall, float height, float screenZ, bool isInFront); 212 | 213 | //------------------------------------- 214 | // Drawables routine, sort and render drawables 215 | //------------------------------------- 216 | void R_DrawDrawables(void); 217 | 218 | //------------------------------------- 219 | // Draw the passed thin wall 220 | //------------------------------------- 221 | void R_DrawThinWall(walldata_t* wall); 222 | 223 | //------------------------------------- 224 | // Floorcast and ceilingcast 225 | // Params: 226 | // - end = the end of the wall that states where to start to floorcast 227 | // - rayAngle = the current rayangle 228 | // - x = the x coordinate on the screen for this specific floor cast call 229 | //------------------------------------- 230 | void R_FloorCasting(int level, int end, float rayAngle, int x, float wallHeight); 231 | void R_FloorCastingOld(int end, float rayAngle, int x, float wallHeight); 232 | 233 | void R_FloorCastingHor(); 234 | 235 | //------------------------------------- 236 | // Floorcast and ceilingcast 237 | // Params: 238 | // - end = the end of the wall that states where to start to floorcast 239 | // - rayAngle = the current rayangle 240 | // - x = the x coordinate on the screen for this specific floor cast call 241 | //------------------------------------- 242 | void R_CeilingCasting(int level, float start, float rayAngle, int x, float wallHeight); 243 | void R_CeilingCastingOld(int level,float start, float rayAngle, int x, float wallHeight); 244 | bool R_DoesCeilingCast(wallObject_t* obj); 245 | 246 | bool R_DoesFloorCast(wallObject_t* obj); 247 | void R_CeilingCastingHor(int level); 248 | 249 | //------------------------------------- 250 | // Adds a sprite to the visible sprite array and adds its corresponding drawable 251 | //------------------------------------- 252 | void R_AddToVisibleSprite(int gridX, int gridY, int level, int spriteID); 253 | 254 | //------------------------------------- 255 | // Adds a dynamic sprite to the visible sprite array and adds its corresponding drawable 256 | //------------------------------------- 257 | void R_AddDynamicToVisibleSprite(int level, int gridX, int gridY); 258 | 259 | //------------------------------------- 260 | // Adds a dynamic sprite to the visible sprite array and adds its corresponding drawable 261 | //------------------------------------- 262 | void R_AddDeadDynamicToVisibleSprite(int level, int gridX, int gridY); 263 | 264 | //------------------------------------- 265 | // Draws the visible sprites 266 | //------------------------------------- 267 | void R_DrawSprite(sprite_t* sprite, bool angled); 268 | 269 | //------------------------------------- 270 | // Draws the visible sprites 271 | //------------------------------------- 272 | void R_DrawDynamicSprite(dynamicSprite_t* sprite, bool angled); 273 | 274 | //------------------------------------- 275 | // Given a level and the grid coordinates, returns what is in the map 276 | //------------------------------------- 277 | wallObject_t* R_GetWallObjectFromMap(int level, int y, int x); 278 | 279 | //------------------------------------- 280 | // Given a level and the grid coordinates, returns what is in the map 281 | //------------------------------------- 282 | int R_GetValueFromSpritesMap(int level, int y, int x); 283 | 284 | void R_SetValueFromSpritesMap(int level, int y, int x, int value); 285 | 286 | void R_SetValueFromCollisionMap(int level, int y, int x, int value); 287 | 288 | //------------------------------------- 289 | // Draws a column of pixels with texture mapping 290 | //------------------------------------- 291 | void R_DrawColumnTextured(int x, int y, int endY, SDL_Surface* texture, int xOffset, float wallheight); 292 | 293 | //------------------------------------- 294 | // Draws a column of pixels with texture mapping and shading 295 | //------------------------------------- 296 | void R_DrawStripeTexturedShaded(int x, int y, int endY, SDL_Surface* texture, int xOffset, int yOffset, float wallheight, float intensity, float dist, bool hasFog, float fogBlendingFactor); 297 | 298 | //------------------------------------- 299 | // Queue an Alert Message to be displayed 300 | //------------------------------------- 301 | void R_QueueAlertMessage(alertMessage_t* m, int x, int y, char* msg, float duration, float size); 302 | 303 | //------------------------------------- 304 | // Updates and renders the Alert Messages queue 305 | //------------------------------------- 306 | void R_UpdateAlertMessages(void); 307 | 308 | int R_ThreadRoutine(void* data); 309 | 310 | #endif -------------------------------------------------------------------------------- /src/Engine/T_TextRendering.c: -------------------------------------------------------------------------------- 1 | #include "T_TextRendering.h" 2 | #include "D_AssetsManager.h" 3 | #include "R_Rendering.h" 4 | 5 | // ---------------------------------------------------------------------- 6 | // Displays the given text 7 | // ---------------------------------------------------------------------- 8 | void T_DisplayText(int fontID, char* text, int x, int y) 9 | { 10 | // The X we're currently drawing to 11 | int curDrawingX = x; 12 | 13 | // Current char in the text 14 | int curCIndex = 0; 15 | char curC = 0; 16 | 17 | // Width of the font 18 | int width = tomentdatapack.fontsheets[fontID]->width; 19 | 20 | int curY = y; // y can be affected by \n 21 | 22 | // While we're not finished drawing 23 | while(text[curCIndex] != '\0') 24 | { 25 | if(text[curCIndex] == '\n') 26 | { 27 | curY += width; 28 | curDrawingX = x; 29 | curCIndex++; 30 | } 31 | else 32 | { 33 | // Get the char we have to draw 34 | curC = text[curCIndex]; 35 | 36 | // Translate to the sprite sheet coords to get the correct texture 37 | int texX, texY; 38 | T_TranslateASCIIToSpriteSheetCoords(curC, &texX, &texY); 39 | 40 | // Blit it on the screen 41 | SDL_Rect screenPos = {curDrawingX, curY, width, width}; 42 | SDL_Rect size = {(texX * width), (texY * width), width, width}; 43 | R_BlitIntoScreen(&size, tomentdatapack.fontsheets[fontID]->texture, &screenPos); 44 | 45 | // Next 46 | curCIndex++; 47 | curDrawingX += tomentdatapack.fontsheets[fontID]->glyphsWidth[texY][texX]; 48 | } 49 | } 50 | } 51 | 52 | // ---------------------------------------------------------------------- 53 | // Displays the given text 54 | // ---------------------------------------------------------------------- 55 | void T_DisplayTextScaled(int fontID, char* text, int x, int y, float scaleFactor) 56 | { 57 | // The X we're currently drawing to 58 | int curDrawingX = x; 59 | 60 | // Current char in the text 61 | int curCIndex = 0; 62 | char curC = 0; 63 | 64 | // Width of the font 65 | int width = tomentdatapack.fontsheets[fontID]->width; 66 | 67 | int curY = y; // y can be affected by \n 68 | 69 | // While we're not finished drawing 70 | while(text[curCIndex] != '\0') 71 | { 72 | if(text[curCIndex] == '\n') 73 | { 74 | curY += width * scaleFactor; 75 | curDrawingX = x; 76 | curCIndex++; 77 | } 78 | else 79 | { 80 | // Get the char we have to draw 81 | curC = text[curCIndex]; 82 | 83 | // Translate to the sprite sheet coords to get the correct texture 84 | int texX, texY; 85 | T_TranslateASCIIToSpriteSheetCoords(curC, &texX, &texY); 86 | 87 | // Blit it on the screen 88 | SDL_Rect screenPos = {curDrawingX, curY, width * scaleFactor, width* scaleFactor}; 89 | SDL_Rect size = {(texX * width), (texY * width), width, width}; 90 | R_BlitIntoScreenScaled(&size, tomentdatapack.fontsheets[fontID]->texture, &screenPos); 91 | 92 | // Next 93 | curCIndex++; 94 | curDrawingX += tomentdatapack.fontsheets[fontID]->glyphsWidth[texY][texX] * scaleFactor; 95 | } 96 | } 97 | } 98 | 99 | // ---------------------------------------------------------------------- 100 | // Given a char, returns the sprite sheet coords, most naive approach ever 101 | // 102 | // This translation also kills the universality of the Text Renderer by forcing the font sheet to be 16x6 103 | // ---------------------------------------------------------------------- 104 | void T_TranslateASCIIToSpriteSheetCoords(char c, int* destX, int* destY) 105 | { 106 | switch(c) 107 | { 108 | case ' ': 109 | *destX = 0; 110 | *destY = 0; 111 | break; 112 | 113 | case '!': 114 | *destX = 1; 115 | *destY = 0; 116 | break; 117 | 118 | case '"': 119 | *destX = 2; 120 | *destY = 0; 121 | break; 122 | 123 | case '#': 124 | *destX = 3; 125 | *destY = 0; 126 | break; 127 | 128 | case '$': 129 | *destX = 4; 130 | *destY = 0; 131 | break; 132 | 133 | case '%': 134 | *destX = 5; 135 | *destY = 0; 136 | break; 137 | 138 | case '&': 139 | *destX = 6; 140 | *destY = 0; 141 | break; 142 | 143 | case '\'': 144 | *destX = 7; 145 | *destY = 0; 146 | break; 147 | 148 | case '(': 149 | *destX = 8; 150 | *destY = 0; 151 | break; 152 | 153 | case ')': 154 | *destX = 9; 155 | *destY = 0; 156 | break; 157 | 158 | case '*': 159 | *destX = 10; 160 | *destY = 0; 161 | break; 162 | 163 | case '+': 164 | *destX = 11; 165 | *destY = 0; 166 | break; 167 | 168 | case ',': 169 | *destX = 12; 170 | *destY = 0; 171 | break; 172 | 173 | case '-': 174 | *destX = 13; 175 | *destY = 0; 176 | break; 177 | 178 | case '.': 179 | *destX = 14; 180 | *destY = 0; 181 | break; 182 | 183 | case '/': 184 | *destX = 15; 185 | *destY = 0; 186 | break; 187 | 188 | case '0': 189 | *destX = 0; 190 | *destY = 1; 191 | break; 192 | 193 | case '1': 194 | *destX = 1; 195 | *destY = 1; 196 | break; 197 | 198 | case '2': 199 | *destX = 2; 200 | *destY = 1; 201 | break; 202 | 203 | case '3': 204 | *destX = 3; 205 | *destY = 1; 206 | break; 207 | 208 | case '4': 209 | *destX = 4; 210 | *destY = 1; 211 | break; 212 | 213 | case '5': 214 | *destX = 5; 215 | *destY = 1; 216 | break; 217 | 218 | case '6': 219 | *destX = 6; 220 | *destY = 1; 221 | break; 222 | 223 | case '7': 224 | *destX = 7; 225 | *destY = 1; 226 | break; 227 | 228 | case '8': 229 | *destX = 8; 230 | *destY = 1; 231 | break; 232 | 233 | case '9': 234 | *destX = 9; 235 | *destY = 1; 236 | break; 237 | 238 | case ':': 239 | *destX = 10; 240 | *destY = 1; 241 | break; 242 | 243 | case ';': 244 | *destX = 11; 245 | *destY = 1; 246 | break; 247 | 248 | case '<': 249 | *destX = 12; 250 | *destY = 1; 251 | break; 252 | 253 | case '=': 254 | *destX = 13; 255 | *destY = 1; 256 | break; 257 | 258 | case '>': 259 | *destX = 14; 260 | *destY = 1; 261 | break; 262 | 263 | case '?': 264 | *destX = 15; 265 | *destY = 1; 266 | break; 267 | 268 | case '@': 269 | *destX = 0; 270 | *destY = 2; 271 | break; 272 | 273 | case 'A': 274 | *destX = 1; 275 | *destY = 2; 276 | break; 277 | 278 | case 'B': 279 | *destX = 2; 280 | *destY = 2; 281 | break; 282 | 283 | case 'C': 284 | *destX = 3; 285 | *destY = 2; 286 | break; 287 | 288 | case 'D': 289 | *destX = 4; 290 | *destY = 2; 291 | break; 292 | 293 | case 'E': 294 | *destX = 5; 295 | *destY = 2; 296 | break; 297 | 298 | case 'F': 299 | *destX = 6; 300 | *destY = 2; 301 | break; 302 | 303 | case 'G': 304 | *destX = 7; 305 | *destY = 2; 306 | break; 307 | 308 | case 'H': 309 | *destX = 8; 310 | *destY = 2; 311 | break; 312 | 313 | case 'I': 314 | *destX = 9; 315 | *destY = 2; 316 | break; 317 | 318 | case 'J': 319 | *destX = 10; 320 | *destY = 2; 321 | break; 322 | 323 | case 'K': 324 | *destX = 11; 325 | *destY = 2; 326 | break; 327 | 328 | case 'L': 329 | *destX = 12; 330 | *destY = 2; 331 | break; 332 | 333 | case 'M': 334 | *destX = 13; 335 | *destY = 2; 336 | break; 337 | 338 | case 'N': 339 | *destX = 14; 340 | *destY = 2; 341 | break; 342 | 343 | case 'O': 344 | *destX = 15; 345 | *destY = 2; 346 | break; 347 | 348 | case 'P': 349 | *destX = 0; 350 | *destY = 3; 351 | break; 352 | 353 | case 'Q': 354 | *destX = 1; 355 | *destY = 3; 356 | break; 357 | 358 | case 'R': 359 | *destX = 2; 360 | *destY = 3; 361 | break; 362 | 363 | case 'S': 364 | *destX = 3; 365 | *destY = 3; 366 | break; 367 | 368 | case 'T': 369 | *destX = 4; 370 | *destY = 3; 371 | break; 372 | 373 | case 'U': 374 | *destX = 5; 375 | *destY = 3; 376 | break; 377 | 378 | case 'V': 379 | *destX = 6; 380 | *destY = 3; 381 | break; 382 | 383 | case 'W': 384 | *destX = 7; 385 | *destY = 3; 386 | break; 387 | 388 | case 'X': 389 | *destX = 8; 390 | *destY = 3; 391 | break; 392 | 393 | case 'Y': 394 | *destX = 9; 395 | *destY = 3; 396 | break; 397 | 398 | case 'Z': 399 | *destX = 10; 400 | *destY = 3; 401 | break; 402 | 403 | case '[': 404 | *destX = 11; 405 | *destY = 3; 406 | break; 407 | 408 | case '\\': 409 | *destX = 12; 410 | *destY = 3; 411 | break; 412 | 413 | case ']': 414 | *destX = 13; 415 | *destY = 3; 416 | break; 417 | 418 | case '^': 419 | *destX = 14; 420 | *destY = 3; 421 | break; 422 | 423 | case '_': 424 | *destX = 15; 425 | *destY = 3; 426 | break; 427 | 428 | case '`': 429 | *destX = 0; 430 | *destY = 4; 431 | break; 432 | 433 | case 'a': 434 | *destX = 1; 435 | *destY = 4; 436 | break; 437 | 438 | case 'b': 439 | *destX = 2; 440 | *destY = 4; 441 | break; 442 | 443 | case 'c': 444 | *destX = 3; 445 | *destY = 4; 446 | break; 447 | 448 | case 'd': 449 | *destX = 4; 450 | *destY = 4; 451 | break; 452 | 453 | case 'e': 454 | *destX = 5; 455 | *destY = 4; 456 | break; 457 | 458 | case 'f': 459 | *destX = 6; 460 | *destY = 4; 461 | break; 462 | 463 | case 'g': 464 | *destX = 7; 465 | *destY = 4; 466 | break; 467 | 468 | case 'h': 469 | *destX = 8; 470 | *destY = 4; 471 | break; 472 | 473 | case 'i': 474 | *destX = 9; 475 | *destY = 4; 476 | break; 477 | 478 | case 'j': 479 | *destX = 10; 480 | *destY = 4; 481 | break; 482 | 483 | case 'k': 484 | *destX = 11; 485 | *destY = 4; 486 | break; 487 | 488 | case 'l': 489 | *destX = 12; 490 | *destY = 4; 491 | break; 492 | 493 | case 'm': 494 | *destX = 13; 495 | *destY = 4; 496 | break; 497 | 498 | case 'n': 499 | *destX = 14; 500 | *destY = 4; 501 | break; 502 | 503 | case 'o': 504 | *destX = 15; 505 | *destY = 4; 506 | break; 507 | 508 | case 'p': 509 | *destX = 0; 510 | *destY = 5; 511 | break; 512 | 513 | case 'q': 514 | *destX = 1; 515 | *destY = 5; 516 | break; 517 | 518 | case 'r': 519 | *destX = 2; 520 | *destY = 5; 521 | break; 522 | 523 | case 's': 524 | *destX = 3; 525 | *destY = 5; 526 | break; 527 | 528 | case 't': 529 | *destX = 4; 530 | *destY = 5; 531 | break; 532 | 533 | case 'u': 534 | *destX = 5; 535 | *destY = 5; 536 | break; 537 | 538 | case 'v': 539 | *destX = 6; 540 | *destY = 5; 541 | break; 542 | 543 | case 'w': 544 | *destX = 7; 545 | *destY = 5; 546 | break; 547 | 548 | case 'x': 549 | *destX = 8; 550 | *destY = 5; 551 | break; 552 | 553 | case 'y': 554 | *destX = 9; 555 | *destY = 5; 556 | break; 557 | 558 | case 'z': 559 | *destX = 10; 560 | *destY = 5; 561 | break; 562 | 563 | case '{': 564 | *destX = 11; 565 | *destY = 5; 566 | break; 567 | 568 | case '|': 569 | *destX = 12; 570 | *destY = 5; 571 | break; 572 | 573 | case '}': 574 | *destX = 13; 575 | *destY = 5; 576 | break; 577 | 578 | case '~': 579 | *destX = 14; 580 | *destY = 5; 581 | break; 582 | 583 | case 127: 584 | *destX = 15; 585 | *destY = 5; 586 | break; 587 | 588 | default: 589 | *destX = 15; 590 | *destY = 5; 591 | break; 592 | } 593 | } 594 | 595 | textfield_t* T_NewTextfield(int x, int y, int w, int h, int textlimit, float textScale) 596 | { 597 | textfield_t* tfield = (textfield_t*)malloc(sizeof(textfield_t)); 598 | tfield->x = x; 599 | tfield->y = y; 600 | tfield->w = w; 601 | tfield->h = h; 602 | tfield->text[0] = '\0'; 603 | tfield->textLimit = textlimit; 604 | tfield->textScale = textScale; 605 | } 606 | 607 | void T_SetTextField(textfield_t *tfield, int x, int y, int w, int h, int textlimit, float textScale) 608 | { 609 | tfield->x = x; 610 | tfield->y = y; 611 | tfield->w = w; 612 | tfield->h = h; 613 | tfield->text[0] = '\0'; 614 | tfield->textLimit = textlimit; 615 | tfield->textScale = textScale; 616 | } -------------------------------------------------------------------------------- /src/Engine/T_TextRendering.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_RENDERING_H_INCLUDED 2 | #define TEXT_RENDERING_H_INCLUDED 3 | 4 | #include "SDL.h" 5 | #include 6 | 7 | #define TEXTFIELD_MAX_LENGTH 32 8 | 9 | typedef struct textfield_t 10 | { 11 | int x, y, w, h; 12 | bool isFocus; 13 | char text[TEXTFIELD_MAX_LENGTH]; 14 | int textLimit; 15 | float textScale; 16 | } textfield_t; 17 | 18 | // ---------------------------------------------------------------------- 19 | // Displays the given text 20 | // ---------------------------------------------------------------------- 21 | void T_DisplayText(int fontID, char* text, int x, int y); 22 | 23 | // ---------------------------------------------------------------------- 24 | // Displays the given text scaled by the scale factor 25 | // ---------------------------------------------------------------------- 26 | void T_DisplayTextScaled(int fontID, char* text, int x, int y, float scaleFactor); 27 | 28 | // ---------------------------------------------------------------------- 29 | // Given a char, returns the sprite sheet coords, most naive approach ever 30 | // 31 | // This translation also kills the universality of the Text Renderer by forcing the font sheet to be 16x6 32 | // You could make different translations for each map or find a better way to translate 33 | // ---------------------------------------------------------------------- 34 | void T_TranslateASCIIToSpriteSheetCoords(char c, int* destX, int* destY); 35 | 36 | // ---------------------------------------------------------------------- 37 | // Returns a new TextField 38 | // ---------------------------------------------------------------------- 39 | textfield_t* T_NewTextfield(int x, int y, int w, int h, int textlimit, float textScale); 40 | 41 | void T_SetTextField(textfield_t *tfield, int x, int y, int w, int h, int textlimit, float textScale); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/Engine/U_DataTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_TYPES_H_INCLUDED 2 | #define DATA_TYPES_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "U_Timer.h" 8 | 9 | // 1 Radian 10 | #define RADIAN 0.0174533 11 | 12 | // Byte definition 13 | typedef uint8_t byte; 14 | 15 | typedef struct vector2_s 16 | { 17 | float x, y; 18 | } vector2_t; 19 | 20 | typedef struct vector2Int_s 21 | { 22 | int x,y; 23 | } vector2Int_t; 24 | 25 | 26 | typedef struct circle_s 27 | { 28 | vector2_t pos; 29 | float r; 30 | } circle_t; 31 | 32 | 33 | //------------------------------------- 34 | // Pathfinding 35 | //------------------------------------- 36 | // Node of a path 37 | typedef struct pathnode_s 38 | { 39 | vector2Int_t gridPos; 40 | 41 | // Costs 42 | int f, g, h; 43 | struct pathnode_s* parent; 44 | } pathnode_t; 45 | 46 | // Used by AI and such 47 | typedef struct path_s 48 | { 49 | bool isValid; 50 | pathnode_t* nodes[256]; 51 | unsigned int nodesLength; 52 | } path_t; 53 | 54 | // ------------------------------- 55 | // Sprite data structure, represents static sprites 56 | // ------------------------------- 57 | typedef struct sprite_s 58 | { 59 | char* name; 60 | bool active; // used for dynamics 61 | 62 | vector2_t pos; // position in world 63 | float angle; 64 | 65 | vector2_t centeredPos; // position in world centered 66 | vector2Int_t gridPos; // position in grid 67 | vector2_t pSpacePos; // position in player space 68 | 69 | int level; // which level this sprite is in 70 | 71 | circle_t collisionCircle; 72 | 73 | int spriteID; 74 | int sheetLength; // For Animated objects 75 | 76 | float dist; // distance from player 77 | float height; // how big the sprite will be drawn 78 | 79 | float z; 80 | } sprite_t; 81 | 82 | // Entity attributes 83 | typedef struct entityattributes_s 84 | { 85 | float curHealth; 86 | float maxHealth; 87 | 88 | float curMana; 89 | float maxMana; 90 | 91 | float spellPower; // multiplier for certain skills (healer) 92 | 93 | float baseDamage; 94 | int attackChance; 95 | int criticalChance; 96 | float criticalModifier; 97 | } entityattributes_t; 98 | 99 | // Defines the states of dynamic sprites (AI) 100 | typedef enum dynamicSpriteState_e 101 | { 102 | DS_STATE_NULL = 0, 103 | DS_STATE_IDLE, 104 | DS_STATE_MOVING, 105 | DS_STATE_ATTACKING, 106 | DS_STATE_DEAD, 107 | DS_STATE_CASTING, 108 | DS_STATE_SPECIAL1, 109 | DS_STATE_SPECIAL2, 110 | DS_STATE_SPECIAL3 111 | } dynamicSpriteState_e; 112 | 113 | // Types of dynamic sprites 114 | typedef enum dynamicSpriteType_e 115 | { 116 | DS_TYPE_AI = 0, 117 | DS_TYPE_PROJECTILE, 118 | DS_TYPE_OTHERPLAYER 119 | } dynamicSpriteType_e; 120 | 121 | // Max number of spell an AI can have (Used to create Timers with which create spell logic) 122 | #define AI_MAX_SPELLS 5 123 | 124 | // ------------------------------- 125 | // Dynamic Sprite data structure, represents dynamic sprites such as AI or projectiles 126 | // ------------------------------- 127 | typedef struct dynamicSprite_s 128 | { 129 | sprite_t base; 130 | 131 | // Network 132 | uint32_t networkID; 133 | bool hasChanged; 134 | 135 | // Dnyamic-Specific 136 | dynamicSpriteType_e type; 137 | dynamicSpriteState_e state; 138 | bool isAlive; 139 | float speed; 140 | 141 | // Boss related stuff 142 | bool isBoss; // If true, when the AI will attack the player the boss UI will appear 143 | bool bossPreventOpeningDoorsWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with doors 144 | bool bossPreventClimbingLaddersWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with ladders 145 | bool bossPreventActivatingTriggersWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with triggers 146 | int bossPhase; 147 | int counter1; // a counter useful to build spells on it 148 | float value1; // a value useful to build spells on it 149 | 150 | bool canBeHit; 151 | 152 | void (*BehaviourUpdate)(struct dynamicSprite_s* this); 153 | Timer* cooldowns[AI_MAX_SPELLS]; // spells/abilities cooldowns, used by bosses and casters 154 | 155 | bool aggroedPlayer; // True if this AI already attacked/chased the player 156 | int spellInUse; 157 | 158 | SDL_Surface* curAnim; 159 | int curAnimLength; 160 | 161 | Timer* animTimer; 162 | bool animPlay; 163 | int animFrame; 164 | bool animPlayOnce; 165 | float animSpeed; 166 | 167 | entityattributes_t attributes; 168 | 169 | vector2_t* targetPos; 170 | vector2Int_t* targetGridPos; 171 | circle_t* targetColl; 172 | 173 | path_t* path; 174 | 175 | // Used for projectiles to distinguish between player's projectiles and AI's projectiles 176 | struct dynamicSprite_s* aiOwner; 177 | bool isOfPlayer; 178 | bool isBeingDestroyed; // called when a projectile hits and awaits the explosion animation to be removed from the list 179 | 180 | // How muc this dynamic needs to move each tick on the z axis 181 | float verticalMovementDelta; 182 | 183 | vector2_t overheadPos; 184 | 185 | // Aggro variables to determine which player this AI should follow and attack 186 | int hostAggro; 187 | int joinerAggro; 188 | 189 | // Online position updates 190 | bool posArrived; 191 | float lastPosX, lastPosY, lastPosZ; 192 | 193 | // Display positions 194 | 195 | /* 196 | When the host calculates the position packets and sends them to the joiner, there will always be some delay. 197 | If the joiner sets the coordinates directly, there is no other problem than the fact that the movements of the AI will be visibly snapped to the movements coordinates inside the packets. 198 | 199 | If the joiner smoothly interoplates its current positions to the packet's position, the movements appear smooth, but the world may get out of synch as the host may do something like instantiating the AI on a tile that is occupied for the other player (because the smoothed movement is slower and not synchronized at any given frame) 200 | So it's definitely a no-go. 201 | 202 | The solution is to use a display position only on the joiner side. 203 | The joiner will keep setting directly what is the normal "pos" (now called the "internal" pos) of the AIs that will make sure the worlds are synched. 204 | But the Drawables will now be rendered using the display pos instead of the internal pos. 205 | 206 | The display pos is smoothly interpolated by what it was before and what arrived in the packet. 207 | 208 | The result is that the world will be kept in synch and the AI will appear moving smoothly on the joiner when lag occurs. 209 | */ 210 | vector2_t displayPos; 211 | float displayZ; 212 | float displayDist; 213 | 214 | } dynamicSprite_t; 215 | 216 | // ------------------------------- 217 | // Door states 218 | // ------------------------------- 219 | typedef enum doorstate_e 220 | { 221 | DState_Closed = 0, 222 | DState_Opening, 223 | DState_Open, 224 | DState_Closing 225 | } doorstate_e; 226 | 227 | 228 | // ------------------------------- 229 | // Holds all the info got during raycast to draw walls 230 | // ------------------------------- 231 | typedef struct walldata_s 232 | { 233 | float rayAngle; 234 | int x; 235 | float curX, curY; 236 | bool isVertical; 237 | vector2Int_t gridPos; 238 | float distance; 239 | struct wallObject_s* objectHit; 240 | int level; 241 | 242 | // Extra Data 243 | // For Thin Wall Extra data is 0 = should draw this column of pixel because it is visible 1 = should not draw this column of pixel because the door is open 244 | int extraData; 245 | } walldata_t; 246 | 247 | // ------------------------------- 248 | // Drawable Types 249 | // ------------------------------- 250 | typedef enum drawabletype_e 251 | { 252 | DRWB_WALL = 0, 253 | DRWB_SPRITE, 254 | DRWB_DYNAMIC_SPRITE 255 | } drawabletype_e; 256 | 257 | // ------------------------------- 258 | // Drawables are object in the world that will need to be drawn 259 | // A drawable array holds everything to render and allows Zdepth 260 | // ------------------------------- 261 | typedef struct drawabledata_s 262 | { 263 | drawabletype_e type; 264 | 265 | // Pointers 266 | walldata_t* wallPtr; 267 | sprite_t* spritePtr; 268 | dynamicSprite_t* dynamicSpritePtr; 269 | 270 | // Quick access varaibles 271 | float dist; 272 | } drawabledata_t; 273 | 274 | typedef struct projectileNode_s 275 | { 276 | dynamicSprite_t this; 277 | 278 | // If a projectile is a network instance, no collision checking should happen on this player, the other player will send a message to update us when the projectile hit something 279 | bool isNetworkInstance; 280 | uint32_t networkID; 281 | 282 | struct projectileNode_s* next; 283 | struct projectileNode_s* previous; 284 | } projectileNode_t; 285 | 286 | typedef struct alertMessage_s 287 | { 288 | int x,y; 289 | char* message; 290 | float size, duration; 291 | Timer* timer; 292 | 293 | struct alertMessage_s* next; 294 | } alertMessage_t; 295 | 296 | 297 | typedef enum orientation_e 298 | { 299 | NORTH = 0, 300 | NORTH_WEST, 301 | WEST, 302 | SOUTH_WEST, 303 | SOUTH, 304 | SOUTH_EAST, 305 | EAST, 306 | NORTH_EAST, 307 | NO_ORIENTATION, 308 | NORTH_SOUTH, // Specials 309 | WEST_EAST, 310 | ALL_FOUR_DIR, 311 | } orientation_e; 312 | 313 | typedef struct mappuddle_s 314 | { 315 | int networkID; 316 | bool isNetworkedInstance; 317 | 318 | int gridX; 319 | int gridY; 320 | 321 | bool damagesAI; 322 | bool damagesPlayers; 323 | 324 | float damage; 325 | int duration; // in ms 326 | 327 | int level; 328 | int previousFloorID; 329 | int newFloorID; 330 | 331 | Timer* timer; 332 | 333 | struct mappuddle_s* next; 334 | struct mappuddle_s* previous; 335 | } mappudlle_t; 336 | 337 | #define INCOMING_CHAT_INITIAL_Y_POS 440 338 | #define INCOMING_CHAT_DEFAULT_DURATION 5000.0f 339 | #define INCOMING_CHAT_DEFAULT_SCALE 1.0f 340 | #define INCOMING_CHAT_DEFAULT_MESSAGES_SPACING 35 341 | #define INCOMING_CHAT_MSG_MAX_LENGTH 128 342 | typedef struct incomingchatmessage_t 343 | { 344 | char msg[INCOMING_CHAT_MSG_MAX_LENGTH]; 345 | int x,y; 346 | float duration; 347 | int fontID; 348 | float scale; 349 | 350 | Timer* timer; 351 | 352 | struct incomingchatmessage_t* next; 353 | struct incomingchatmessage_t* previous; 354 | } incomingchatmessage_t; 355 | 356 | #endif 357 | -------------------------------------------------------------------------------- /src/Engine/U_Timer.c: -------------------------------------------------------------------------------- 1 | #include "U_Timer.h" 2 | 3 | // Timer functions 4 | static void U_TimerInit(Timer* timer); 5 | static void U_TimerStart(Timer* timer); 6 | static void U_TimerStop(Timer* timer); 7 | static void U_TimerPause(Timer* timer); 8 | static void U_TimerResume(Timer* timer); 9 | 10 | static unsigned int U_TimerGetTicks(Timer* timer); 11 | 12 | // Timer checks 13 | static bool U_TimerIsStarted(Timer* timer); 14 | static bool U_TimerIsPaused(Timer* timer); 15 | 16 | Timer* U_TimerCreateNew(void) 17 | { 18 | Timer* timer = (Timer*)malloc(sizeof(Timer)); 19 | 20 | timer->Init = U_TimerInit; 21 | timer->Start = U_TimerStart; 22 | timer->Stop = U_TimerStop; 23 | timer->Pause = U_TimerPause; 24 | timer->Resume = U_TimerResume; 25 | timer->GetTicks = U_TimerGetTicks; 26 | timer->IsStarted = U_TimerIsStarted; 27 | timer->IsPaused = U_TimerIsPaused; 28 | timer->Init(timer); 29 | 30 | return timer; 31 | } 32 | 33 | static void U_TimerInit(Timer* timer) 34 | { 35 | timer->timerTicks = 0; 36 | timer->pausedTicks = 0; 37 | 38 | timer->isStarted = false; 39 | timer->isPaused = false; 40 | } 41 | 42 | static void U_TimerStart(Timer* timer) 43 | { 44 | timer->isStarted = true; 45 | timer->isPaused = false; 46 | 47 | timer->timerTicks = SDL_GetTicks(); 48 | timer->pausedTicks = 0; 49 | } 50 | 51 | static void U_TimerStop(Timer* timer) 52 | { 53 | timer->timerTicks = 0; 54 | timer->pausedTicks = 0; 55 | 56 | timer->isStarted = false; 57 | timer->isPaused = false; 58 | } 59 | 60 | static void U_TimerPause(Timer* timer) 61 | { 62 | if(timer->isStarted && !timer->isPaused) 63 | { 64 | timer->pausedTicks = SDL_GetTicks() - timer->timerTicks; 65 | timer->timerTicks = 0; 66 | 67 | timer->isPaused = true; 68 | } 69 | } 70 | 71 | static void U_TimerResume(Timer* timer) 72 | { 73 | if(timer->isStarted && timer->isPaused) 74 | { 75 | timer->timerTicks = SDL_GetTicks() - timer->pausedTicks; 76 | timer->pausedTicks = 0; 77 | 78 | timer->isPaused = false; 79 | } 80 | } 81 | 82 | static unsigned int U_TimerGetTicks(Timer* timer) 83 | { 84 | unsigned int time = 0; 85 | 86 | if(timer->isStarted) 87 | { 88 | if(timer->isPaused) 89 | return timer->pausedTicks; 90 | else 91 | time = SDL_GetTicks() - timer->timerTicks; 92 | } 93 | 94 | return time; 95 | } 96 | 97 | static bool U_TimerIsStarted(Timer* timer) 98 | { 99 | return timer->isStarted; 100 | } 101 | 102 | static bool U_TimerIsPaused(Timer* timer) 103 | { 104 | return timer->isPaused; 105 | } -------------------------------------------------------------------------------- /src/Engine/U_Timer.h: -------------------------------------------------------------------------------- 1 | // Header Guard 2 | #ifndef TIMER_H_INCLUDED 3 | #define TIMER_H_INCLUDED 4 | 5 | #include 6 | #include 7 | 8 | #include "SDL.h" 9 | 10 | // ------------------------------- 11 | // Timer 12 | // ------------------------------- 13 | typedef struct Timer 14 | { 15 | unsigned int timerTicks; 16 | unsigned int pausedTicks; 17 | bool isPaused; 18 | bool isStarted; 19 | 20 | void (*Init)(struct Timer* this); 21 | 22 | void (*Start)(struct Timer* this); 23 | void (*Stop)(struct Timer* this); 24 | void (*Pause)(struct Timer* this); 25 | void (*Resume)(struct Timer* this); 26 | 27 | unsigned int (*GetTicks)(struct Timer* this); 28 | 29 | bool (*IsStarted)(struct Timer* this); 30 | bool (*IsPaused)(struct Timer* this); 31 | 32 | 33 | } Timer; 34 | 35 | // ------------------------------- 36 | // Allocates and creates a Timer 37 | // ------------------------------- 38 | Timer* U_TimerCreateNew(void); 39 | 40 | #endif -------------------------------------------------------------------------------- /src/Engine/U_Utilities.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "U_Utilities.h" 5 | 6 | void U_SetBit(byte* b, byte n) 7 | { 8 | *b |= 1 << n; 9 | } 10 | 11 | byte U_GetBit(byte* b, byte n) 12 | { 13 | return (*b >> n) & 1; 14 | } 15 | 16 | 17 | 18 | // ------------------------------- 19 | // Perform quicksorts before drawing drawables to draw from farther to nearest 20 | // ------------------------------- 21 | void U_QuicksortDrawables(drawabledata_t a[], int left, int right) 22 | { 23 | int i = left; 24 | int j = right; 25 | 26 | drawabledata_t temp = a[i]; // temp and pivot 27 | 28 | if( left < right) 29 | { 30 | while(i < j) 31 | { 32 | while(a[j].dist <= temp.dist && i < j) 33 | j--; 34 | 35 | a[i] = a[j]; 36 | 37 | while(a[i].dist >= temp.dist && i < j) 38 | i++; 39 | 40 | a[j] = a[i]; 41 | } 42 | a[i] = temp; 43 | 44 | U_QuicksortDrawables(a, left, i - 1); 45 | U_QuicksortDrawables(a, j + 1, right); 46 | } 47 | } 48 | 49 | // ------------------------------- 50 | // Perform quicksorts before drawing drawables to draw from farther to nearest 51 | // ------------------------------- 52 | void U_QuicksortWallData(walldata_t a[], int left, int right) 53 | { 54 | int i = left; 55 | int j = right; 56 | 57 | walldata_t temp = a[i]; // temp and pivot 58 | 59 | if( left < right) 60 | { 61 | while(i < j) 62 | { 63 | while(a[j].distance <= temp.distance && i < j) 64 | j--; 65 | 66 | a[i] = a[j]; 67 | 68 | while(a[i].distance >= temp.distance && i < j) 69 | i++; 70 | 71 | a[j] = a[i]; 72 | } 73 | a[i] = temp; 74 | 75 | U_QuicksortWallData(a, left, i - 1); 76 | U_QuicksortWallData(a, j + 1, right); 77 | } 78 | } -------------------------------------------------------------------------------- /src/Engine/U_Utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H_INCLUDED 2 | #define UTILITIES_H_INCLUDED 3 | 4 | #include "U_DataTypes.h" 5 | 6 | #define FIX_ANGLES(a) (a > 2*M_PI) ? a -= 2*M_PI : (a < 0) ? a += 2*M_PI : a 7 | #define FIX_ANGLES_DEGREES(a) (a > 360) ? a -= 360 : (a < 0) ? a += 360 : a 8 | #define RADIAN_TO_DEGREE 57.2957795131 9 | 10 | void U_SetBit(byte* b, byte n); 11 | byte U_GetBit(byte* b, byte n); 12 | 13 | // ------------------------------- 14 | // Perform quicksorts before drawing drawables to draw from farther to nearest 15 | // ------------------------------- 16 | void U_QuicksortDrawables(drawabledata_t drawables[], int first, int last); 17 | 18 | // ------------------------------- 19 | // Perform quicksorts before drawing drawables to draw from farther to nearest 20 | // ------------------------------- 21 | void U_QuicksortWallData(walldata_t data[], int first, int last); 22 | #endif -------------------------------------------------------------------------------- /src/Network/net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "netdef.h" 5 | #include "packet.h" 6 | #include "../Online/O_Lobby.h" 7 | 8 | netplayer_t thisPlayer; 9 | netplayer_t otherPlayer; 10 | 11 | // Input related 12 | bool wantsToAbortHosting = FALSE; 13 | bool wantsToAbortJoining = FALSE; 14 | 15 | // Used to store values when joining game 16 | static char remoteAddress[1024] = "\0"; 17 | static u_short remotePort = 0; 18 | 19 | int NET_InitializeNet(void) 20 | { 21 | WORD wVersion = MAKEWORD(2,2); 22 | WSADATA wsaData; 23 | 24 | WSAStartup(wVersion, &wsaData); 25 | 26 | PCKT_ZeroBuffer(&inputPcktBuffer); 27 | PCKT_ZeroBuffer(&outputPcktBuffer); 28 | 29 | NET_InitializeNetPlayers(); 30 | 31 | return 0; 32 | } 33 | 34 | void NET_InitializeNetPlayers(void) 35 | { 36 | thisPlayer.id = 0; 37 | thisPlayer.socket = -1; 38 | //thisPlayer.address = 0; 39 | thisPlayer.status = NETSTS_NULL; 40 | thisPlayer.isHost = false; 41 | thisPlayer.name[0] = '\0'; 42 | //thisPlayer.favoriteClass = CLASS_TANK; // The class contained in the greet packet, used to initialize the lobby state and set in the Options main menu 43 | thisPlayer.selectedClass = CLASS_TANK; 44 | thisPlayer.isReady = false; 45 | thisPlayer.startingGame = false; 46 | thisPlayer.gameStated = false; 47 | thisPlayer.curWeapon = 0; 48 | thisPlayer.curSpell = 0; 49 | thisPlayer.dead = false; 50 | 51 | otherPlayer.id = 0; 52 | otherPlayer.socket = -1; 53 | //otherPlayer.address = 0; 54 | otherPlayer.status = NETSTS_NULL; 55 | otherPlayer.isHost = false; 56 | otherPlayer.name[0] = '\0'; 57 | //otherPlayer.favoriteClass = CLASS_TANK; // The class contained in the greet packet, used to initialize the lobby state and set in the Options main menu 58 | otherPlayer.selectedClass = CLASS_TANK; 59 | otherPlayer.isReady = false; 60 | otherPlayer.startingGame = false; 61 | otherPlayer.gameStated = false; 62 | otherPlayer.curWeapon = 0; 63 | otherPlayer.curSpell = 0; 64 | otherPlayer.dead = false; 65 | } 66 | 67 | 68 | int NET_HostGameProcedure(char* passedUsername) 69 | { 70 | strcpy(thisPlayer.name, passedUsername); 71 | 72 | // Player 1 is the host 73 | thisPlayer.id = 0; 74 | 75 | // Set who is host 76 | thisPlayer.isHost = true; 77 | otherPlayer.isHost = false; 78 | 79 | // Create the socket to listen 80 | thisPlayer.socket = INVALID_SOCKET; 81 | thisPlayer.socket = socket(AF_INET, SOCK_STREAM, 0); 82 | if(thisPlayer.socket == INVALID_SOCKET) 83 | { 84 | printf("Error creating a socket while hosting a game | WSAError: %d\n", WSAGetLastError()); 85 | WSACleanup(); 86 | return 1; 87 | } 88 | 89 | // Set the listen socket to be non-blocking 90 | u_long iMode = 1; 91 | ioctlsocket(thisPlayer.socket, FIONBIO, &iMode); 92 | 93 | // Set address of the host 94 | memset(thisPlayer.address.sin_zero, 0, sizeof(thisPlayer.address.sin_zero)); 95 | thisPlayer.address.sin_family = AF_INET; 96 | thisPlayer.address.sin_addr.S_un.S_addr = htons(INADDR_ANY); 97 | thisPlayer.address.sin_port = htons(NET_LISTENING_PORT); 98 | int hostPlayerAddressLen = sizeof(thisPlayer.address); 99 | 100 | printf("IP Address: %s:%d\n", inet_ntoa(thisPlayer.address.sin_addr), (int)ntohs(thisPlayer.address.sin_port)); 101 | 102 | // Attempt to bind 103 | if(bind(thisPlayer.socket, (struct sockaddr*)&thisPlayer.address, hostPlayerAddressLen) < 0) 104 | { 105 | printf("Error binding a socket while hosting a game | WSAError: %d\n", WSAGetLastError()); 106 | WSACleanup(); 107 | return 2; 108 | } 109 | 110 | // Set socket to listen mode 111 | if(listen(thisPlayer.socket, SOMAXCONN) < 0) 112 | { 113 | printf("Error setting a socket to listen while hosting a game | WSAError: %d\n", WSAGetLastError()); 114 | WSACleanup(); 115 | return 3; 116 | } 117 | 118 | int flag = 1; 119 | int result = setsockopt(thisPlayer.socket, /* socket affected */ 120 | IPPROTO_TCP, /* set option at TCP level */ 121 | TCP_NODELAY, /* name of option */ 122 | (char *) &flag, /* the cast is historical cruft */ 123 | sizeof(int)); /* length of option value */ 124 | if (result < 0) 125 | printf("Error while disabling Nagle's Alg.\n"); 126 | 127 | printf("Disable Nagle returned %d\n", result); 128 | 129 | // Other player hasn't connected 130 | otherPlayer.status = NETSTS_NULL; 131 | 132 | printf("Hosting procedure initialized.\n"); 133 | 134 | return 0; 135 | } 136 | 137 | int NET_HostGameWaitForConnection(void) 138 | { 139 | // Accept new connections 140 | bool otherPlayerConnected = FALSE; 141 | if(!otherPlayerConnected && !wantsToAbortHosting) 142 | { 143 | struct sockaddr_in otherAddress; 144 | int otherAddressLength = sizeof(otherAddress); 145 | SOCKET acceptedSocket = accept(thisPlayer.socket, (struct sockaddr*)&otherAddress, &otherAddressLength); 146 | 147 | // Wait until other player connects 148 | if(acceptedSocket != SOCKET_ERROR) 149 | { 150 | printf("A player connected! | Addr: %s:%d\n", inet_ntoa(otherAddress.sin_addr), (int)ntohs(otherAddress.sin_port)); 151 | 152 | // Set other player's socket to be non blocking 153 | u_long iMode = 1; 154 | ioctlsocket(acceptedSocket, FIONBIO, &iMode); 155 | int flag = 1; 156 | int result = setsockopt(acceptedSocket, /* socket affected */ 157 | IPPROTO_TCP, /* set option at TCP level */ 158 | TCP_NODELAY, /* name of option */ 159 | (char *) &flag, /* the cast is historical cruft */ 160 | sizeof(int)); /* length of option value */ 161 | if (result < 0) 162 | printf("Error while disabling Nagle's Alg.\n"); 163 | 164 | printf("Disable Nagle returned %d\n", result); 165 | 166 | 167 | otherPlayer.id = 1; 168 | 169 | // Copy inner struct data to otherPlayer 170 | otherPlayer.socket = acceptedSocket; 171 | otherPlayer.address = otherAddress; 172 | otherPlayerConnected = TRUE; 173 | otherPlayer.status = NETSTS_JUST_CONNECTED; 174 | } 175 | } 176 | 177 | 178 | if(otherPlayerConnected) 179 | { 180 | // Close the listening socket 181 | closesocket(thisPlayer.socket); 182 | 183 | printf("Other player has connected, closing listening socket.\n"); 184 | return 0; 185 | } 186 | else if(wantsToAbortHosting) 187 | { 188 | NET_HostGameAbortConnection(); 189 | return 2; 190 | } 191 | else 192 | return 1; 193 | } 194 | 195 | int NET_HostGameAbortConnection(void) 196 | { 197 | printf("Listening was aborted by user.\n"); 198 | 199 | // Close the socket 200 | closesocket(thisPlayer.socket); 201 | 202 | return 0; 203 | } 204 | 205 | int NET_HostGameWaitForGreet(void) 206 | { 207 | printf("HOST WAIT FOR GREET\n"); 208 | 209 | // When this function gets called, the packet arrived on the PCKT_ReceivePacket call and was saved inside the inputPacketBuffer->buffer 210 | // At this point, receivedPacket points at the inputPacketBuffer->buffer that contains the packet that arrived 211 | pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 212 | 213 | if(receivedPacket->id == PCKTID_GREET) 214 | { 215 | // Manage packet, if receivedPacket->id == PCKT_GREET: 216 | pckt_greet_t greetPacket; 217 | memcpy(&greetPacket, receivedPacket->data, sizeof(greetPacket)); 218 | 219 | printf("Packet received! ID: %d | Greet value: %s | Class: %d\n", receivedPacket->id, greetPacket.name, greetPacket.favoriteClass); 220 | 221 | // Parse packet 222 | strcpy(otherPlayer.name, greetPacket.name); 223 | otherPlayer.favoriteClass = greetPacket.favoriteClass; 224 | 225 | otherPlayer.status = NETSTS_HAVE_TO_GREET; 226 | return 0; 227 | } 228 | } 229 | 230 | int NET_HostGameMakeGreetPacket(void) 231 | { 232 | // Make greet packet 233 | pckt_t* greetPacket = PCKT_MakeGreetPacket(&packetToSend, thisPlayer.name, thisPlayer.favoriteClass); 234 | 235 | if(outputPcktBuffer.packetsToWrite < MAX_PCKTS_PER_BUFFER) 236 | { 237 | // Store the greet packet in the output buffer 238 | outputPcktBuffer.hasBegunWriting = TRUE; 239 | memcpy(outputPcktBuffer.buffer+(outputPcktBuffer.packetsToWrite*PCKT_SIZE), (char*)greetPacket, PCKT_SIZE); 240 | outputPcktBuffer.packetsToWrite++; 241 | printf("GREET PACKET MADE!\n"); 242 | } 243 | else 244 | { 245 | printf("CRITICAL ERROR: Send buffer was full when in NET_HostGameMakeGreetPacket\n"); 246 | } 247 | } 248 | 249 | int NET_HostGameSendGreet(void) 250 | { 251 | // When this function gets called, the packet got sent on the PCKT_SendPacket call and was saved inside the output->buffer 252 | // At this point, receivedPacket points at the output->buffer that contains the packet that arrived 253 | pckt_t* sent = (pckt_t*)outputPcktBuffer.buffer; 254 | 255 | printf("Host sent greet %s\n", sent); 256 | 257 | O_LobbyDefineClassesHostwise(); 258 | 259 | otherPlayer.status = NETSTS_GREETED; 260 | } 261 | 262 | 263 | int NET_JoinGameProcedure(char* passedUsername, char* passedIP, char* passedPort) 264 | { 265 | printf("Joining a game...\n"); 266 | 267 | otherPlayer.status = NETSTS_NULL; 268 | 269 | // Set basic info 270 | otherPlayer.id = 0; 271 | 272 | // Set who is host 273 | thisPlayer.isHost = false; 274 | otherPlayer.isHost = true; 275 | 276 | // Create socket 277 | otherPlayer.socket = INVALID_SOCKET; 278 | otherPlayer.socket = socket(AF_INET, SOCK_STREAM, 0); 279 | if(otherPlayer.socket == INVALID_SOCKET) 280 | { 281 | printf("Error creating a socket while joining a game | WSAError: %d\n", WSAGetLastError()); 282 | WSACleanup(); 283 | return 1; 284 | } 285 | 286 | // Set other player's socket to be non blocking 287 | u_long iMode = 1; 288 | ioctlsocket(otherPlayer.socket, FIONBIO, &iMode); 289 | 290 | 291 | // Setup address 292 | memset(otherPlayer.address.sin_zero, 0, sizeof(otherPlayer.address.sin_zero)); 293 | otherPlayer.address.sin_family = AF_INET; 294 | otherPlayer.address.sin_addr.S_un.S_addr = INADDR_ANY; 295 | otherPlayer.address.sin_port = htons(0); 296 | 297 | // Attempt to bind 298 | if(bind(otherPlayer.socket, (struct sockaddr*)&otherPlayer.address, sizeof(otherPlayer.address)) < 0) 299 | { 300 | printf("Error binding a socket while joining a game | WSAError: %d\n", WSAGetLastError()); 301 | WSACleanup(); 302 | return 2; 303 | } 304 | 305 | // Get server address and port 306 | strcpy(thisPlayer.name, passedUsername); 307 | strcpy(remoteAddress, passedIP); 308 | remotePort = atoi(passedPort); 309 | 310 | // Setup host 311 | struct sockaddr_in hostAddress; 312 | int hostAddressLen = sizeof(hostAddress); 313 | memset(hostAddress.sin_zero, 0, sizeof(hostAddress.sin_zero)); 314 | 315 | hostAddress.sin_family = AF_INET; 316 | hostAddress.sin_addr.S_un.S_addr = inet_addr(remoteAddress); 317 | hostAddress.sin_port = htons(remotePort); 318 | 319 | printf("Inserted IP Address: %s:%d\n", inet_ntoa(hostAddress.sin_addr), (int)ntohs(hostAddress.sin_port)); 320 | printf("Attempting to connect...\n"); 321 | 322 | // Attempt to connect 323 | int conn = connect(otherPlayer.socket, (struct sockaddr*)&hostAddress, hostAddressLen); 324 | printf("CONN VALUE: %d\n", conn); 325 | // If connection was instant 326 | if(conn == 0) 327 | { 328 | NET_JoinGameOnConnectionEstabilishes(); 329 | return 0; 330 | } 331 | // If we need to wait for connection 332 | else if(WSAGetLastError() == WSAEWOULDBLOCK) 333 | { 334 | return 0; 335 | } 336 | else 337 | { 338 | printf("Connection failed.\n"); 339 | return 3; 340 | } 341 | } 342 | 343 | int NET_JoinGameWaitForConnection(void) 344 | { 345 | printf("Waiting for connection...\n"); 346 | 347 | // If connection is already estabilished 348 | if(otherPlayer.status == NETSTS_JUST_CONNECTED) 349 | { 350 | return 3; 351 | } 352 | else 353 | { 354 | // Wait for it 355 | fd_set connectionSet; 356 | FD_ZERO(&connectionSet); 357 | 358 | FD_SET(otherPlayer.socket, &connectionSet); 359 | 360 | struct timeval waitVal; 361 | waitVal.tv_sec = 0; 362 | 363 | int result = select(0, NULL, &connectionSet, NULL, &waitVal); 364 | 365 | // If process ended 366 | if(result > 0) 367 | { 368 | if(FD_ISSET(otherPlayer.socket, &connectionSet)) 369 | { 370 | // Connection happened, find out what happened 371 | struct sockaddr_in connectedAddr; 372 | int connectedAddrLen = sizeof(connectedAddr); 373 | memset(&connectedAddr, 0, sizeof(connectedAddr)); 374 | if (getpeername(otherPlayer.socket, (struct sockaddr *)&connectedAddr, &connectedAddrLen) == 0) 375 | { 376 | printf("TCP Connection succeeded!\n"); 377 | NET_JoinGameOnConnectionEstabilishes(); 378 | return 0; 379 | } 380 | else 381 | { 382 | printf("TCP Connection failed!\n"); 383 | return 4; 384 | } 385 | } 386 | } 387 | else if(wantsToAbortJoining) 388 | { 389 | NET_JoinGameAbortConnection(); 390 | return 2; 391 | } 392 | 393 | return 1; 394 | } 395 | } 396 | 397 | int NET_JoinGameAbortConnection(void) 398 | { 399 | printf("Joining was aborted by user.\n"); 400 | 401 | // Close the socket 402 | closesocket(otherPlayer.socket); 403 | 404 | return 0; 405 | } 406 | 407 | int NET_JoinGameOnConnectionEstabilishes(void) 408 | { 409 | printf("Connection estabilished!\n"); 410 | 411 | int flag = 1; 412 | int result = setsockopt(otherPlayer.socket, /* socket affected */ 413 | IPPROTO_TCP, /* set option at TCP level */ 414 | TCP_NODELAY, /* name of option */ 415 | (char *) &flag, /* the cast is historical cruft */ 416 | sizeof(int)); /* length of option value */ 417 | if (result < 0) 418 | printf("Error while disabling Nagle's Alg.\n"); 419 | 420 | printf("Disable Nagle returned %d\n", result); 421 | 422 | otherPlayer.status = NETSTS_JUST_CONNECTED; 423 | 424 | return 0; 425 | } 426 | 427 | int NET_JoinGameWaitForGreet(void) 428 | { 429 | // When this function gets called, the packet arrived on the PCKT_ReceivePacket call and was saved inside the inputPacketBuffer->buffer 430 | // At this point, receivedPacket points at the inputPacketBuffer->buffer that contains the packet that arrived 431 | pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 432 | 433 | if(receivedPacket->id == PCKTID_GREET) 434 | { 435 | // Manage packet, if receivedPacket->id == PCKT_GREET: 436 | pckt_greet_t greetPacket; 437 | memcpy(&greetPacket, receivedPacket->data, sizeof(greetPacket)); 438 | 439 | printf("Packet received! ID: %d | Greet value: %s - Class: %d\n", receivedPacket->id, greetPacket.name, greetPacket.favoriteClass); 440 | 441 | // Parse packet 442 | strcpy(otherPlayer.name, greetPacket.name); 443 | otherPlayer.favoriteClass = greetPacket.favoriteClass; 444 | 445 | O_LobbyDefineClassesJoinerwise(); 446 | 447 | otherPlayer.status = NETSTS_GREETED; 448 | 449 | return 0; 450 | } 451 | } 452 | 453 | int NET_JoinGameMakeGreetPacket(void) 454 | { 455 | // Make greet packet 456 | pckt_t* greetPacket = PCKT_MakeGreetPacket(&packetToSend, thisPlayer.name, thisPlayer.favoriteClass); 457 | 458 | if(outputPcktBuffer.packetsToWrite < MAX_PCKTS_PER_BUFFER) 459 | { 460 | // Store the greet packet in the output buffer 461 | outputPcktBuffer.hasBegunWriting = TRUE; 462 | memcpy(outputPcktBuffer.buffer+(outputPcktBuffer.packetsToWrite*PCKT_SIZE), (char*)greetPacket, PCKT_SIZE); 463 | outputPcktBuffer.packetsToWrite++; 464 | printf("GREET PACKET MADE!\n"); 465 | } 466 | else 467 | { 468 | printf("CRITICAL ERROR: Send buffer was full when in NET_JoinGameMakeGreetPacket\n"); 469 | } 470 | } 471 | 472 | int NET_JoinGameSendGreet(void) 473 | { 474 | // When this function gets called, the packet got sent on the PCKT_SendPacket call and was saved inside the output->buffer 475 | // At this point, receivedPacket points at the output->buffer that contains the packet that arrived 476 | pckt_t* sent = (pckt_t*)outputPcktBuffer.buffer; 477 | 478 | printf("Join sent greet %s\n", sent); 479 | otherPlayer.status = NETSTS_HAVE_TO_GREET; 480 | } -------------------------------------------------------------------------------- /src/Network/netdef.h: -------------------------------------------------------------------------------- 1 | /// --------------------------------------------------------------- 2 | /// Header that describes network entities 3 | /// --------------------------------------------------------------- 4 | 5 | #ifndef NETDEF_H_INCLUDED 6 | #define NETDEF_H_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | #include "../Online/O_GameDef.h" 12 | 13 | #define NET_MAX_PLAYER_NAME_LENGTH 24 14 | #define NET_LISTENING_PORT 61530 15 | 16 | // Enum that describes all the states the other player can be in 17 | typedef enum netstatus 18 | { 19 | NETSTS_NULL = 0, 20 | NETSTS_JUST_CONNECTED, 21 | NETSTS_HAVE_TO_GREET, 22 | NETSTS_GREETED, 23 | NETSTS_PREPARING, 24 | NETSTS_READY, 25 | NETSTS_PLAYING, 26 | NETSTS_DISCONNECTED 27 | } netstatus_e; 28 | 29 | // netplayer is used to represent a player on the net (other player) 30 | typedef struct netplayer 31 | { 32 | // Status 33 | unsigned int id; 34 | SOCKET socket; 35 | struct sockaddr_in address; 36 | netstatus_e status; 37 | bool isHost; 38 | 39 | // Host/Join 40 | char name[NET_MAX_PLAYER_NAME_LENGTH]; 41 | playableclasses_e favoriteClass; // The class contained in the greet packet, used to initialize the lobby state and set in the Options main menu 42 | 43 | // Lobby 44 | playableclasses_e selectedClass; 45 | bool isReady; 46 | bool startingGame; 47 | bool gameStated; 48 | 49 | // Game 50 | // Object specific details like health and mana are in otherPlayerObject 51 | int curWeapon; 52 | int curSpell; 53 | 54 | bool dead; 55 | } netplayer_t; 56 | 57 | extern netplayer_t thisPlayer; 58 | extern netplayer_t otherPlayer; 59 | 60 | extern bool wantsToAbortHosting; 61 | extern bool wantsToAbortJoining; 62 | 63 | 64 | // Initializes network related stuff 65 | int NET_InitializeNet(void); 66 | void NET_InitializeNetPlayers(void); 67 | 68 | // Starts the procedure to host a game 69 | int NET_HostGameProcedure(char* passedUsername); 70 | int NET_HostGameWaitForConnection(void); 71 | int NET_HostGameWaitForGreet(void); 72 | 73 | int NET_HostGameMakeGreetPacket(void); 74 | int NET_HostGameSendGreet(void); 75 | 76 | int NET_HostGameAbortConnection(void); 77 | 78 | // Starts the procedure to join a game 79 | int NET_JoinGameProcedure(char* passedUsername, char* passedIP, char* passedPort); 80 | int NET_JoinGameWaitForConnection(void); 81 | int NET_JoinGameOnConnectionEstabilishes(void); 82 | int NET_JoinGameWaitForGreet(void); 83 | 84 | int NET_JoinGameMakeGreetPacket(void); 85 | int NET_JoinGameSendGreet(void); 86 | 87 | int NET_JoinGameAbortConnection(void); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/Network/packet.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "packet.h" 3 | 4 | // Global packet data to send out 5 | pckt_t packetToSend; 6 | 7 | // Input buffer when receiving packets 8 | pckt_buffer_t inputPcktBuffer; 9 | 10 | // Output buffer when sending packets 11 | pckt_buffer_t outputPcktBuffer; 12 | 13 | void PCKT_Zero(pckt_t* p) 14 | { 15 | p->id = 0; 16 | memset(p->data, 0, PCKT_SIZE); 17 | } 18 | 19 | void PCKT_ZeroBuffer(pckt_buffer_t* pBuf) 20 | { 21 | memset(pBuf->buffer, 0, PCKT_SIZE); 22 | pBuf->len = 0; 23 | pBuf->shorted = FALSE; 24 | pBuf->hasBegunWriting = FALSE; 25 | pBuf->packetsToWrite = 0; 26 | pBuf->packetOffset = 0; 27 | } 28 | 29 | int PCKT_ReceivePacket(int (*OnPacketArrives)(void)) 30 | { 31 | int recvVal = 0; 32 | recvVal = recv(otherPlayer.socket, inputPcktBuffer.buffer+inputPcktBuffer.len, (PCKT_SIZE*MAX_PCKTS_PER_BUFFER)-inputPcktBuffer.len, 0); 33 | 34 | // If invalid 35 | if(recvVal < 0) 36 | { 37 | if(WSAGetLastError() != WSAEWOULDBLOCK) 38 | { 39 | printf("Receive Error. RecvVal = %d | WSAError: %d\n", recvVal, WSAGetLastError()); 40 | return PCKT_RECEIVE_RETURN_ERROR; 41 | } 42 | else 43 | return 1; 44 | } 45 | 46 | //printf("Receive val: %d\n", recvVal); 47 | 48 | bool allConsumed = false; 49 | while(!allConsumed) 50 | { 51 | if(inputPcktBuffer.shorted) 52 | { 53 | // We are waiting for a fragment 54 | int avail = PCKT_SIZE - inputPcktBuffer.len; 55 | printf("Fragment Received! Size: %d\n", recvVal); 56 | 57 | // If we got just what we were waiting for 58 | if(recvVal >= avail) 59 | { 60 | inputPcktBuffer.len += avail; 61 | 62 | // Reconstruct the packet 63 | if(inputPcktBuffer.len == PCKT_SIZE) 64 | { 65 | //pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 66 | //OnPacketArrives must do the conversion, it may use the inputBuffer for its all purpose or allocate a pckt_t and release the inputBuffer for future use 67 | printf("Packet recostructed!\n"); 68 | OnPacketArrives(); 69 | 70 | inputPcktBuffer.packetOffset += PCKT_SIZE; 71 | inputPcktBuffer.len = 0; 72 | inputPcktBuffer.shorted = FALSE; 73 | recvVal -= avail; 74 | 75 | } 76 | else 77 | { 78 | // How? 79 | printf("Critical error. CODE-01\n"); 80 | return 2; 81 | } 82 | } 83 | else // antoher fragment 84 | { 85 | // Short received, save what we got so far 86 | printf("Double shorted Val: %d\n", recvVal); 87 | inputPcktBuffer.shorted = TRUE; 88 | inputPcktBuffer.len += recvVal; 89 | } 90 | } 91 | else if(recvVal >= PCKT_SIZE) 92 | { 93 | //pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 94 | //OnPacketArrives must do the conversion, it may use the inputBuffer for its all purpose or allocate a pckt_t and release the inputBuffer for future use 95 | OnPacketArrives(); 96 | inputPcktBuffer.packetOffset += PCKT_SIZE; 97 | recvVal -= PCKT_SIZE; 98 | } 99 | else 100 | { 101 | if(recvVal == 0) 102 | { 103 | allConsumed = true; 104 | PCKT_ZeroBuffer(&inputPcktBuffer); 105 | } 106 | else 107 | { 108 | // Short receive 109 | printf("Short received value of %d\n", recvVal); 110 | inputPcktBuffer.shorted = true; 111 | inputPcktBuffer.len = recvVal; 112 | allConsumed = true; 113 | 114 | memmove(inputPcktBuffer.buffer, inputPcktBuffer.buffer+inputPcktBuffer.packetOffset, recvVal); 115 | inputPcktBuffer.packetOffset = 0; 116 | } 117 | } 118 | } 119 | } 120 | 121 | int PCKT_SendPacket(int (*OnPacketIsSent)(void)) 122 | { 123 | if(!outputPcktBuffer.hasBegunWriting) 124 | { 125 | //printf("There is nothing to write.\n"); 126 | return 104; 127 | } 128 | 129 | while(outputPcktBuffer.packetsToWrite > 0) 130 | { 131 | int sendVal = 0; 132 | if(!outputPcktBuffer.shorted) 133 | { 134 | sendVal = send(otherPlayer.socket, outputPcktBuffer.buffer+(outputPcktBuffer.packetOffset), PCKT_SIZE, 0); 135 | 136 | // If invalid 137 | if(sendVal < 0) 138 | { 139 | if(WSAGetLastError() != WSAEWOULDBLOCK) 140 | { 141 | printf("Send Error. SendVal = %d | WSAError: %d\n", sendVal, WSAGetLastError()); 142 | return 2; 143 | } 144 | else 145 | return 1; 146 | } 147 | 148 | //printf("%d Sent!\n", sendVal); 149 | 150 | if(sendVal > 0) 151 | { 152 | if(sendVal >= PCKT_SIZE) 153 | { 154 | //pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 155 | //OnPacketIsSent must do the conversion, it may use the outputBuffer for its all purpose or allocate a pckt_t and release the outputBuffer for future use 156 | int sVal = OnPacketIsSent(); 157 | 158 | if(sVal == PCKT_SEND_AND_CLOSE) 159 | { 160 | return 1; 161 | } 162 | 163 | // Check if there are other packets queued 164 | if(outputPcktBuffer.packetsToWrite > 1) 165 | { 166 | // Don't zero the buffer, increment the offset and discard if previous information was shorted 167 | outputPcktBuffer.packetOffset += PCKT_SIZE; 168 | outputPcktBuffer.packetsToWrite--; 169 | outputPcktBuffer.shorted = FALSE; 170 | outputPcktBuffer.len = 0; 171 | 172 | // Move the remaining packets at the beginning of the buffer so that next packets are appended 173 | memmove(outputPcktBuffer.buffer, outputPcktBuffer.buffer+outputPcktBuffer.packetOffset, sizeof(outputPcktBuffer.buffer)-outputPcktBuffer.packetOffset); 174 | outputPcktBuffer.packetOffset -= PCKT_SIZE; 175 | } 176 | else 177 | { 178 | // Reset the outuput buffer, it did all it had to do 179 | PCKT_ZeroBuffer(&outputPcktBuffer); 180 | } 181 | } 182 | else 183 | { 184 | // Short send, save what we sent so far 185 | outputPcktBuffer.shorted = TRUE; 186 | outputPcktBuffer.len = sendVal; 187 | } 188 | } 189 | } 190 | else 191 | { 192 | // We couldn't send the whole thing, we have to send another fragment 193 | int avail = PCKT_SIZE - outputPcktBuffer.len; 194 | 195 | int sendValue = send(otherPlayer.socket, outputPcktBuffer.buffer+(outputPcktBuffer.packetOffset)+outputPcktBuffer.len, avail, 0); 196 | 197 | // If invalid 198 | if(sendValue < 0) 199 | { 200 | if(WSAGetLastError() != WSAEWOULDBLOCK) 201 | { 202 | printf("SendFrag Error. SendVal = %d | WSAError: %d\n", sendVal, WSAGetLastError()); 203 | return 2; 204 | } 205 | else 206 | return 1; 207 | } 208 | 209 | printf("Fragment Sent! Size: %d\n", sendVal); 210 | 211 | if(sendValue == avail) 212 | { 213 | // We are done sending everyting 214 | 215 | outputPcktBuffer.shorted = FALSE; 216 | outputPcktBuffer.len += sendVal; 217 | 218 | if(outputPcktBuffer.len == PCKT_SIZE) 219 | { 220 | //pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 221 | //OnPacketIsSent must do the conversion, it may use the outputBuffer for its all purpose or allocate a pckt_t and release the outputBuffer for future use 222 | OnPacketIsSent(); 223 | 224 | // Check if there are other packets queued 225 | if(outputPcktBuffer.packetsToWrite > 1) 226 | { 227 | // Don't zero the buffer, increment the offset 228 | outputPcktBuffer.packetOffset += PCKT_SIZE; 229 | outputPcktBuffer.packetsToWrite--; 230 | outputPcktBuffer.shorted = FALSE; 231 | outputPcktBuffer.len = 0; 232 | 233 | // Move the remaining packets at the beginning of the buffer so that next packets are appended 234 | memmove(outputPcktBuffer.buffer, outputPcktBuffer.buffer+outputPcktBuffer.packetOffset, sizeof(outputPcktBuffer.buffer)-outputPcktBuffer.packetOffset); 235 | outputPcktBuffer.packetOffset -= PCKT_SIZE; 236 | } 237 | else 238 | { 239 | // Reset the outuput buffer, it did all it had to do 240 | PCKT_ZeroBuffer(&outputPcktBuffer); 241 | } 242 | } 243 | else 244 | { 245 | // How? 246 | printf("Critical error. CODE-02\n"); 247 | return 2; 248 | } 249 | } 250 | else 251 | { 252 | // We need to send yet another fragment 253 | // Short sent, save what we got so far 254 | printf("DOUBLE FRAGMENTED\n\n"); 255 | outputPcktBuffer.shorted = TRUE; 256 | outputPcktBuffer.len += sendVal; 257 | } 258 | } 259 | } 260 | 261 | return 0; 262 | } 263 | 264 | pckt_t* PCKT_MakeGreetPacket(pckt_t* packet, char pName[NET_MAX_PLAYER_NAME_LENGTH], byte pFavClass) 265 | { 266 | PCKT_Zero(packet); 267 | 268 | // Create the packet 269 | packet->protocol = PROT_ID_TCP; 270 | packet->id = PCKTID_GREET; 271 | 272 | // Create and fill the content 273 | pckt_greet_t content; 274 | strcpy(content.name, pName); 275 | content.favoriteClass = pFavClass; 276 | 277 | // Convert content as packet.data 278 | memcpy(packet->data, &content, sizeof(content)); 279 | 280 | return packet; 281 | } 282 | 283 | pckt_t* PCKT_MakeSetClassPacket(pckt_t* packet, byte pClassToSet) 284 | { 285 | PCKT_Zero(packet); 286 | 287 | // Create the packet 288 | packet->protocol = PROT_ID_TCP; 289 | packet->id = PCKTID_SET_CLASS; 290 | 291 | // Create and fill the content 292 | pckt_set_class_t content; 293 | content.classSet = pClassToSet; 294 | 295 | // Convert content as packet.data 296 | memcpy(packet->data, &content, sizeof(content)); 297 | 298 | return packet; 299 | } 300 | 301 | pckt_t* PCKT_MakeReadyPacket(pckt_t* packet, byte pIsReady) 302 | { 303 | PCKT_Zero(packet); 304 | 305 | // Create the packet 306 | packet->protocol = PROT_ID_TCP; 307 | packet->id = PCKTID_READY; 308 | 309 | // Create and fill the content 310 | pckt_ready_t content; 311 | content.isReady = pIsReady; 312 | 313 | // Convert content as packet.data 314 | memcpy(packet->data, &content, sizeof(content)); 315 | 316 | return packet; 317 | } 318 | 319 | pckt_t* PCKT_MakeStartingPacket(pckt_t* packet, byte pStartingValue) 320 | { 321 | PCKT_Zero(packet); 322 | 323 | // Create the packet 324 | packet->protocol = PROT_ID_TCP; 325 | packet->id = PCKTID_STARTING; 326 | 327 | // Create and fill the content 328 | pckt_starting_t content; 329 | content.starting = pStartingValue; 330 | 331 | // Convert content as packet.data 332 | memcpy(packet->data, &content, sizeof(content)); 333 | 334 | return packet; 335 | } 336 | 337 | 338 | pckt_t* PCKT_MakePlayerUpdatePacket(pckt_t* packet, float pX, float pY, float pZ, float pAngle, float pCurHealth, float pMaxHealth, float pCurMana, float pMaxMana, int pCurWeapon, int pCurSpell) 339 | { 340 | PCKT_Zero(packet); 341 | 342 | // Create the packet 343 | packet->protocol = PROT_ID_TCP; 344 | packet->id = PCKTID_PLAYERUPDATE; 345 | 346 | // Create and fill the content 347 | pckt_playerupdate_t content; 348 | content.x = pX; 349 | content.y = pY; 350 | content.z = pZ; 351 | content.angle = pAngle; 352 | content.curHealth = pCurHealth; 353 | content.maxHealth = pMaxHealth; 354 | content.curMana = pCurMana; 355 | content.maxMana = pMaxMana; 356 | content.curWeapon = pCurWeapon; 357 | content.curSpell = pCurSpell; 358 | 359 | // Convert content as packet.data 360 | memcpy(packet->data, &content, sizeof(content)); 361 | 362 | return packet; 363 | } 364 | 365 | pckt_t* PCKT_MakeDoorChangePacket(pckt_t* packet, int pLevel, int pX, int pY, int pState) 366 | { 367 | PCKT_Zero(packet); 368 | 369 | // Create the packet 370 | packet->protocol = PROT_ID_TCP; 371 | packet->id = PCKTID_DOOR_CHANGE; 372 | 373 | // Create and fill the content 374 | pckt_door_change_t content; 375 | content.level = pLevel; 376 | content.x = pX; 377 | content.y = pY; 378 | content.state = pState; 379 | 380 | // Convert content as packet.data 381 | memcpy(packet->data, &content, sizeof(content)); 382 | 383 | return packet; 384 | } 385 | 386 | pckt_t* PCKT_MakePickupPickedPacket(pckt_t* packet, int pLevel, int pX, int pY) 387 | { 388 | PCKT_Zero(packet); 389 | 390 | // Create the packet 391 | packet->protocol = PROT_ID_TCP; 392 | packet->id = PCKTID_PICKUP_PICKED; 393 | 394 | // Create and fill the content 395 | pckt_pickup_picked_t content; 396 | content.level = pLevel; 397 | content.x = pX; 398 | content.y = pY; 399 | 400 | // Convert content as packet.data 401 | memcpy(packet->data, &content, sizeof(content)); 402 | 403 | return packet; 404 | } 405 | 406 | pckt_t* PCKT_MakeProjectileSpawnPacket(pckt_t* packet, unsigned pLength, packedprojectilespawn_t pProjectiles[MAX_PROJECTILES_TO_SEND_SIZE]) 407 | { 408 | PCKT_Zero(packet); 409 | 410 | // Create the packet 411 | packet->protocol = PROT_ID_TCP; 412 | packet->id = PCKTID_PROJECTILE_SPAWN; 413 | 414 | // Create and fill the content 415 | pckt_projectile_spawn_t content; 416 | content.length = pLength; 417 | 418 | for(int i = 0; i < content.length; i++) 419 | { 420 | content.projectiles[i] = pProjectiles[i]; 421 | } 422 | 423 | // Convert content as packet.data 424 | memcpy(packet->data, &content, sizeof(content)); 425 | 426 | return packet; 427 | } 428 | 429 | pckt_t* PCKT_MakeProjectileDestrPacket(pckt_t* packet, int pNetworkID, int pSpriteID, bool pForceDestroy) 430 | { 431 | PCKT_Zero(packet); 432 | 433 | // Create the packet 434 | packet->protocol = PROT_ID_TCP; 435 | packet->id = PCKTID_PROJECTILE_DESTR; 436 | 437 | // Create and fill the content 438 | pckt_projectile_destr_t content; 439 | content.networkID = pNetworkID; 440 | content.spriteID = pSpriteID; 441 | content.forceDestroy = pForceDestroy; 442 | 443 | // Convert content as packet.data 444 | memcpy(packet->data, &content, sizeof(content)); 445 | 446 | return packet; 447 | } 448 | 449 | pckt_t* PCKT_MakeAIMovementUpdatePacket(pckt_t* packet) 450 | { 451 | PCKT_Zero(packet); 452 | 453 | // Create the packet 454 | packet->protocol = PROT_ID_TCP; 455 | packet->id = PCKTID_AI_MOVEMENTS; 456 | 457 | // Content and fill is done where this function is called 458 | // Create and fill the content 459 | pckt_aimovementupdate_t content; 460 | 461 | // Convert content as packet.data 462 | memcpy(packet->data, &content, sizeof(content)); 463 | 464 | return packet; 465 | } 466 | 467 | pckt_t* PCKT_MakeAIAttackPacket(pckt_t* packet, int pNetworkID, float pDamage, bool pDied) 468 | { 469 | PCKT_Zero(packet); 470 | 471 | // Create the packet 472 | packet->protocol = PROT_ID_TCP; 473 | packet->id = PCKTID_AI_ATTACKED; 474 | 475 | // Create and fill the content 476 | pckt_aiattacked_t content; 477 | content.networkID = pNetworkID; 478 | content.damage = pDamage; 479 | content.died = pDied; 480 | 481 | // Convert content as packet.data 482 | memcpy(packet->data, &content, sizeof(content)); 483 | 484 | return packet; 485 | } 486 | 487 | pckt_t* PCKT_MakeAIPlayAnimPacket(pckt_t* packet, int pNetworkID, int pAnimID, bool pLoop) 488 | { 489 | PCKT_Zero(packet); 490 | 491 | // Create the packet 492 | packet->protocol = PROT_ID_TCP; 493 | packet->id = PCKTID_AI_PLAY_ANIM; 494 | 495 | // Create and fill the content 496 | pckt_aiplayanim_t content; 497 | content.networkID = pNetworkID; 498 | content.anim = pAnimID; 499 | content.loop = pLoop; 500 | 501 | // Convert content as packet.data 502 | memcpy(packet->data, &content, sizeof(content)); 503 | 504 | return packet; 505 | } 506 | 507 | pckt_t* PCKT_MakeAIInstantiatePacket(pckt_t* packet, int pNetworkID, int pLevel, int pGridX, int pGridY, int pSpriteID, bool pPlayAnim, int pAnimID, bool pLoop) 508 | { 509 | PCKT_Zero(packet); 510 | 511 | // Create the packet 512 | packet->protocol = PROT_ID_TCP; 513 | packet->id = PCKTID_AI_INSTANTIATE; 514 | 515 | // Create and fill the content 516 | pckt_aiinstantiate_t content; 517 | content.networkID = pNetworkID; 518 | content.level = pLevel; 519 | content.gridX = pGridX; 520 | content.gridY = pGridY; 521 | content.spriteID = pSpriteID; 522 | content.playAnimation = pPlayAnim; 523 | content.animID = pAnimID; 524 | content.loop = pLoop; 525 | 526 | // Convert content as packet.data 527 | memcpy(packet->data, &content, sizeof(content)); 528 | 529 | return packet; 530 | } 531 | 532 | pckt_t* PCKT_MakePuddlesInstantiatePacket(pckt_t* packet) 533 | { 534 | PCKT_Zero(packet); 535 | 536 | // Create the packet 537 | packet->protocol = PROT_ID_TCP; 538 | packet->id = PCKTID_PUDDLES_INSTANTIATE; 539 | 540 | // Create the content 541 | pckt_puddle_instantiate_t content; 542 | 543 | // Convert content as packet.data 544 | memcpy(packet->data, &content, sizeof(content)); 545 | 546 | return packet; 547 | } 548 | 549 | pckt_t* PCKT_MakeHealOtherPacket(pckt_t* packet, float pAmount) 550 | { 551 | PCKT_Zero(packet); 552 | 553 | // Create the packet 554 | packet->protocol = PROT_ID_TCP; 555 | packet->id = PCKTID_HEAL_OTHER; 556 | 557 | // Create and fill the content 558 | pckt_heal_other_t content; 559 | content.healAmount = pAmount; 560 | 561 | // Convert content as packet.data 562 | memcpy(packet->data, &content, sizeof(content)); 563 | 564 | return packet; 565 | } 566 | 567 | pckt_t* PCKT_MakePlayerDeathPacket(pckt_t* packet) 568 | { 569 | PCKT_Zero(packet); 570 | 571 | // Create the packet 572 | packet->protocol = PROT_ID_TCP; 573 | packet->id = PCKTID_PLAYER_DEATH; 574 | 575 | // No content 576 | 577 | return packet; 578 | } 579 | 580 | pckt_t* PCKT_MakeChatMessagePacket(pckt_t* packet, char* msg) 581 | { 582 | PCKT_Zero(packet); 583 | 584 | // Create the packet 585 | packet->protocol = PROT_ID_TCP; 586 | packet->id = PCKTID_CHATMESSAGE; 587 | 588 | // Create and fill the content 589 | pckt_chatmessage_t content; 590 | strcpy(content.message, msg); 591 | 592 | // Convert content as packet.data 593 | memcpy(packet->data, &content, sizeof(content)); 594 | 595 | return packet; 596 | } -------------------------------------------------------------------------------- /src/Network/packet.h: -------------------------------------------------------------------------------- 1 | #ifndef PACKET_H_INCLUDED 2 | #define PACKET_H_INCLUDED 3 | 4 | #include "netdef.h" 5 | #include "replication.h" 6 | 7 | // Protocol relate 8 | #define PROT_ID_TCP 1 9 | #define PROT_ID_UDP 2 10 | 11 | #define PCKT_SIZE sizeof(pckt_t) // sizeof(pckt_t) 12 | #define MAX_PCKT_DATA 1300 // sizeof the char buffer inside the packet data 13 | 14 | #define PCKT_RECEIVE_RETURN_ERROR 240 15 | #define PCKT_SEND_AND_CLOSE 640 16 | 17 | // Packets IDs 18 | #define PCKTID_GREET 1 19 | #define PCKTID_SET_CLASS 2 20 | #define PCKTID_READY 3 21 | #define PCKTID_STARTING 4 22 | #define PCKTID_PLAYERUPDATE 5 23 | #define PCKTID_DOOR_CHANGE 6 24 | #define PCKTID_PICKUP_PICKED 7 25 | #define PCKTID_PROJECTILE_SPAWN 8 26 | #define PCKTID_PROJECTILE_DESTR 9 27 | #define PCKTID_AI_MOVEMENTS 10 28 | #define PCKTID_AI_ATTACKED 11 29 | #define PCKTID_AI_PLAY_ANIM 12 30 | #define PCKTID_AI_INSTANTIATE 13 31 | #define PCKTID_PUDDLES_INSTANTIATE 14 32 | #define PCKTID_HEAL_OTHER 15 33 | #define PCKTID_PLAYER_DEATH 16 34 | #define PCKTID_CHATMESSAGE 17 35 | 36 | #define PCKT_BUFFER PCKT_SIZE 37 | 38 | // Abs packet 39 | typedef struct pckt_t 40 | { 41 | byte protocol; 42 | byte id; 43 | byte data[MAX_PCKT_DATA]; 44 | } pckt_t; 45 | 46 | // Packets struct 47 | typedef struct pckt_greet_t 48 | { 49 | char name[NET_MAX_PLAYER_NAME_LENGTH]; 50 | byte favoriteClass; 51 | } pckt_greet_t; 52 | 53 | typedef struct pckt_set_class_t 54 | { 55 | byte classSet; 56 | } pckt_set_class_t; 57 | 58 | typedef struct pckt_ready_t 59 | { 60 | byte isReady; 61 | } pckt_ready_t; 62 | 63 | typedef struct pckt_starting_t 64 | { 65 | byte starting; // 0 = not starting - 1 = starting - 2 = playing 66 | } pckt_starting_t; 67 | 68 | typedef struct pckt_playerupdate_t 69 | { 70 | float x,y; 71 | float angle; 72 | float z; 73 | 74 | float curHealth; 75 | float maxHealth; 76 | 77 | float curMana; 78 | float maxMana; 79 | 80 | int curWeapon; 81 | int curSpell; 82 | } pckt_playerupdate_t; 83 | 84 | typedef struct pckt_door_change_t 85 | { 86 | int level; 87 | int x,y; 88 | int state; 89 | } pckt_door_change_t; 90 | 91 | typedef struct pckt_pickup_picked_t 92 | { 93 | int level; 94 | int x,y; 95 | } pckt_pickup_picked_t; 96 | 97 | 98 | typedef struct packedprojectilespawn_t 99 | { 100 | int networkID; 101 | int spriteID; 102 | float angle; 103 | int level; 104 | float posX, posY, posZ; 105 | float verticalAngle; 106 | bool isOfPlayer; 107 | int aiOwnerID; 108 | } packedprojectilespawn_t; 109 | 110 | #define MAX_PROJECTILES_TO_SEND_SIZE 256 // max amounts of projectiles that can be saved to be sent this frame 111 | #define MAX_PROJECTILESPAWN_PER_PACKET 32 // max amounts of projectiles per packet 112 | typedef struct pckt_projectile_spawn_t 113 | { 114 | uint32_t length; 115 | packedprojectilespawn_t projectiles[MAX_PROJECTILESPAWN_PER_PACKET]; 116 | } pckt_projectile_spawn_t; 117 | 118 | typedef struct pckt_projectile_destr_t 119 | { 120 | int networkID; 121 | int spriteID; 122 | bool forceDestroy; // forces the destroy by skipping the isNetworkInstance check 123 | } pckt_projectile_destr_t; 124 | 125 | #define MAX_AIREPLICATIONT_PER_PACKET 64 // 64*sizeof(aireplicated_t)+content must be < 1300 126 | typedef struct pckt_aimovementupdate_t 127 | { 128 | uint32_t length; 129 | aireplicated_t ais[MAX_AIREPLICATIONT_PER_PACKET]; 130 | } pckt_aimovementupdate_t; 131 | 132 | typedef struct pckt_aiattacked_t 133 | { 134 | int networkID; 135 | float damage; 136 | bool died; 137 | } pckt_aiattacked_t; 138 | 139 | typedef struct pckt_aiplayanim_t 140 | { 141 | int networkID; 142 | int anim; 143 | bool loop; 144 | } pckt_aiplayanim_t; 145 | 146 | typedef struct pckt_aiinstantiate_t 147 | { 148 | int networkID; 149 | 150 | int level; 151 | int gridX, gridY; 152 | int spriteID; 153 | 154 | bool playAnimation; 155 | int animID; 156 | bool loop; 157 | } pckt_aiinstantiate_t; 158 | 159 | 160 | // Single puddle, used in pckt_puddle_istantiate to embed more than 1 per packet 161 | typedef struct packedpuddle_t 162 | { 163 | int networkID; 164 | 165 | int gridX, gridY; 166 | bool damagesAI; 167 | bool damagesPlayer; 168 | float damage; 169 | int duration; 170 | int level; 171 | int newFloorID; 172 | int previousFloorID; 173 | } packedpuddle_t; 174 | 175 | #define MAX_PUDDLE_ABS_SIZE 2048 176 | #define MAX_PUDDLE_OBJECTS_INSTANTIATE 30 //sizeof(packedpuddle_t) * 30 <= MAX_PCKT_DATA 177 | typedef struct pckt_puddle_instantiate_t 178 | { 179 | unsigned length; 180 | packedpuddle_t puddles[MAX_PUDDLE_OBJECTS_INSTANTIATE]; 181 | } pckt_puddle_instantiate_t; 182 | 183 | typedef struct pckt_heal_other_t 184 | { 185 | float healAmount; 186 | } pckt_heal_other_t; 187 | 188 | #define CHATMESSAGE_MAX_LENGTH 33 189 | typedef struct pckt_chatmessage_t 190 | { 191 | char message[CHATMESSAGE_MAX_LENGTH]; 192 | } pckt_chatmessage_t; 193 | 194 | #define MAX_PCKTS_PER_BUFFER 200 195 | typedef struct pckt_buffer_t 196 | { 197 | char buffer [PCKT_SIZE*MAX_PCKTS_PER_BUFFER]; 198 | 199 | // For short receive, lenght of what got received so far and flag 200 | int len; 201 | bool shorted; 202 | 203 | // For short send, this flag fills the buffer for writing and tells the program to not do it again on future PCKT_Send calls 204 | bool hasBegunWriting; 205 | int packetsToWrite; // how many packets are written in the buffer to send 206 | int packetOffset; 207 | } pckt_buffer_t; 208 | 209 | 210 | // Global packet data to send out 211 | extern pckt_t packetToSend; 212 | 213 | // Input buffer when receiving packets 214 | extern pckt_buffer_t inputPcktBuffer; 215 | 216 | // Output buffer when sending packets 217 | extern pckt_buffer_t outputPcktBuffer; 218 | 219 | void PCKT_Zero(pckt_t* p); 220 | void PCKT_ZeroBuffer(pckt_buffer_t* pBuf); 221 | 222 | void PCKT_ExtractFromBuffer(int offset); 223 | 224 | pckt_t* PCKT_MakeGreetPacket(pckt_t* packet, char pName[NET_MAX_PLAYER_NAME_LENGTH], byte pFavClass); 225 | pckt_t* PCKT_MakeSetClassPacket(pckt_t* packet, byte pClassToSet); 226 | pckt_t* PCKT_MakeReadyPacket(pckt_t* packet, byte pIsReady); 227 | pckt_t* PCKT_MakeStartingPacket(pckt_t* packet, byte pStarting); 228 | pckt_t* PCKT_MakePlayerUpdatePacket(pckt_t* packet, float pX, float pY, float pZ, float pAngle, float pCurHealth, float pMaxHealth, float pCurMana, float pMaxMana, int pCurWeapon, int pCurSpell); 229 | pckt_t* PCKT_MakeDoorChangePacket(pckt_t* packet, int pLevel, int pX, int pY, int pState); 230 | pckt_t* PCKT_MakePickupPickedPacket(pckt_t* packet, int pLevel, int pX, int pY); 231 | pckt_t* PCKT_MakeProjectileSpawnPacket(pckt_t* packet, unsigned pLength, packedprojectilespawn_t pProjectiles[MAX_PROJECTILES_TO_SEND_SIZE]); 232 | pckt_t* PCKT_MakeProjectileDestrPacket(pckt_t* packet, int pNetworkID, int pSpriteID, bool pForceDestroy); 233 | pckt_t* PCKT_MakeAIMovementUpdatePacket(pckt_t* packet); 234 | pckt_t* PCKT_MakeAIAttackPacket(pckt_t* packet, int pNetworkID, float pDamage, bool pDied); 235 | pckt_t* PCKT_MakeAIPlayAnimPacket(pckt_t* packet, int pNetworkID, int pAnimID, bool pLoop); 236 | pckt_t* PCKT_MakeAIInstantiatePacket(pckt_t* packet, int pNetworkID, int pLevel, int pGridX, int pGridY, int pSpriteID, bool pPlayAnim, int pAnimID, bool pLoop); 237 | pckt_t* PCKT_MakePuddlesInstantiatePacket(pckt_t* packet); 238 | pckt_t* PCKT_MakeHealOtherPacket(pckt_t* packet, float pAmount); 239 | pckt_t* PCKT_MakePlayerDeathPacket(pckt_t* packet); 240 | pckt_t* PCKT_MakeChatMessagePacket(pckt_t* packet, char* msg); 241 | 242 | int PCKT_ReceivePacket(int (*OnPacketArrives)(void)); 243 | int PCKT_SendPacket(int (*OnPacketIsSent)(void)); 244 | 245 | #endif 246 | -------------------------------------------------------------------------------- /src/Network/replication.c: -------------------------------------------------------------------------------- 1 | #include "netdef.h" 2 | #include "replication.h" 3 | 4 | uint32_t lastUsedNetworkID = 0; 5 | 6 | void REPL_InitializeNetworkIDs() 7 | { 8 | if(thisPlayer.isHost) 9 | lastUsedNetworkID = 0; 10 | else 11 | lastUsedNetworkID = 4294967295U / 2; 12 | } 13 | 14 | uint32_t REPL_GenerateNetworkID() 15 | { 16 | return lastUsedNetworkID++; 17 | } -------------------------------------------------------------------------------- /src/Network/replication.h: -------------------------------------------------------------------------------- 1 | #ifndef REPLICATION_H_INCLUDED 2 | #define REPLICATION_H_INCLUDED 3 | 4 | #include "stdio.h" 5 | #include "stdint.h" 6 | 7 | typedef struct aireplicated_t 8 | { 9 | uint32_t networkID; 10 | float x,y,z; 11 | char hostAggro, joinerAggro; 12 | bool canBeHit; 13 | } aireplicated_t; 14 | 15 | extern uint32_t lastUsedNetworkID; 16 | 17 | void REPL_InitializeNetworkIDs(); 18 | uint32_t REPL_GenerateNetworkID(); 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/Online/O_Game.h: -------------------------------------------------------------------------------- 1 | #ifndef OGAME_H_INCLUDED 2 | #define OGAME_H_INCLUDED 3 | 4 | #include 5 | #include "SDL.h" 6 | #include "O_GameDef.h" 7 | #include "../Network/netdef.h" 8 | #include "../Network/packet.h" 9 | #include "../Engine/U_DataTypes.h" 10 | #include "../Engine/T_TextRendering.h" 11 | 12 | extern dynamicSprite_t otherPlayerObject; 13 | 14 | extern packedprojectilespawn_t projectilesToSend[MAX_PROJECTILES_TO_SEND_SIZE]; 15 | extern unsigned projectilesToSendLength; 16 | 17 | int O_GameInitializeOtherPlayer(void); 18 | 19 | int O_GameOtherPlayerLoop(void); 20 | 21 | int O_GameReceivePackets(void); 22 | int O_GameSendPackets(void); 23 | 24 | int O_GameOnPacketIsSent(void); 25 | int O_GameOnPacketIsReceived(void); 26 | 27 | void O_GameOtherPlayerRender(void); 28 | 29 | void O_GameSetDoorState(int level, int dX, int dY, doorstate_e state); 30 | void O_GamePickPickup(int level, int dX, int dY); 31 | void O_GameSpawnProjectile(int pNetworkID, int pSpriteID, float pAngle, int pLevel, float pPosX, float pPosY, float pPosZ, float pVerticalAngle, bool pIsOfPlayer, int pAiOwnerID); 32 | void O_GameDestroyProjectile(int pNetworkID, int pSpriteID, bool pForceDestroy); 33 | 34 | void O_GameSendAIUpdate(void); 35 | void O_GameAITakeDamage(int pNetworkID, float pDamage, bool pDied); 36 | void O_GameAIPlayAnim(int networkID, int anim, bool loop); 37 | void O_GameAIInstantiate(int pNetworkID, int pLevel, int pGridX, int pGridY, int pSpriteID, bool pPlayAnim, int pAnimID, bool pLoop); 38 | void O_GameSpawnPuddles(int length, packedpuddle_t puddles[MAX_PUDDLE_ABS_SIZE]); 39 | void O_GameHealOther(float amount); 40 | void O_GameSendDeathPacket(void); 41 | void O_GameSendChatMessage(char* msg); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/Online/O_GameDef.h: -------------------------------------------------------------------------------- 1 | #ifndef OGAMEDEF_H_INCLUDED 2 | #define OGAMEDEF_H_INCLUDED 3 | 4 | #include "../Engine/U_DataTypes.h" 5 | 6 | // Definitions for the Online game 7 | typedef enum playableclasses_e 8 | { 9 | CLASS_TANK = 0, 10 | CLASS_HEALER, 11 | CLASS_DPS 12 | } playableclasses_e; 13 | 14 | typedef enum gamedungeons_e 15 | { 16 | THE_FROZEN_END = 0 17 | } gamedungeons_e; 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/Online/O_Lobby.c: -------------------------------------------------------------------------------- 1 | #include "O_Lobby.h" 2 | 3 | #include "../Engine/A_Application.h" 4 | #include "../Engine/G_MainMenu.h" 5 | #include "../Engine/D_AssetsManager.h" 6 | #include "../Engine/R_Rendering.h" 7 | #include "../Engine/T_TextRendering.h" 8 | #include "../Engine/G_Game.h" 9 | 10 | #include "../Network/netdef.h" 11 | #include "../Network/packet.h" 12 | 13 | bool thisPlayerReady; 14 | bool otherPlayerReady; 15 | 16 | gamedungeons_e selectedDungeon; 17 | 18 | // Lobby menu buttons 19 | static void CALLBACK_ClassSelectTank(void); 20 | static void CALLBACK_ClassSelectHealer(void); 21 | static void CALLBACK_ClassSelectDps(void); 22 | 23 | menuelement_t lobbyButtons[] = 24 | { 25 | {"CLASS_SELECT_TANK", {14, 336, 32, 32}, CALLBACK_ClassSelectTank}, 26 | {"CLASS_SELECT_HEALER", {76, 336, 32, 32}, CALLBACK_ClassSelectHealer}, 27 | {"CLASS_SELECT_DPS", {134, 336, 32, 32}, CALLBACK_ClassSelectDps} 28 | }; 29 | int lobbyButtonsLength = 3; 30 | 31 | int O_LobbyDefineClassesHostwise(void) 32 | { 33 | if(thisPlayer.favoriteClass != otherPlayer.favoriteClass) 34 | { 35 | printf("LOBBYINFO: Default classes are ok\n"); 36 | thisPlayer.selectedClass = thisPlayer.favoriteClass; 37 | otherPlayer.selectedClass = otherPlayer.favoriteClass; 38 | return 0; 39 | } 40 | else 41 | { 42 | printf("LOBBYINFO: Default classes are not ok, prioritizing host...\n"); 43 | 44 | // Both players wants the same class, prioritize the host 45 | thisPlayer.selectedClass = thisPlayer.favoriteClass; 46 | 47 | switch(thisPlayer.favoriteClass) 48 | { 49 | case CLASS_TANK: 50 | otherPlayer.selectedClass = CLASS_HEALER; 51 | break; 52 | 53 | case CLASS_HEALER: 54 | otherPlayer.selectedClass = CLASS_TANK; 55 | break; 56 | 57 | case CLASS_DPS: 58 | otherPlayer.selectedClass = CLASS_TANK; 59 | break; 60 | } 61 | 62 | return 1; 63 | } 64 | } 65 | 66 | int O_LobbyDefineClassesJoinerwise(void) 67 | { 68 | if(thisPlayer.favoriteClass != otherPlayer.favoriteClass) 69 | { 70 | printf("LOBBYINFO: Default classes are ok\n"); 71 | 72 | thisPlayer.selectedClass = thisPlayer.favoriteClass; 73 | otherPlayer.selectedClass = otherPlayer.favoriteClass; 74 | return 0; 75 | } 76 | else 77 | { 78 | printf("LOBBYINFO: Default classes are not ok, prioritizing host...\n"); 79 | 80 | // Both players wants the same class, prioritize the host 81 | otherPlayer.selectedClass = otherPlayer.favoriteClass; 82 | 83 | switch(otherPlayer.favoriteClass) 84 | { 85 | case CLASS_TANK: 86 | thisPlayer.selectedClass = CLASS_HEALER; 87 | break; 88 | 89 | case CLASS_HEALER: 90 | thisPlayer.selectedClass = CLASS_TANK; 91 | break; 92 | 93 | case CLASS_DPS: 94 | thisPlayer.selectedClass = CLASS_TANK; 95 | break; 96 | } 97 | 98 | return 1; 99 | } 100 | } 101 | 102 | int O_LobbySetReady(bool ready) 103 | { 104 | thisPlayer.isReady = ready; 105 | 106 | // Send packet to change class 107 | pckt_t* readyPacket = PCKT_MakeReadyPacket(&packetToSend, (ready) ? 1 : 0); 108 | 109 | // outputpcktbuffer was already sending something, check if we can append this packet 110 | if(outputPcktBuffer.packetsToWrite < MAX_PCKTS_PER_BUFFER) 111 | { 112 | // Append this packet, it will be sent after the ones already in buffer 113 | outputPcktBuffer.hasBegunWriting = TRUE; 114 | memcpy(outputPcktBuffer.buffer+(outputPcktBuffer.packetsToWrite*PCKT_SIZE), (char*)readyPacket, PCKT_SIZE); 115 | outputPcktBuffer.packetsToWrite++; 116 | 117 | printf("Ready Packet made!\n"); 118 | 119 | thisPlayer.isReady = ready; 120 | 121 | // Check if both are ready 122 | if(thisPlayer.isReady && otherPlayer.isReady && !thisPlayer.startingGame) 123 | { 124 | printf("BOTH READY, SHOULD START THE GAME\n"); 125 | O_LobbyStartGame(); 126 | } 127 | 128 | return 0; 129 | } 130 | else 131 | { 132 | // Outputpcktbuffer is full and this packet should be sent, what to do? 133 | printf("CRITICAL ERROR: Send buffer was full when trying to send SetClassPacket\n"); 134 | return 2; 135 | } 136 | } 137 | 138 | int O_LobbySetMap(void) 139 | { 140 | 141 | } 142 | 143 | int O_LobbySetClass(playableclasses_e selected) 144 | { 145 | if(selected == thisPlayer.selectedClass || selected == otherPlayer.selectedClass || thisPlayer.isReady) 146 | return 1; 147 | 148 | // Send packet to change class 149 | pckt_t* setClassPacket = PCKT_MakeSetClassPacket(&packetToSend, selected); 150 | 151 | // outputpcktbuffer was already sending something, check if we can append this packet 152 | if(outputPcktBuffer.packetsToWrite < MAX_PCKTS_PER_BUFFER) 153 | { 154 | // Append this packet, it will be sent after the ones already in buffer 155 | outputPcktBuffer.hasBegunWriting = TRUE; 156 | memcpy(outputPcktBuffer.buffer+(outputPcktBuffer.packetsToWrite*PCKT_SIZE), (char*)setClassPacket, PCKT_SIZE); 157 | outputPcktBuffer.packetsToWrite++; 158 | 159 | printf("Set Class Packet made!\n"); 160 | 161 | thisPlayer.selectedClass = selected; 162 | return 0; 163 | } 164 | else 165 | { 166 | // Outputpcktbuffer is full and this packet should be sent, what to do? 167 | printf("CRITICAL ERROR: Send buffer was full when trying to send SetClassPacket\n"); 168 | return 2; 169 | } 170 | } 171 | 172 | int O_LobbyLeave(void) 173 | { 174 | 175 | } 176 | 177 | 178 | int O_LobbyRender(void) 179 | { 180 | T_DisplayTextScaled(FONT_BLKCRY, "Lobby", 300, 30, 2.0f); 181 | 182 | // Display this player name 183 | T_DisplayTextScaled(FONT_BLKCRY, thisPlayer.name, 50, 150, 1.0f); 184 | 185 | SDL_Rect defaultSize = {0,0, SCREEN_WIDTH, SCREEN_HEIGHT}; 186 | 187 | // Display this player potrait 188 | SDL_Rect thisPotraitScreenPos = {20, 190, SCREEN_WIDTH, SCREEN_HEIGHT}; 189 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_POTRAIT_TANK+thisPlayer.selectedClass]->texture, &thisPotraitScreenPos); 190 | 191 | // Display this player classess 192 | SDL_Rect thisTankIcon = {14, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 193 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_TANK]->texture, &thisTankIcon); 194 | 195 | SDL_Rect thisHealerIcon = {76, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 196 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_HEALER]->texture, &thisHealerIcon); 197 | 198 | SDL_Rect thisDpsIcon = {134, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 199 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_DPS]->texture, &thisDpsIcon); 200 | 201 | SDL_Rect selectionIcon = {14, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 202 | if(thisPlayer.selectedClass == 0) 203 | selectionIcon.x = 14; 204 | else if(thisPlayer.selectedClass == 1) 205 | selectionIcon.x = 76; 206 | else 207 | selectionIcon.x = 134; 208 | 209 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_SELECTION]->texture, &selectionIcon); 210 | 211 | SDL_Rect disabledIcon = {134, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 212 | if(otherPlayer.selectedClass == 0) 213 | disabledIcon.x = 14; 214 | else if(otherPlayer.selectedClass == 1) 215 | disabledIcon.x = 76; 216 | else 217 | disabledIcon.x = 134; 218 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_SELECTION_DISABLED]->texture, &disabledIcon); 219 | 220 | // Ready text 221 | T_DisplayTextScaled(FONT_BLKCRY, "Ready: ", 14, 405, 1.0f); 222 | 223 | SDL_Rect readyIcon = {120, 405, SCREEN_WIDTH, SCREEN_HEIGHT}; 224 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[thisPlayer.isReady ? G_ASSET_ICON_READY : G_ASSET_ICON_NOTREADY]->texture, &readyIcon); 225 | 226 | // Display other player name 227 | T_DisplayTextScaled(FONT_BLKCRY, otherPlayer.name, 650, 150, 1.0f); 228 | 229 | 230 | // Display OTHER player potrait 231 | SDL_Rect otherPotraitScreenPos = {630, 190, SCREEN_WIDTH, SCREEN_HEIGHT}; 232 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_POTRAIT_TANK+otherPlayer.selectedClass]->texture, &otherPotraitScreenPos); 233 | 234 | // Display other player class 235 | 236 | // Display this player classess 237 | SDL_Rect otherClassIcon = {680, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 238 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_TANK+otherPlayer.selectedClass]->texture, &otherClassIcon); 239 | 240 | SDL_Rect otherClassSelection = {680, 336, SCREEN_WIDTH, SCREEN_HEIGHT}; 241 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[G_ASSET_CLASS_ICON_SELECTION]->texture, &otherClassSelection); 242 | 243 | // Ready text 244 | T_DisplayTextScaled(FONT_BLKCRY, "Ready: ", 610, 405, 1.0f); 245 | 246 | SDL_Rect otherReadyIcon = {716, 405, SCREEN_WIDTH, SCREEN_HEIGHT}; 247 | R_BlitIntoScreenScaled(&defaultSize, tomentdatapack.uiAssets[otherPlayer.isReady ? G_ASSET_ICON_READY : G_ASSET_ICON_NOTREADY]->texture, &otherReadyIcon); 248 | } 249 | 250 | void O_LobbyInputHandling(SDL_Event* e) 251 | { 252 | // Offset mouse pos 253 | static int x = 0; 254 | static int y = 0; 255 | 256 | //Get the mouse offsets 257 | x = e->button.x; 258 | y = e->button.y; 259 | 260 | for(int i = 0; i < lobbyButtonsLength; i++) 261 | { 262 | bool isOn = (( x > lobbyButtons[i].box.x) && ( x < lobbyButtons[i].box.x + lobbyButtons[i].box.w ) && 263 | ( y > lobbyButtons[i].box.y) && ( y < lobbyButtons[i].box.y + lobbyButtons[i].box.h )); 264 | 265 | if(e->type == SDL_MOUSEBUTTONUP && e->button.button == SDL_BUTTON_LEFT) 266 | { 267 | if (isOn && lobbyButtons[i].OnClick != NULL) 268 | { 269 | lobbyButtons[i].OnClick(); 270 | return; 271 | } 272 | } 273 | } 274 | } 275 | 276 | static void CALLBACK_ClassSelectTank(void) 277 | { 278 | O_LobbySetClass(CLASS_TANK); 279 | } 280 | 281 | static void CALLBACK_ClassSelectHealer(void) 282 | { 283 | O_LobbySetClass(CLASS_HEALER); 284 | } 285 | 286 | static void CALLBACK_ClassSelectDps(void) 287 | { 288 | O_LobbySetClass(CLASS_DPS); 289 | } 290 | 291 | int O_LobbySendPackets(void) 292 | { 293 | return PCKT_SendPacket(O_LobbyOnPacketIsSent); 294 | } 295 | 296 | int O_LobbyOnPacketIsSent(void) 297 | { 298 | printf("Packet just got sent, offset: %d ID: ", outputPcktBuffer.packetOffset); 299 | char thisPcktBuffer[PCKT_SIZE]; 300 | memcpy(thisPcktBuffer, outputPcktBuffer.buffer+(outputPcktBuffer.packetOffset), PCKT_SIZE); 301 | 302 | pckt_t* pcktSent = (pckt_t*)thisPcktBuffer; 303 | 304 | printf("%d\n", pcktSent->id); 305 | switch(pcktSent->id) 306 | { 307 | 308 | case PCKTID_READY: 309 | { 310 | pckt_ready_t readyPacket; 311 | memcpy(&readyPacket, pcktSent->data, sizeof(readyPacket)); 312 | 313 | printf("Ready packet sent value: %d\n", readyPacket.isReady); 314 | break; 315 | } 316 | 317 | 318 | case PCKTID_STARTING: 319 | { 320 | // Start the game, we warned the other user 321 | printf("Starting the game.\n"); 322 | 323 | // Load game 324 | G_InitGame(); 325 | A_ChangeState(GSTATE_GAME); 326 | break; 327 | } 328 | } 329 | 330 | return 0; 331 | } 332 | 333 | int O_LobbyReceivePackets(void) 334 | { 335 | return PCKT_ReceivePacket(O_LobbyOnPacketIsReceived); 336 | } 337 | 338 | int O_LobbyOnPacketIsReceived(void) 339 | { 340 | // When this function gets called, the packet arrived on the PCKT_ReceivePacket call and was saved inside the inputPacketBuffer->buffer 341 | // At this point, receivedPacket points at the inputPacketBuffer->buffer that contains the packet that arrived 342 | pckt_t* receivedPacket = (pckt_t*)inputPcktBuffer.buffer; 343 | 344 | printf("Packet received! ID: %d\n", receivedPacket->id); 345 | 346 | switch(receivedPacket->id) 347 | { 348 | case PCKTID_SET_CLASS: 349 | { 350 | // Manage packet, if receivedPacket->id == PCKT_GREET: 351 | pckt_set_class_t setClassPacket; 352 | memcpy(&setClassPacket, receivedPacket->data, sizeof(setClassPacket)); 353 | 354 | printf("Packet received! ID: %d | - Class: %d\n", receivedPacket->id, setClassPacket.classSet); 355 | 356 | // Parse packet, check if it is the same as ours? 357 | if(setClassPacket.classSet == thisPlayer.selectedClass) 358 | { 359 | // Class clash, may be caused by delay, if this is the host, keep the class, if this is the client change it 360 | printf("Class clash!\n"); 361 | if(thisPlayer.isHost) 362 | { 363 | switch(thisPlayer.selectedClass) 364 | { 365 | case CLASS_TANK: 366 | otherPlayer.selectedClass = CLASS_HEALER; 367 | break; 368 | 369 | case CLASS_HEALER: 370 | otherPlayer.selectedClass = CLASS_TANK; 371 | break; 372 | 373 | case CLASS_DPS: 374 | otherPlayer.selectedClass = CLASS_TANK; 375 | break; 376 | } 377 | } 378 | else 379 | { 380 | switch(setClassPacket.classSet) 381 | { 382 | case CLASS_TANK: 383 | thisPlayer.selectedClass = CLASS_HEALER; 384 | break; 385 | 386 | case CLASS_HEALER: 387 | thisPlayer.selectedClass = CLASS_TANK; 388 | break; 389 | 390 | case CLASS_DPS: 391 | thisPlayer.selectedClass = CLASS_TANK; 392 | break; 393 | } 394 | 395 | otherPlayer.selectedClass = setClassPacket.classSet; 396 | } 397 | } 398 | else 399 | otherPlayer.selectedClass = setClassPacket.classSet; 400 | 401 | return 0; 402 | } 403 | 404 | case PCKTID_READY: 405 | { 406 | // Manage packet, if receivedPacket->id == PCKT_GREET: 407 | pckt_ready_t readyPacket; 408 | memcpy(&readyPacket, receivedPacket->data, sizeof(readyPacket)); 409 | 410 | printf("Packet received! ID: %d | - Ready: %d\n", receivedPacket->id, readyPacket.isReady); 411 | 412 | otherPlayer.isReady = (readyPacket.isReady); 413 | 414 | if(thisPlayer.isReady && otherPlayer.isReady && !thisPlayer.startingGame) 415 | { 416 | printf("BOTH READY, SHOULD START THE GAME\n"); 417 | O_LobbyStartGame(); 418 | } 419 | 420 | break; 421 | } 422 | 423 | case PCKTID_STARTING: 424 | { 425 | // Manage packet, if startingPacket->id == PCKTID_STARTING: 426 | pckt_starting_t startingPacket; 427 | memcpy(&startingPacket, receivedPacket->data, sizeof(startingPacket)); 428 | 429 | printf("Packet received! ID: %d | - Starting value: %d\n", receivedPacket->id, startingPacket.starting); 430 | 431 | if(startingPacket.starting == 1) 432 | { 433 | otherPlayer.startingGame = true; 434 | 435 | // At this point the other player is starting the game, if we are not, we have to start it as well. 436 | printf("Other player is starting the game.\n"); 437 | if(!thisPlayer.startingGame) 438 | { 439 | // This may happen if one client was aware of both starting and while one packet was traveling one of either set himself unready. 440 | printf("We were not, force start.\n"); 441 | O_LobbyStartGame(); 442 | } 443 | } 444 | 445 | break; 446 | } 447 | } 448 | } 449 | 450 | int O_LobbyStartGame(void) 451 | { 452 | thisPlayer.startingGame = TRUE; 453 | thisPlayer.gameStated = FALSE; 454 | 455 | // Send packet to notice other player that we are starting the game, he will start as soon as this packet is received 456 | pckt_t* startingPacket = PCKT_MakeStartingPacket(&packetToSend, 1); 457 | 458 | // outputpcktbuffer was already sending something, check if we can append this packet 459 | if(outputPcktBuffer.packetsToWrite < MAX_PCKTS_PER_BUFFER) 460 | { 461 | // Append this packet, it will be sent after the ones already in buffer 462 | outputPcktBuffer.hasBegunWriting = TRUE; 463 | memcpy(outputPcktBuffer.buffer+(outputPcktBuffer.packetsToWrite*PCKT_SIZE), (char*)startingPacket, PCKT_SIZE); 464 | outputPcktBuffer.packetsToWrite++; 465 | 466 | printf("StartingPacket made!\n"); 467 | return 0; 468 | } 469 | else 470 | { 471 | // Outputpcktbuffer is full and this packet should be sent, what to do? 472 | printf("CRITICAL ERROR: Send buffer was full when in O_LobbyStartGame\n"); 473 | return 2; 474 | } 475 | } -------------------------------------------------------------------------------- /src/Online/O_Lobby.h: -------------------------------------------------------------------------------- 1 | #ifndef LOBBY_H_INCLUDED 2 | #define LOBBY_H_INCLUDED 3 | 4 | #include 5 | #include "SDL.h" 6 | #include "O_GameDef.h" 7 | 8 | extern bool thisPlayerReady; 9 | extern bool otherPlayerReady; 10 | 11 | extern gamedungeons_e selectedDungeon; 12 | 13 | // ------------------------------------------------------------------------------------------------------------------------------ 14 | // These two function checks wether each of the favorite players classess are ok to play together (if they are not the same) 15 | // If they're the same, the host keeps its own while the client will send a SetClass packet 16 | // ------------------------------------------------------------------------------------------------------------------------------ 17 | int O_LobbyDefineClassesHostwise(void); 18 | int O_LobbyDefineClassesJoinerwise(void); 19 | 20 | int O_LobbyLeave(void); 21 | 22 | int O_LobbySetReady(bool ready); 23 | int O_LobbySetMap(void); 24 | int O_LobbySetClass(playableclasses_e selected); 25 | 26 | int O_LobbyRender(void); 27 | 28 | int O_LobbyStartGame(void); 29 | 30 | int O_LobbySendPackets(void); 31 | int O_LobbyOnPacketIsSent(void); 32 | 33 | int O_LobbyReceivePackets(void); 34 | int O_LobbyOnPacketIsReceived(void); 35 | 36 | void O_LobbyInputHandling(SDL_Event* e); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // Standard Libraries 2 | #include 3 | 4 | // External Libraries 5 | #include "SDL.h" 6 | #undef main 7 | 8 | // Engine 9 | #include "Engine/A_Application.h" 10 | #include "Engine/I_InputHandling.h" 11 | #include "Engine/M_Map.h" 12 | #include "Engine/G_Player.h" 13 | #include "Engine/G_Game.h" 14 | #include "Engine/D_AssetsManager.h" 15 | 16 | // ------------------------------------------------ 17 | // Source Code 18 | // ------------------------------------------------ 19 | // A_ [ Application/Implementation Specific] 20 | // G_ [Game] 21 | // I_ [Input/Implementation Specific] 22 | // R_ [Rendering] 23 | // P_ [Physics] 24 | // M_ [Map] 25 | // D_ [Game data/Asset management] 26 | // U_ [Utilities] 27 | // O_ [Online] 28 | // ------------------------------------------------ 29 | 30 | int main(int argc, char* argv[]) 31 | { 32 | // Absolute initializations 33 | A_InitApplication(); 34 | D_InitAssetManager(); 35 | G_InitMainMenu(); 36 | 37 | // Loop 38 | while(!application.quit) 39 | { 40 | G_GameLoop(); 41 | } 42 | 43 | // Exit 44 | A_QuitApplication(); 45 | return 0; 46 | } -------------------------------------------------------------------------------- /src/toment.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentOnline/e10fa5fecb1dc8a1004a47194cabeb3438da0d09/src/toment.ico -------------------------------------------------------------------------------- /src/toment.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentOnline/e10fa5fecb1dc8a1004a47194cabeb3438da0d09/src/toment.o -------------------------------------------------------------------------------- /src/toment.rc: -------------------------------------------------------------------------------- 1 | id ICON "toment.ico" -------------------------------------------------------------------------------- /src/toment.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentOnline/e10fa5fecb1dc8a1004a47194cabeb3438da0d09/src/toment.res --------------------------------------------------------------------------------