├── src ├── toment.rc ├── toment.o ├── toment.ico ├── toment.res ├── Engine │ ├── G_AIBehaviour.h │ ├── U_Utilities.h │ ├── I_InputHandling.h │ ├── D_ObjectsCallbacks.h │ ├── U_Timer.h │ ├── P_Physics.h │ ├── T_TextRendering.h │ ├── I_InputHandling.c │ ├── P_Physics.c │ ├── G_AI.h │ ├── U_Utilities.c │ ├── G_MainMenu.h │ ├── G_Pathfinding.h │ ├── U_Timer.c │ ├── G_Game.h │ ├── A_Application.h │ ├── A_Application.c │ ├── M_Map.h │ ├── U_DataTypes.h │ ├── G_Player.h │ ├── D_ObjectsCallbacks.c │ ├── G_MainMenu.c │ ├── G_AI.c │ ├── R_Rendering.h │ ├── T_TextRendering.c │ ├── D_AssetsManager.h │ ├── G_Pathfinding.c │ ├── G_Game.c │ └── M_Map.c └── main.c ├── bin ├── SDL2.dll └── Data │ └── img.archt ├── .vscode ├── configurationCache.log ├── dryrun.log ├── targets.log ├── launch.json ├── settings.json ├── c_cpp_properties.json └── tasks.json ├── LICENSE ├── Makefile └── README.md /src/toment.rc: -------------------------------------------------------------------------------- 1 | id ICON "toment.ico" -------------------------------------------------------------------------------- /bin/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentRaycaster/HEAD/bin/SDL2.dll -------------------------------------------------------------------------------- /src/toment.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentRaycaster/HEAD/src/toment.o -------------------------------------------------------------------------------- /src/toment.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentRaycaster/HEAD/src/toment.ico -------------------------------------------------------------------------------- /src/toment.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentRaycaster/HEAD/src/toment.res -------------------------------------------------------------------------------- /bin/Data/img.archt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvematt/TomentRaycaster/HEAD/bin/Data/img.archt -------------------------------------------------------------------------------- /.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} -------------------------------------------------------------------------------- /.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make.exe --dry-run --always-make --keep-going --print-directory 2 | 'make.exe' is not recognized as an internal or external command, 3 | operable program or batch file. 4 | 5 | -------------------------------------------------------------------------------- /.vscode/targets.log: -------------------------------------------------------------------------------- 1 | make.exe all --print-data-base --no-builtin-variables --no-builtin-rules --question 2 | 'make.exe' is not recognized as an internal or external command, 3 | operable program or batch file. 4 | 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [] 7 | } -------------------------------------------------------------------------------- /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 | // AI Specific, allows to modify the AI Behaviour for each enemy 8 | void G_AI_BehaviourMeeleEnemy(dynamicSprite_t* cur); 9 | void G_AI_BehaviourCasterEnemy(dynamicSprite_t* cur); 10 | void G_AI_BehaviourSkeletonLord(dynamicSprite_t* cur); 11 | 12 | #endif -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "sdl.h": "c", 4 | "cmath": "c", 5 | "t_textrendering.h": "c", 6 | "r_rendering.h": "c", 7 | "m_map.h": "c", 8 | "p_physics.h": "c", 9 | "g_ai.h": "c", 10 | "u_utilities.h": "c", 11 | "stdlib.h": "c", 12 | "stdio.h": "c", 13 | "u_datatypes.h": "c", 14 | "g_player.h": "c", 15 | "a_application.h": "c", 16 | "float.h": "c", 17 | "d_assetsmanager.h": "c", 18 | "g_game.h": "c", 19 | "sdl_thread.h": "c" 20 | } 21 | } -------------------------------------------------------------------------------- /.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.22621.0", 15 | "compilerPath": "C:/MinGW/bin/gcc.exe", 16 | "cStandard": "c17", 17 | "cppStandard": "c++17", 18 | "intelliSenseMode": "windows-gcc-x86" 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "type": "cppbuild", 5 | "label": "C/C++: gcc.exe build active file", 6 | "command": "C:\\MinGW\\bin\\gcc.exe", 7 | "args": [ 8 | "-fdiagnostics-color=always", 9 | "-g", 10 | "${file}", 11 | "-o", 12 | "${fileDirname}\\${fileBasenameNoExtension}.exe" 13 | ], 14 | "options": { 15 | "cwd": "${fileDirname}" 16 | }, 17 | "problemMatcher": [ 18 | "$gcc" 19 | ], 20 | "group": { 21 | "kind": "build", 22 | "isDefault": true 23 | }, 24 | "detail": "Task generated by Debugger." 25 | } 26 | ], 27 | "version": "2.0.0" 28 | } -------------------------------------------------------------------------------- /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/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/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 | #endif 30 | -------------------------------------------------------------------------------- /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/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/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 | // ------------------------------------------------ 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | // Absolute initializations 32 | A_InitApplication(); 33 | D_InitAssetManager(); 34 | G_InitMainMenu(); 35 | 36 | // Loop 37 | while(!application.quit) 38 | { 39 | G_GameLoop(); 40 | } 41 | 42 | // Exit 43 | A_QuitApplication(); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/Engine/T_TextRendering.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_RENDERING_H_INCLUDED 2 | #define TEXT_RENDERING_H_INCLUDED 3 | 4 | #include "SDL.h" 5 | 6 | // ---------------------------------------------------------------------- 7 | // Displays the given text 8 | // ---------------------------------------------------------------------- 9 | void T_DisplayText(int fontID, char* text, int x, int y); 10 | 11 | // ---------------------------------------------------------------------- 12 | // Displays the given text scaled by the scale factor 13 | // ---------------------------------------------------------------------- 14 | void T_DisplayTextScaled(int fontID, char* text, int x, int y, float scaleFactor); 15 | 16 | // ---------------------------------------------------------------------- 17 | // Given a char, returns the sprite sheet coords, most naive approach ever 18 | // 19 | // This translation also kills the universality of the Text Renderer by forcing the font sheet to be 16x6 20 | // You could make different translations for each map or find a better way to translate 21 | // ---------------------------------------------------------------------- 22 | void T_TranslateASCIIToSpriteSheetCoords(char c, int* destX, int* destY); 23 | 24 | #endif -------------------------------------------------------------------------------- /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/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 18 | 19 | #OBJ_NAME specifies the name of our exectuable 20 | OBJ_NAME = bin/Toment 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) -------------------------------------------------------------------------------- /src/Engine/I_InputHandling.c: -------------------------------------------------------------------------------- 1 | #include "I_InputHandling.h" 2 | 3 | playerinput_t playerinput; 4 | 5 | // ------------------------------- 6 | // Handles SDL Events and input 7 | // ------------------------------- 8 | void I_HandleInputGame(void) 9 | { 10 | const Uint8* keyboard_state = SDL_GetKeyboardState(NULL); 11 | const Uint32 mouse_state = SDL_GetMouseState(NULL, NULL); 12 | 13 | SDL_Event e; 14 | 15 | while(SDL_PollEvent(&e)) 16 | { 17 | switch (e.type) 18 | { 19 | case SDL_QUIT: 20 | application.quit = true; 21 | break; 22 | 23 | default: 24 | break; 25 | } 26 | 27 | G_InGameInputHandlingEvent(&e); 28 | } 29 | 30 | // Send Input event to subsystems 31 | if(application.gamestate == GSTATE_GAME) 32 | { 33 | G_InGameInputHandling(keyboard_state); 34 | } 35 | } 36 | 37 | void I_HandleInputMenu(void) 38 | { 39 | const Uint8* keyboard_state = SDL_GetKeyboardState(NULL); 40 | const Uint32 mouse_state = SDL_GetMouseState(NULL, NULL); 41 | 42 | SDL_Event e; 43 | 44 | while(SDL_PollEvent(&e)) 45 | { 46 | switch (e.type) 47 | { 48 | case SDL_QUIT: 49 | application.quit = true; 50 | break; 51 | 52 | default: 53 | break; 54 | } 55 | 56 | G_InMenuInputHandling(&e); 57 | } 58 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TomentRaycaster 2 | A Software Rendering Raycaster Engine written in C and SDL2. 3 | 4 | image 5 | 6 | Video: https://www.youtube.com/watch?v=XFCinBirBVw 7 | 8 | Check out [TomentOnline](https://github.com/silvematt/TomentOnline)! An upgraded version of this engine for multiplayer! 9 | 10 | I've written this raycaster following Permadi's paper and Wolfenstein 3D code (and Black Book), as a pilgrimage to my God John Carmack. 11 | 12 | Features: 13 | - Multithreaded rendering, using SDL Threads 14 | - 640x480 with scalable raycasting resolution 15 | - Player Input (Walk forward/backwards, fly up/down, rotate, look up/down, strafe, interact) 16 | - Textured Walls, Floors, Ceilings 17 | - Thin (offsetted) Walls 18 | - Sliding Doors 19 | - Linear Fog Rendering 20 | - Transparent (see through) doors and thin walls 21 | - Multi Floor support (for player, doors, walls, sprites etc.) 22 | - Vertical Motion 23 | - Usable ladders to go up/down levels. 24 | - Sprites Rendering & Collisions 25 | - AI and basic BFS Pathfinding 26 | - Boss AI and possibility to add custom behaviours for each AI (melee, casters, bosses) 27 | - Player and AI combat 28 | - Projectiles & Player's spells 29 | - Pickups of weapons and consumables (potions) for the player 30 | - Support for Animated Sprites 31 | - Seamless Sky Rendering or Absolute Ceiling Rendering (or a mix of the two) 32 | - Shading and Map Lighting 33 | - Text Rendering of Bitmap Fonts 34 | - Weapon and Spell switching 35 | - Player HUD (Healthbar, Manabar, Equipped Weapon, Equipped Spell) 36 | - Game Menus 37 | - Load map from file 38 | - Assets Manager 39 | - Packed data in custom files (.archt) format [GENERATED WITH: [TomentARCH](https://github.com/silvematt/TomentARCH)] 40 | - Minimap 41 | - Fixed Time Step 42 | -------------------------------------------------------------------------------- /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 500.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); 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 | #endif 58 | -------------------------------------------------------------------------------- /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/G_MainMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_MENU_H_INCLUDED 2 | #define MAIN_MENU_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "SDL.h" 7 | 8 | // How off the text the cursor should be 9 | #define CURSOR_X_OFFSET 35 10 | 11 | typedef enum 12 | { 13 | MENU_START = 0, 14 | MENU_DEATH, 15 | MENU_OPTIONS, 16 | MENU_END_GAME, 17 | MENU_ABOUT 18 | } menusIDs_e; 19 | 20 | 21 | typedef struct menuelement_s 22 | { 23 | char* text; 24 | SDL_Rect box; 25 | void (*OnClick)(void); 26 | } menuelement_t; 27 | 28 | typedef struct menu_s 29 | { 30 | int ID; 31 | menuelement_t* elements; 32 | int elementsLength; 33 | menuelement_t* selectedElement; 34 | } menu_t; 35 | 36 | 37 | extern menu_t* currentMenu; 38 | 39 | // Menus are defined at compile-time 40 | extern menuelement_t MainMenuElements[]; 41 | extern menu_t MainMenu; 42 | 43 | extern menuelement_t DeathMenuElements[]; 44 | extern menu_t DeathMenu; 45 | 46 | extern menuelement_t OptionsMenuElements[]; 47 | extern menu_t OptionsMenu; 48 | 49 | extern menuelement_t EndGameMenuElements[]; 50 | extern menu_t EndGameMenu; 51 | 52 | extern menuelement_t AboutMenuElements[]; 53 | extern menu_t AboutMenu; 54 | 55 | //------------------------------------- 56 | // Initialize the Main Menu state 57 | //------------------------------------- 58 | void G_InitMainMenu(); 59 | 60 | //------------------------------------- 61 | // Set the MenuState 62 | //------------------------------------- 63 | void G_SetMenu(menu_t* menu); 64 | 65 | //------------------------------------- 66 | // Renders what's gonna be behind the current menu 67 | //------------------------------------- 68 | void G_RenderCurrentMenuBackground(void); 69 | 70 | //------------------------------------- 71 | // Render menu routine, renders the elements of any menu 72 | //------------------------------------- 73 | void G_RenderCurrentMenu(void); 74 | 75 | //------------------------------------- 76 | // Manages Menu Input routine 77 | //------------------------------------- 78 | void G_InMenuInputHandling(SDL_Event* e); 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /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/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/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 | 8 | // DOOR DEFINE 9 | #define DOOR_FULLY_CLOSED 64.0f 10 | #define DOOR_FULLY_OPENED 2.0f 11 | #define DOOR_OPEN_SPEED 100.0f 12 | #define DOOR_CLOSE_SPEED 100.0f 13 | 14 | #define N_FRAMES_SKIP_FOR_DISPLAY 20 15 | 16 | // Game Timer 17 | extern Timer* gameTimer; 18 | 19 | // Current Game Time 20 | extern double curTime; 21 | 22 | // Game Time at last tick 23 | extern double oldTime; 24 | 25 | // Doors 26 | extern int doorstateLevel0[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 27 | extern int doorstateLevel1[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 28 | extern int doorstateLevel2[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 29 | 30 | extern float doorpositionsLevel0[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 31 | extern float doorpositionsLevel1[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 32 | extern float doorpositionsLevel2[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 33 | 34 | extern projectileNode_t* projectilesHead; 35 | extern projectileNode_t* explodingProjectilesHead; 36 | 37 | extern bool showFPS; 38 | extern float readOnlyFPS; // to write FPS on screen 39 | extern char fpsText[16]; 40 | extern int frameCountForFPSDisplay; // to update readOnlyFPS not everyframe, but every x frames 41 | 42 | 43 | //------------------------------------- 44 | // Initialize game related stuff 45 | //------------------------------------- 46 | void G_InitGame(void); 47 | 48 | //------------------------------------- 49 | // Loop of the application 50 | //------------------------------------- 51 | void G_GameLoop(void); 52 | 53 | //------------------------------------- 54 | // Loop of the game while in menu (GSTATE_MENU) 55 | //------------------------------------- 56 | void G_StateMenuLoop(void); 57 | 58 | //------------------------------------- 59 | // Loop of the game while in game (GSTATE_GAME) 60 | //------------------------------------- 61 | void G_StateGameLoop(void); 62 | 63 | //------------------------------------- 64 | // Update Doors by moving them in base of their timer 65 | //------------------------------------- 66 | void G_UpdateDoors(void); 67 | 68 | //------------------------------------- 69 | // Loads the map with ID mapID 70 | //------------------------------------- 71 | void G_ChangeMap(char* mapID); 72 | 73 | //------------------------------------- 74 | // Updates the spawned projectiles 75 | //------------------------------------- 76 | void G_UpdateProjectiles(void); 77 | 78 | //------------------------------------- 79 | // Spawns a new projectile 80 | //------------------------------------- 81 | void G_SpawnProjectile(int id, float angle, int level, float posx, float posy, float posz, float verticalAngle, bool isOfPlayer, dynamicSprite_t* aiOwner); 82 | 83 | #endif -------------------------------------------------------------------------------- /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 640 14 | #define SCREEN_HEIGHT 480 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 94 | -------------------------------------------------------------------------------- /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/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 24 14 | #define MAP_HEIGHT 24 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/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 baseDamage; 92 | int attackChance; 93 | int criticalChance; 94 | float criticalModifier; 95 | } entityattributes_t; 96 | 97 | // Defines the states of dynamic sprites (AI) 98 | typedef enum dynamicSpriteState_e 99 | { 100 | DS_STATE_NULL = 0, 101 | DS_STATE_IDLE, 102 | DS_STATE_MOVING, 103 | DS_STATE_ATTACKING, 104 | DS_STATE_DEAD, 105 | DS_STATE_CASTING, 106 | DS_STATE_SPECIAL1, 107 | DS_STATE_SPECIAL2 108 | } dynamicSpriteState_e; 109 | 110 | // Types of dynamic sprites 111 | typedef enum dynamicSpriteType_e 112 | { 113 | DS_TYPE_AI = 0, 114 | DS_TYPE_PROJECTILE 115 | } dynamicSpriteType_e; 116 | 117 | // Max number of spell an AI can have (Used to create Timers with which create spell logic) 118 | #define AI_MAX_SPELLS 5 119 | 120 | // ------------------------------- 121 | // Dynamic Sprite data structure, represents dynamic sprites such as AI or projectiles 122 | // ------------------------------- 123 | typedef struct dynamicSprite_s 124 | { 125 | sprite_t base; 126 | 127 | // Dnyamic-Specific 128 | dynamicSpriteType_e type; 129 | dynamicSpriteState_e state; 130 | bool isAlive; 131 | float speed; 132 | 133 | // Boss related stuff 134 | bool isBoss; // If true, when the AI will attack the player the boss UI will appear 135 | bool bossPreventOpeningDoorsWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with doors 136 | bool bossPreventClimbingLaddersWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with ladders 137 | bool bossPreventActivatingTriggersWhileFighting; // If this is true, when this boss will be fought the player will not be able to interact with triggers 138 | 139 | bool canBeHit; 140 | 141 | void (*BehaviourUpdate)(struct dynamicSprite_s* this); 142 | Timer* cooldowns[AI_MAX_SPELLS]; // spells/abilities cooldowns, used by bosses and casters 143 | 144 | bool aggroedPlayer; // True if this AI already attacked/chased the player 145 | int spellInUse; 146 | 147 | SDL_Surface* curAnim; 148 | int curAnimLength; 149 | 150 | Timer* animTimer; 151 | bool animPlay; 152 | int animFrame; 153 | bool animPlayOnce; 154 | float animSpeed; 155 | 156 | entityattributes_t attributes; 157 | 158 | vector2_t* targetPos; 159 | vector2Int_t* targetGridPos; 160 | circle_t* targetColl; 161 | 162 | path_t* path; 163 | 164 | // Used for projectiles to distinguish between player's projectiles and AI's projectiles 165 | struct dynamicSprite_s* aiOwner; 166 | bool isOfPlayer; 167 | bool isBeingDestroyed; // called when a projectile hits and awaits the explosion animation to be removed from the list 168 | 169 | // How muc this dynamic needs to move each tick on the z axis 170 | float verticalMovementDelta; 171 | } dynamicSprite_t; 172 | 173 | // ------------------------------- 174 | // Door states 175 | // ------------------------------- 176 | typedef enum doorstate_e 177 | { 178 | DState_Closed = 0, 179 | DState_Opening, 180 | DState_Open, 181 | DState_Closing 182 | } doorstate_e; 183 | 184 | 185 | // ------------------------------- 186 | // Holds all the info got during raycast to draw walls 187 | // ------------------------------- 188 | typedef struct walldata_s 189 | { 190 | float rayAngle; 191 | int x; 192 | float curX, curY; 193 | bool isVertical; 194 | vector2Int_t gridPos; 195 | float distance; 196 | struct wallObject_s* objectHit; 197 | int level; 198 | 199 | // Extra Data 200 | // 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 201 | int extraData; 202 | } walldata_t; 203 | 204 | // ------------------------------- 205 | // Drawable Types 206 | // ------------------------------- 207 | typedef enum drawabletype_e 208 | { 209 | DRWB_WALL = 0, 210 | DRWB_SPRITE, 211 | DRWB_DYNAMIC_SPRITE 212 | } drawabletype_e; 213 | 214 | // ------------------------------- 215 | // Drawables are object in the world that will need to be drawn 216 | // A drawable array holds everything to render and allows Zdepth 217 | // ------------------------------- 218 | typedef struct drawabledata_s 219 | { 220 | drawabletype_e type; 221 | 222 | // Pointers 223 | walldata_t* wallPtr; 224 | sprite_t* spritePtr; 225 | dynamicSprite_t* dynamicSpritePtr; 226 | 227 | // Quick access varaibles 228 | float dist; 229 | } drawabledata_t; 230 | 231 | typedef struct projectileNode_s 232 | { 233 | dynamicSprite_t this; 234 | 235 | struct projectileNode_s* next; 236 | struct projectileNode_s* previous; 237 | } projectileNode_t; 238 | 239 | typedef struct alertMessage_s 240 | { 241 | int x,y; 242 | char* message; 243 | float size, duration; 244 | Timer* timer; 245 | 246 | struct alertMessage_s* next; 247 | } alertMessage_t; 248 | 249 | 250 | typedef enum orientation_e 251 | { 252 | NORTH = 0, 253 | NORTH_WEST, 254 | WEST, 255 | SOUTH_WEST, 256 | SOUTH, 257 | SOUTH_EAST, 258 | EAST, 259 | NORTH_EAST, 260 | NO_ORIENTATION, 261 | NORTH_SOUTH, // Specials 262 | WEST_EAST, 263 | ALL_FOUR_DIR, 264 | } orientation_e; 265 | 266 | #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 | 9 | // Minimap Player 10 | #define PLAYER_WIDTH 16 11 | #define PLAYER_HEIGHT 16 12 | 13 | // To fix the player position to be at the center instead of the upper corner, this is WIDTH/2 14 | #define PLAYER_CENTER_FIX 8 15 | 16 | // Speed of the player 17 | #define PLAYER_SPEED 250.0f 18 | 19 | // Rotation speed of the player 20 | #define PLAYER_ROT_SPEED 0.5f 21 | 22 | // Speed of the vertical head motion 23 | #define PLAYER_VERTICAL_HEAD_MOVEMENT_SPEED 60.0f 24 | 25 | // Default Player's starting rotation 26 | #define PLAYER_STARTING_ROT M_PI 27 | 28 | // The minimum distance the player can get to the wall 29 | #define PLAYER_MIN_DIST_TO_WALL 30 30 | 31 | // The light that the player will always carry, no matter the current map 32 | #define PLAYER_POINT_LIGHT_INTENSITY 10 33 | 34 | // The attack cone (in grids) of the player 35 | #define ATTACK_CONE_SIZE 7 36 | 37 | // The Max thresold of difference of angle (used to calculate if the AI we're trying to attack is in front of us) 38 | #define ATTACK_CONE_MAX_DIFF 25 39 | 40 | // Amount of seconds the crosshair will turn red when the player hits an AI 41 | #define CROSSHAIR_HIT_TIME_SECONDS 0.35f 42 | 43 | // Climbing speed 44 | #define PLAYER_CLIMBING_LADDER_UP_SPEED 24 45 | #define PLAYER_CLIMBING_LADDER_DOWN_SPEED 48 46 | 47 | // State of the player 48 | typedef enum playerState_e 49 | { 50 | PSTATE_IDLE = 0, 51 | PSTATE_ATTACKING1, 52 | PSTATE_CASTSPELL, 53 | PSTATE_CLIMBING_LADDER 54 | } playerState_e; 55 | 56 | // Attacks the player can perform 57 | typedef enum playerAttacks_e 58 | { 59 | P_ATTACK_MELEE_1 = 0, 60 | P_ATTACK_CAST_FIREBALL1 61 | } playerAttacks_e; 62 | 63 | // Spells (shared with AI) 64 | typedef enum playerSpells_e 65 | { 66 | SPELL_NULL = 0, 67 | SPELL_FIREBALL1 = 4, // Needs to be the same as the corresponding sprite that will be instantiated as the projectile 68 | SPELL_ICEDART1 = 8 69 | } playerSpells_e; 70 | 71 | typedef struct player_s 72 | { 73 | vector2_t position; // abs SDL position 74 | vector2_t centeredPos; // position centered for the player's width and height 75 | float z; // z pos 76 | int level; // The current floor level the player is at 77 | 78 | circle_t collisionCircle; 79 | 80 | vector2Int_t gridPosition; // position of the player in the tilemap 81 | vector2Int_t inFrontGridPosition; // The grid pos of the cell in front of the player 82 | 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 83 | 84 | vector2_t deltaPos; // used to calculate movements and collision 85 | 86 | // Minimap rapresentaton of the player 87 | SDL_Surface* surface; 88 | SDL_Rect surfaceRect; 89 | 90 | // Rot of the player in radians 91 | float angle; 92 | float verticalHeadMovement; 93 | 94 | // Loaded at runtime 95 | bool hasBeenInitialized; 96 | 97 | // The grid the player starts in (loaded from map) 98 | int startingGridX; 99 | int startingGridY; 100 | float startingRot; 101 | 102 | playerState_e state; 103 | playerFPID_e curWeapon; 104 | playerSpells_e curSpell; 105 | 106 | entityattributes_t attributes; 107 | 108 | // Animation 109 | Timer* animTimer; 110 | bool animPlay; 111 | int animFrame; 112 | bool animPlayOnce; 113 | int playedFrames; 114 | 115 | // Has to attack/cast a spell, used to not attack more than once while playing the animation 116 | bool hasToCast; 117 | bool hasCasted; 118 | 119 | // Timer used by the crosshair to change its color when the player hits an enemy 120 | bool crosshairHit; 121 | Timer* crosshairTimer; 122 | 123 | // Weapons 124 | float weaponDistance; 125 | bool hasAxe; 126 | bool hasGreatsword; 127 | 128 | // Spells 129 | bool hasFireball; 130 | bool hasIceDart; 131 | 132 | // Ladder 133 | bool hasToClimb; 134 | bool climbingUp; 135 | 136 | float climbingPosX; 137 | float climbingPosY; 138 | float climbingPosZ; 139 | 140 | bool isFightingBoss; 141 | dynamicSprite_t* bossFighting; 142 | } player_t; 143 | 144 | 145 | extern player_t player; 146 | 147 | //------------------------------------- 148 | // Initializes Player 149 | //------------------------------------- 150 | void G_InitPlayer(void); 151 | 152 | //------------------------------------- 153 | // Player's Tick 154 | //------------------------------------- 155 | void G_PlayerTick(void); 156 | 157 | //------------------------------------- 158 | // Handles Input from the player while reading the keyboard state 159 | //------------------------------------- 160 | void G_InGameInputHandling(const uint8_t* keyboardState); 161 | 162 | //------------------------------------- 163 | // Handles Input from the player while doing the Event Input Handling 164 | //------------------------------------- 165 | void G_InGameInputHandlingEvent(SDL_Event* e); 166 | 167 | //------------------------------------- 168 | // Checks the collision map at player's level and returns what found 169 | //------------------------------------- 170 | int G_CheckCollisionMap(int level, int y, int x); 171 | 172 | //------------------------------------- 173 | // Checks door state map at player's level and returns what found 174 | //------------------------------------- 175 | int G_GetDoorState(int level, int y, int x); 176 | 177 | //------------------------------------- 178 | // Sets door state map at player's level 179 | //------------------------------------- 180 | int G_SetDoorState(int level, int y, int x, doorstate_e state); 181 | 182 | //------------------------------------- 183 | // Checks door position map at player's level and returns what found 184 | //------------------------------------- 185 | float G_GetDoorPosition(int level, int y, int x); 186 | 187 | //------------------------------------- 188 | // Checks object T map at player's level and returns what found 189 | //------------------------------------- 190 | int G_GetFromObjectTMap(int level, int y, int x); 191 | 192 | //------------------------------------- 193 | // Sets a value of the object T map 194 | //------------------------------------- 195 | void G_SetObjectTMap(int level, int y, int x, int value); 196 | 197 | //------------------------------------- 198 | // Checks players collisions 199 | //------------------------------------- 200 | void G_PlayerCollisionCheck(); 201 | 202 | //------------------------------------- 203 | // Renders the player 204 | //------------------------------------- 205 | void G_PlayerRender(void); 206 | 207 | //------------------------------------- 208 | // Renders the player's UI 209 | //------------------------------------- 210 | void G_PlayerUIRender(void); 211 | 212 | //------------------------------------- 213 | // Plays an animation once (FP hands) 214 | //------------------------------------- 215 | void G_PlayerPlayAnimationOnce(objectanimationsID_e animID); 216 | 217 | //------------------------------------- 218 | // Returns if the player can attack 219 | //------------------------------------- 220 | bool G_PlayerCanAttack(void); 221 | 222 | //------------------------------------- 223 | // Makes the player take damage 224 | //------------------------------------- 225 | void G_PlayerTakeDamage(float dmg); 226 | 227 | //------------------------------------- 228 | // Drains player's mana 229 | //------------------------------------- 230 | void G_PlayerDrainMana(float amount); 231 | 232 | //------------------------------------- 233 | // Adds player's health 234 | //------------------------------------- 235 | void G_PlayerGainHealth(float amount); 236 | 237 | //------------------------------------- 238 | // Adds player's mana 239 | //------------------------------------- 240 | void G_PlayerGainMana(float amount); 241 | 242 | //------------------------------------- 243 | // Sets Player's Weapon 244 | //------------------------------------- 245 | void G_PlayerSetWeapon(playerFPID_e weaponID); 246 | 247 | //------------------------------------- 248 | // Sets Player's Spell 249 | //------------------------------------- 250 | void G_PlayerSetSpell(playerSpells_e spellID); 251 | 252 | //------------------------------------- 253 | // Makes player die 254 | //------------------------------------- 255 | void G_PlayerDeath(); 256 | 257 | #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 | } -------------------------------------------------------------------------------- /src/Engine/G_MainMenu.c: -------------------------------------------------------------------------------- 1 | #include "G_MainMenu.h" 2 | #include "G_Game.h" 3 | #include "R_Rendering.h" 4 | #include "T_TextRendering.h" 5 | #include "D_AssetsManager.h" 6 | 7 | //------------------------------------- 8 | // BUTTONS CALLBACKS 9 | //------------------------------------- 10 | static void CALLBACK_MAINMENU_NewGame(void); 11 | static void CALLBACK_MAINMENU_Options(void); 12 | static void CALLBACK_MAINMENU_Quit(void); 13 | static void CALLBACK_ReturnToMainMenu(void); 14 | static void CALLBACK_OPTIONSMENU_ChangeGraphics(void); 15 | static void CALLBACK_OPTIONSMENU_ChangeShowFPS(void); 16 | static void CALLBACK_MAINMENU_About(void); 17 | static void CALLBACK_Continue(void); 18 | 19 | // ---------------------------- 20 | // Define Menus 21 | // ---------------------------- 22 | menuelement_t MainMenuElements[] = 23 | { 24 | {"Continue", {220, 150, 400, 40}, CALLBACK_Continue}, 25 | {"New Game", {220, 200, 400, 40}, CALLBACK_MAINMENU_NewGame}, 26 | {"Load Game", {220, 250, 400, 40}, NULL}, 27 | {"Options", {220, 300, 400, 40}, CALLBACK_MAINMENU_Options}, 28 | {"About", {220, 350, 400, 40}, CALLBACK_MAINMENU_About}, 29 | {"Quit", {220, 400, 400, 40}, CALLBACK_MAINMENU_Quit} 30 | }; 31 | menu_t MainMenu = {MENU_START, MainMenuElements, 6, &MainMenuElements[1]}; 32 | 33 | menuelement_t DeathMenuElements[] = 34 | { 35 | {"Restart Game", {220, 200, 400, 40}, CALLBACK_MAINMENU_NewGame}, 36 | {"Return", {220, 250, 200, 40}, CALLBACK_ReturnToMainMenu}, 37 | }; 38 | menu_t DeathMenu = {MENU_DEATH, DeathMenuElements, 2, &DeathMenuElements[0]}; 39 | 40 | menuelement_t OptionsMenuElements[] = 41 | { 42 | {"Graphics:", {220, 200, 400, 40}, CALLBACK_OPTIONSMENU_ChangeGraphics}, 43 | {"Fps:", {220, 250, 400, 40}, CALLBACK_OPTIONSMENU_ChangeShowFPS}, 44 | {"Return", {220, 300, 200, 40}, CALLBACK_ReturnToMainMenu}, 45 | }; 46 | menu_t OptionsMenu = {MENU_OPTIONS, OptionsMenuElements, 3, &OptionsMenuElements[0]}; 47 | 48 | menuelement_t EndGameMenuElements[] = 49 | { 50 | {"Return", {220, 350, 400, 40}, CALLBACK_ReturnToMainMenu}, 51 | }; 52 | menu_t EndGameMenu = {MENU_END_GAME, EndGameMenuElements, 1, &EndGameMenuElements[0]}; 53 | 54 | menuelement_t AboutMenuElements[] = 55 | { 56 | {"Return", {220, 350, 400, 40}, CALLBACK_ReturnToMainMenu}, 57 | }; 58 | menu_t AboutMenu = {MENU_ABOUT, AboutMenuElements, 1, &AboutMenuElements[0]}; 59 | 60 | menu_t* currentMenu; 61 | 62 | 63 | void G_InitMainMenu() 64 | { 65 | G_SetMenu(&MainMenu); 66 | } 67 | 68 | //------------------------------------- 69 | // Renders what's gonna be behind the current menu 70 | //------------------------------------- 71 | void G_RenderCurrentMenuBackground(void) 72 | { 73 | switch(currentMenu->ID) 74 | { 75 | case MENU_START: 76 | { 77 | SDL_Rect titleRect = {110, 10, tomentdatapack.uiAssets[M_ASSET_TITLE]->texture->w, tomentdatapack.uiAssets[M_ASSET_TITLE]->texture->h}; 78 | R_BlitIntoScreenScaled(NULL, tomentdatapack.uiAssets[M_ASSET_TITLE]->texture, &titleRect); 79 | break; 80 | } 81 | 82 | case MENU_OPTIONS: 83 | { 84 | T_DisplayTextScaled(FONT_BLKCRY, "Settings", 180, 80, 2.0f); 85 | 86 | // Display graphics current setting: 87 | switch(r_CurrentGraphicsSetting) 88 | { 89 | case GRAPHICS_LOW: 90 | T_DisplayTextScaled(FONT_BLKCRY, "Low", 350, 205, 1.0f); 91 | break; 92 | 93 | case GRAPHICS_MEDIUM: 94 | T_DisplayTextScaled(FONT_BLKCRY, "Medium", 350, 205, 1.0f); 95 | break; 96 | 97 | case GRAPHICS_HIGH: 98 | T_DisplayTextScaled(FONT_BLKCRY, "High", 350, 205, 1.0f); 99 | break; 100 | } 101 | 102 | // Display graphics current setting: 103 | switch(showFPS) 104 | { 105 | case true: 106 | T_DisplayTextScaled(FONT_BLKCRY, "true", 300, 255, 1.0f); 107 | break; 108 | 109 | case false: 110 | T_DisplayTextScaled(FONT_BLKCRY, "false", 300, 255, 1.0f); 111 | break; 112 | } 113 | break; 114 | } 115 | 116 | case MENU_DEATH: 117 | { 118 | T_DisplayTextScaled(FONT_BLKCRY, "You died!", 180, 80, 2.0f); 119 | break; 120 | } 121 | 122 | case MENU_END_GAME: 123 | { 124 | T_DisplayTextScaled(FONT_BLKCRY, "You won!", 180, 80, 2.0f); 125 | T_DisplayTextScaled(FONT_BLKCRY, "You slayed the Lord of all Skeletons\nand you earnt your freedom.", 140, 200, 1.0f); 126 | break; 127 | } 128 | 129 | case MENU_ABOUT: 130 | { 131 | T_DisplayTextScaled(FONT_BLKCRY, "About", 210, 80, 2.0f); 132 | T_DisplayTextScaled(FONT_BLKCRY, "Programmer: Mattia Silvestro ( silvematt)\nVersion: 1.2", 80, 200, 1.0f); 133 | 134 | break; 135 | } 136 | } 137 | } 138 | 139 | //------------------------------------- 140 | // Render menu routine 141 | //------------------------------------- 142 | void G_RenderCurrentMenu(void) 143 | { 144 | // Display text 145 | for(int i = 0; i < currentMenu->elementsLength; i++) 146 | { 147 | T_DisplayTextScaled(FONT_BLKCRY, currentMenu->elements[i].text, currentMenu->elements[i].box.x, currentMenu->elements[i].box.y, 1.25f); 148 | } 149 | 150 | // Display cursor 151 | SDL_Rect cursorRect = {currentMenu->selectedElement->box.x - CURSOR_X_OFFSET, currentMenu->selectedElement->box.y, tomentdatapack.uiAssets[M_ASSET_SELECT_CURSOR]->texture->w, tomentdatapack.uiAssets[M_ASSET_SELECT_CURSOR]->texture->h}; 152 | R_BlitIntoScreen(NULL, tomentdatapack.uiAssets[M_ASSET_SELECT_CURSOR]->texture, &cursorRect); 153 | } 154 | 155 | void G_InMenuInputHandling(SDL_Event* e) 156 | { 157 | // Offset mouse pos 158 | static int x = 0; 159 | static int y = 0; 160 | 161 | //Get the mouse offsets 162 | x = e->button.x; 163 | y = e->button.y; 164 | 165 | for(int i = 0; i < currentMenu->elementsLength; i++) 166 | { 167 | bool isOn = (( x > currentMenu->elements[i].box.x) && ( x < currentMenu->elements[i].box.x + currentMenu->elements[i].box.w ) && 168 | ( y > currentMenu->elements[i].box.y) && ( y < currentMenu->elements[i].box.y + currentMenu->elements[i].box.h )); 169 | 170 | if(e->type == SDL_MOUSEMOTION) 171 | { 172 | // Select hovering element 173 | if (isOn) 174 | currentMenu->selectedElement = ¤tMenu->elements[i]; 175 | } 176 | else if(e->type == SDL_MOUSEBUTTONUP && e->button.button == SDL_BUTTON_LEFT) 177 | { 178 | if (isOn && currentMenu->selectedElement->OnClick != NULL) 179 | { 180 | currentMenu->selectedElement->OnClick(); 181 | return; 182 | } 183 | } 184 | } 185 | } 186 | 187 | void G_SetMenu(menu_t* newMenu) 188 | { 189 | currentMenu = newMenu; 190 | currentMenu->elementsLength = newMenu->elementsLength; 191 | } 192 | 193 | //------------------------------------- 194 | // BUTTONS CALLBACKS 195 | //------------------------------------- 196 | static void CALLBACK_MAINMENU_NewGame(void) 197 | { 198 | // Initialize the player 199 | player.hasBeenInitialized = false; 200 | 201 | G_InitGame(); 202 | A_ChangeState(GSTATE_GAME); 203 | } 204 | 205 | static void CALLBACK_MAINMENU_Options(void) 206 | { 207 | G_SetMenu(&OptionsMenu); 208 | A_ChangeState(GSTATE_MENU); 209 | } 210 | 211 | 212 | static void CALLBACK_MAINMENU_Quit(void) 213 | { 214 | application.quit = true; 215 | } 216 | 217 | 218 | static void CALLBACK_ReturnToMainMenu(void) 219 | { 220 | G_SetMenu(&MainMenu); 221 | A_ChangeState(GSTATE_MENU); 222 | } 223 | 224 | static void CALLBACK_OPTIONSMENU_ChangeGraphics(void) 225 | { 226 | if(r_CurrentGraphicsSetting+1 < 3) 227 | r_CurrentGraphicsSetting++; 228 | else 229 | r_CurrentGraphicsSetting = 0; 230 | 231 | R_SetRenderingGraphics(r_CurrentGraphicsSetting); 232 | R_ClearRendering(); 233 | } 234 | 235 | static void CALLBACK_OPTIONSMENU_ChangeShowFPS(void) 236 | { 237 | showFPS = !showFPS; 238 | } 239 | 240 | static void CALLBACK_Continue(void) 241 | { 242 | if(player.hasBeenInitialized) 243 | A_ChangeState(GSTATE_GAME); 244 | } 245 | 246 | static void CALLBACK_MAINMENU_About(void) 247 | { 248 | G_SetMenu(&AboutMenu); 249 | A_ChangeState(GSTATE_MENU); 250 | } -------------------------------------------------------------------------------- /src/Engine/G_AI.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "G_AI.h" 4 | #include "G_Player.h" 5 | #include "G_Pathfinding.h" 6 | #include "P_Physics.h" 7 | #include "G_AIBehaviour.h" 8 | 9 | // Dynamic AI list 10 | dynamicSprite_t* allDynamicSprites[OBJECTARRAY_DEFAULT_SIZE_HIGH]; 11 | unsigned int allDynamicSpritesLength = 0; 12 | 13 | // Initializes the dynamic sprites 14 | // Switch on the spriteID to be able to configure parameters for each entity, could be done in a file to avoid having to recompile to change stuff like speed 15 | void G_AIInitialize(dynamicSprite_t* cur, int level, int spriteID, int x, int y) 16 | { 17 | // Check if there's room for this ai 18 | if(allDynamicSpritesLength+1 >= OBJECTARRAY_DEFAULT_SIZE_HIGH) 19 | { 20 | free (cur); 21 | return; 22 | } 23 | 24 | //--------------------- 25 | // Base 26 | //--------------------- 27 | cur->type = DS_TYPE_AI; 28 | 29 | cur->base.active = true; 30 | cur->base.level = level; 31 | cur->base.level = SDL_clamp(cur->base.level, 0, MAX_N_LEVELS-1); 32 | 33 | cur->base.z = TILE_SIZE * (level); 34 | cur->verticalMovementDelta = 0.0f; 35 | cur->base.gridPos.x = x; 36 | cur->base.gridPos.y = y; 37 | 38 | cur->canBeHit = true; 39 | 40 | // Get World Pos 41 | cur->base.pos.x = x * TILE_SIZE; 42 | cur->base.pos.y = y * TILE_SIZE; 43 | 44 | cur->base.collisionCircle.pos.x = cur->base.pos.x; 45 | cur->base.collisionCircle.pos.y = cur->base.pos.y; 46 | cur->base.collisionCircle.r = 32; 47 | 48 | // Get ID 49 | cur->base.spriteID = spriteID; 50 | cur->base.sheetLength = tomentdatapack.spritesSheetsLenghtTable[spriteID]; 51 | 52 | //--------------------- 53 | // Dynamic-Specific 54 | //--------------------- 55 | cur->state = DS_STATE_IDLE; 56 | cur->isAlive = true; 57 | cur->speed = 2.0f; 58 | 59 | cur->curAnim = NULL; 60 | cur->curAnimLength = 0; 61 | cur->animTimer = U_TimerCreateNew(); 62 | cur->animPlay = false; 63 | cur->animFrame = 0; 64 | cur->animPlayOnce = false; 65 | 66 | // Init attributes based on the sprite 67 | switch(cur->base.spriteID) 68 | { 69 | case DS_Skeleton: 70 | cur->base.name = "Skeleton"; 71 | cur->isBoss = false; 72 | 73 | cur->BehaviourUpdate = G_AI_BehaviourMeeleEnemy; 74 | 75 | cur->speed = 2.0f; 76 | cur->attributes.maxHealth = 100.0f; 77 | cur->attributes.curHealth = cur->attributes.maxHealth; 78 | 79 | cur->attributes.maxMana = 100.0f; 80 | cur->attributes.curMana = cur->attributes.maxMana; 81 | 82 | cur->attributes.baseDamage = 5.0f; 83 | cur->attributes.attackChance = 80; 84 | cur->attributes.criticalChance = 5; 85 | cur->attributes.criticalModifier = 1.5f; 86 | break; 87 | 88 | case DS_SkeletonElite: 89 | cur->base.name = "Royal Skeleton"; 90 | cur->isBoss = true; 91 | cur->bossPreventClimbingLaddersWhileFighting = true; 92 | cur->bossPreventOpeningDoorsWhileFighting = true; 93 | cur->bossPreventActivatingTriggersWhileFighting = false; 94 | 95 | cur->BehaviourUpdate = G_AI_BehaviourMeeleEnemy; 96 | 97 | cur->speed = 4.0f; 98 | cur->attributes.maxHealth = 600.0f; 99 | cur->attributes.curHealth = cur->attributes.maxHealth; 100 | 101 | cur->attributes.maxMana = 600.0f; 102 | cur->attributes.curMana = cur->attributes.maxMana; 103 | 104 | cur->attributes.baseDamage = 6.0f; 105 | cur->attributes.attackChance = 90; 106 | cur->attributes.criticalChance = 25; 107 | cur->attributes.criticalModifier = 1.5f; 108 | break; 109 | 110 | 111 | case DS_SkeletonBurnt: 112 | cur->base.name = "Burnt Skeleton"; 113 | cur->isBoss = false; 114 | 115 | cur->BehaviourUpdate = G_AI_BehaviourCasterEnemy; 116 | cur->spellInUse = SPELL_FIREBALL1; 117 | 118 | cur->speed = 3.0f; 119 | cur->attributes.maxHealth = 50.0f; 120 | cur->attributes.curHealth = cur->attributes.maxHealth; 121 | 122 | cur->attributes.maxMana = 100.0f; 123 | cur->attributes.curMana = cur->attributes.maxMana; 124 | 125 | cur->attributes.baseDamage = 5.0f; 126 | cur->attributes.attackChance = 80; 127 | cur->attributes.criticalChance = 5; 128 | cur->attributes.criticalModifier = 1.5f; 129 | break; 130 | 131 | case DS_SkeletonLord: 132 | cur->base.name = "Skeleton's Lord"; 133 | cur->isBoss = true; 134 | cur->bossPreventClimbingLaddersWhileFighting = false; 135 | cur->bossPreventOpeningDoorsWhileFighting = true; 136 | cur->bossPreventActivatingTriggersWhileFighting = true; 137 | 138 | cur->BehaviourUpdate = G_AI_BehaviourSkeletonLord; 139 | cur->spellInUse = SPELL_FIREBALL1; 140 | 141 | cur->speed = 4.25f; 142 | cur->attributes.maxHealth = 2250.0f; 143 | cur->attributes.curHealth = cur->attributes.maxHealth; 144 | 145 | cur->attributes.maxMana = 2250.0f; 146 | cur->attributes.curMana = cur->attributes.maxMana; 147 | 148 | cur->attributes.baseDamage = 20.0f; 149 | cur->attributes.attackChance = 100; 150 | cur->attributes.criticalChance = 10; 151 | cur->attributes.criticalModifier = 1.5f; 152 | 153 | // This boss has cooldowns for spells 154 | cur->cooldowns[0] = U_TimerCreateNew(); // Abs cooldown 155 | cur->cooldowns[1] = U_TimerCreateNew(); // Fireball cooldown 156 | cur->cooldowns[2] = U_TimerCreateNew(); // Spell1 cooldown 157 | cur->cooldowns[3] = U_TimerCreateNew(); // Spell2 cooldown 158 | break; 159 | 160 | default: 161 | printf("AI with ID %d was not initalized. Setting it with base stats.\n"); 162 | cur->isBoss = false; 163 | cur->BehaviourUpdate = G_AI_BehaviourMeeleEnemy; 164 | cur->attributes.maxHealth = 100.0f; 165 | cur->attributes.curHealth = cur->attributes.maxHealth; 166 | 167 | cur->attributes.maxMana = 100.0f; 168 | cur->attributes.curMana = cur->attributes.maxMana; 169 | 170 | cur->attributes.baseDamage = 5.0f; 171 | cur->attributes.attackChance = 80; 172 | cur->attributes.criticalChance = 5; 173 | cur->attributes.criticalModifier = 1.5f; 174 | break; 175 | } 176 | 177 | cur->aggroedPlayer = false; 178 | 179 | 180 | // Add it to the dynamic sprite list 181 | allDynamicSprites[allDynamicSpritesLength] = cur; 182 | allDynamicSpritesLength++; 183 | } 184 | 185 | void G_AIUpdate(void) 186 | { 187 | for(int i = 0; i < allDynamicSpritesLength; i++) 188 | { 189 | dynamicSprite_t* cur = allDynamicSprites[i]; 190 | 191 | if(cur == NULL || cur->base.active == false) 192 | continue; 193 | 194 | cur->BehaviourUpdate(cur); 195 | } 196 | } 197 | 198 | void G_AIDie(dynamicSprite_t* cur) 199 | { 200 | if(!cur->isAlive) 201 | return; 202 | 203 | cur->isAlive = false; 204 | G_AIPlayAnimationOnce(cur, ANIM_DIE); 205 | cur->base.collisionCircle.r = -1.0f; // Remove collision 206 | 207 | // Add to death map 208 | G_AddToDeadDynamicSpriteMap(cur, cur->base.level, cur->base.gridPos.y, cur->base.gridPos.x); 209 | 210 | // Clear from dynamic map 211 | G_ClearFromDynamicSpriteMap(cur->base.level, cur->base.gridPos.y, cur->base.gridPos.x); 212 | 213 | // Check if this was the boss the player was fighting, if he was, release the UI 214 | if(player.isFightingBoss && player.bossFighting == cur) 215 | { 216 | player.isFightingBoss = false; 217 | player.bossFighting = NULL; 218 | } 219 | } 220 | 221 | 222 | void G_AIAttackPlayer(dynamicSprite_t* cur) 223 | { 224 | // Attack chance 225 | int attack = (rand() % (100)) + 1; 226 | int critical = (rand() % (100)) + 1; 227 | 228 | if(attack <= cur->attributes.attackChance) 229 | { 230 | float dmg = cur->attributes.baseDamage; 231 | 232 | if(critical <= cur->attributes.criticalChance) 233 | dmg *= cur->attributes.criticalModifier; 234 | 235 | G_PlayerTakeDamage(dmg); 236 | } 237 | } 238 | 239 | 240 | void G_AIPlayAnimationOnce(dynamicSprite_t* cur, objectanimationsID_e animID) 241 | { 242 | cur->animTimer->Start(cur->animTimer); 243 | cur->animPlayOnce = true; 244 | cur->animFrame = 0; 245 | 246 | switch(animID) 247 | { 248 | case ANIM_IDLE: 249 | cur->state = DS_STATE_IDLE; 250 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 251 | break; 252 | 253 | case ANIM_DIE: 254 | cur->state = DS_STATE_DEAD; 255 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 256 | break; 257 | 258 | case ANIM_ATTACK1: 259 | cur->state = DS_STATE_ATTACKING; 260 | cur->animSpeed = ANIMATION_ATTACK_SPEED_DIVIDER; 261 | break; 262 | 263 | case ANIM_CAST_SPELL: 264 | cur->state = DS_STATE_CASTING; 265 | cur->animSpeed = ANIMATION_ATTACK_SPEED_DIVIDER; 266 | break; 267 | 268 | case ANIM_SPECIAL1: 269 | cur->state = DS_STATE_SPECIAL1; 270 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 271 | break; 272 | 273 | case ANIM_SPECIAL2: 274 | cur->state = DS_STATE_SPECIAL2; 275 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 276 | break; 277 | } 278 | 279 | cur->animPlay = true; 280 | } 281 | 282 | void G_AIPlayAnimationLoop(dynamicSprite_t* cur, objectanimationsID_e animID) 283 | { 284 | cur->animTimer->Start(cur->animTimer); 285 | cur->animPlayOnce = false; 286 | cur->animFrame = 0; 287 | 288 | switch(animID) 289 | { 290 | case ANIM_IDLE: 291 | cur->state = DS_STATE_IDLE; 292 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 293 | break; 294 | 295 | case ANIM_DIE: 296 | cur->state = DS_STATE_DEAD; 297 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 298 | break; 299 | 300 | case ANIM_SPECIAL1: 301 | cur->state = DS_STATE_SPECIAL1; 302 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 303 | break; 304 | 305 | case ANIM_SPECIAL2: 306 | cur->state = DS_STATE_SPECIAL2; 307 | cur->animSpeed = ANIMATION_SPEED_DIVIDER; 308 | break; 309 | } 310 | 311 | cur->animPlay = true; 312 | } 313 | 314 | void G_AITakeDamage(dynamicSprite_t* cur, float amount) 315 | { 316 | if(cur != NULL && cur->isAlive) 317 | { 318 | cur->attributes.curHealth -= amount; 319 | cur->aggroedPlayer = true; 320 | 321 | // Check death 322 | if(cur->attributes.curHealth <= 0.0f) 323 | G_AIDie(cur); 324 | } 325 | } 326 | 327 | bool G_AICanAttack(dynamicSprite_t* cur) 328 | { 329 | return (cur->state != DS_STATE_ATTACKING && cur->state != DS_STATE_DEAD && cur->state != DS_STATE_CASTING && cur->state != DS_STATE_SPECIAL1 && cur->state != DS_STATE_SPECIAL2); 330 | } -------------------------------------------------------------------------------- /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 640 19 | #define MAX_PROJECTION_PLANE_HEIGHT 480 20 | 21 | // Runtime Graphichs 22 | typedef enum GraphicsOptions_e 23 | { 24 | GRAPHICS_LOW = 0, 25 | GRAPHICS_MEDIUM, 26 | GRAPHICS_HIGH 27 | } GraphicsOptions_e; 28 | 29 | extern GraphicsOptions_e r_CurrentGraphicsSetting; 30 | 31 | // Projection Plane 32 | extern int PROJECTION_PLANE_WIDTH; 33 | extern int PROJECTION_PLANE_HEIGHT; 34 | extern int PROJECTION_PLANE_CENTER; 35 | 36 | //#define DISTANCE_TO_PROJECTION ((PROJECTION_PLANE_WIDTH / 2) / tan(PLAYER_FOV /2)) 37 | extern int DISTANCE_TO_PROJECTION; 38 | 39 | // Player's look up/down 40 | extern int MAX_VERTICAL_HEAD_MOV; 41 | extern int MIN_VERTICAL_HEAD_MOV; 42 | 43 | // ========================================= 44 | // Minimap 45 | // ========================================= 46 | #define MINIMAP_DIVIDER 16 // Divider for making the minimap smaller 47 | #define MINIMAP_PLAYER_WIDTH 4 // Dividers for making the player in the minimap smaller 48 | #define MINIMAP_PLAYER_HEIGHT 4 49 | 50 | // ========================================= 51 | // Sprites 52 | // ========================================= 53 | #define MAXVISABLE 50 // * MAX_FLOORS, the visibleSprites array and visibleTiles are reused each time 54 | #define MAX_SPRITE_HEIGHT 2000 55 | #define ANIMATION_SPEED_DIVIDER 200 56 | #define ANIMATION_ATTACK_SPEED_DIVIDER 300 57 | 58 | // Visible Sprite Determination 59 | extern bool visibleTiles[MAP_HEIGHT][MAP_WIDTH]; 60 | extern sprite_t visibleSprites[MAXVISABLE]; 61 | extern int visibleSpritesLength; 62 | 63 | // ========================================= 64 | // Thin wall Transparency 65 | // ========================================= 66 | 67 | // Max amounts of times the ray can ignore and save a thin wall (see through a see through wall) 68 | #define MAX_THIN_WALL_TRANSPARENCY_RECURSION 4 69 | 70 | // Found thin walls to draw 71 | extern walldata_t currentThinWalls[MAX_PROJECTION_PLANE_WIDTH * MAX_THIN_WALL_TRANSPARENCY_RECURSION]; 72 | extern unsigned visibleThinWallsLength; 73 | 74 | // Alert message 75 | #define ALERT_MESSAGE_DEF_X 360 76 | #define ALERT_MESSAGE_DEF_Y 10 77 | 78 | extern alertMessage_t* alertMessagesHead; 79 | 80 | 81 | // ========================================= 82 | // Debug 83 | // ========================================= 84 | #define DEBUG_RAYCAST_MINIMAP 0 85 | #define DEBUG_VISIBLE_TILES_MINIMAP 1 86 | #define DEBUG_DYNAMIC_SPRITES 0 87 | #define DEBUG_VISIBLE_SPRITES_MINIMAP 0 88 | 89 | extern uint32_t r_blankColor; // Color shown when nothing else is in the renderer 90 | extern uint32_t r_transparencyColor; // Color marked as "transparency", rendering of this color will be skipped for surfaces 91 | 92 | // Wall heights, saved for each x for each level 93 | extern float zBuffer[MAX_PROJECTION_PLANE_HEIGHT][MAX_PROJECTION_PLANE_WIDTH]; 94 | 95 | extern float floorcastLookUp[MAX_N_LEVELS][MAX_PROJECTION_PLANE_HEIGHT]; 96 | extern float ceilingcastLookUp[MAX_N_LEVELS][MAX_PROJECTION_PLANE_HEIGHT]; 97 | 98 | // Drawables 99 | #define MAX_DRAWABLES MAX_PROJECTION_PLANE_WIDTH * MAX_THIN_WALL_TRANSPARENCY_RECURSION + MAXVISABLE 100 | extern drawabledata_t allDrawables[MAX_DRAWABLES]; 101 | extern int allDrawablesLength; 102 | 103 | extern bool debugRendering; 104 | extern bool r_debugPathfinding; 105 | 106 | 107 | 108 | //------------------------------------- 109 | // Initializes the rendering 110 | //------------------------------------- 111 | void R_InitRendering(void); 112 | 113 | void R_SetRenderingGraphics(GraphicsOptions_e setting); 114 | 115 | //------------------------------------- 116 | // Fill buffers and put framebuffers on top of each other 117 | //------------------------------------- 118 | void R_ComposeFrame(void); 119 | 120 | 121 | //------------------------------------- 122 | // Updates the screen to the win_surface 123 | //------------------------------------- 124 | void R_FinishUpdate(void); 125 | 126 | //------------------------------------- 127 | // Gets a pixel from a surface 128 | //------------------------------------- 129 | Uint32 R_GetPixelFromSurface(SDL_Surface *surface, int x, int y); 130 | 131 | //------------------------------------- 132 | // Given an SDL_Surface, extracts the pixels of it and puts them in the selected framebuffer 133 | //------------------------------------- 134 | void R_BlitIntoScreen(SDL_Rect* size, SDL_Surface* sur, SDL_Rect* pos); 135 | 136 | //------------------------------------- 137 | // Given an SDL_Surface, extracts the pixels of it and puts them in the selected framebuffer 138 | //------------------------------------- 139 | void R_BlitIntoScreenScaled(SDL_Rect* size, SDL_Surface* sur, SDL_Rect* pos); 140 | 141 | //------------------------------------- 142 | // Given a color, extracts draws it in the selected framebuffer 143 | //------------------------------------- 144 | void R_BlitColorIntoScreen(int color, SDL_Rect* pos); 145 | 146 | //------------------------------------- 147 | // Draw lines using Bresenham's 148 | //------------------------------------- 149 | void R_DrawLine(int x0, int y0, int x1, int y1, int color); 150 | 151 | //------------------------------------- 152 | // Draw a single pixel 153 | //------------------------------------- 154 | void R_DrawPixel(int x, int y, int color); 155 | 156 | //------------------------------------- 157 | // Draw a single pixel with shading 158 | //------------------------------------- 159 | void R_DrawPixelShaded(int x, int y, int color, float intensity, float dist, bool usesFog, float fogBlendingFactor); 160 | 161 | //------------------------------------- 162 | // Draw a column of pixels with shading 163 | //------------------------------------- 164 | void R_DrawColumnOfPixelShaded(int x, int y, int endY, int color, float intensity, float distance, bool usesFog, float fogBlendingFactor); 165 | 166 | //------------------------------------- 167 | // Draw a column of pixel 168 | //------------------------------------- 169 | void R_DrawColumn(int x, int y, int endY, int color); 170 | 171 | //------------------------------------- 172 | // Sets the screen to r_blankColor 173 | //------------------------------------- 174 | void R_ClearRendering(void); 175 | 176 | //------------------------------------- 177 | // Draws the minimap 178 | //------------------------------------- 179 | void R_DrawMinimap(void); 180 | 181 | //------------------------------------- 182 | // Draws the background of the game view 183 | //------------------------------------- 184 | void R_DrawBackground(void); 185 | 186 | 187 | //------------------------------------- 188 | // Calls the the Raycast routines and draws the walls 189 | //------------------------------------- 190 | void R_Raycast(void); 191 | 192 | //------------------------------------- 193 | // Raycasts the camera's level, therefore with occlusion 194 | //------------------------------------- 195 | void R_RaycastPlayersLevel(int level, int x, float _rayAngle); 196 | 197 | //------------------------------------- 198 | // Raycasts levels above and below the camera's level, therefore without occlusion 199 | //------------------------------------- 200 | void R_RaycastLevelNoOcclusion(int level, int x, float _rayAngle); 201 | 202 | //------------------------------------- 203 | // Draws the bottom face of a cube that's located above camera's head 204 | //------------------------------------- 205 | void R_DrawWallBottom(walldata_t* wall, float height, float screenZ, bool isInFront); 206 | 207 | //------------------------------------- 208 | // Draws the bottom top of a cube that's located below camera's head 209 | //------------------------------------- 210 | void R_DrawWallTop(walldata_t* wall, float height, float screenZ, bool isInFront); 211 | 212 | //------------------------------------- 213 | // Drawables routine, sort and render drawables 214 | //------------------------------------- 215 | void R_DrawDrawables(void); 216 | 217 | //------------------------------------- 218 | // Draw the passed thin wall 219 | //------------------------------------- 220 | void R_DrawThinWall(walldata_t* wall); 221 | 222 | //------------------------------------- 223 | // Floorcast and ceilingcast 224 | // Params: 225 | // - end = the end of the wall that states where to start to floorcast 226 | // - rayAngle = the current rayangle 227 | // - x = the x coordinate on the screen for this specific floor cast call 228 | //------------------------------------- 229 | void R_FloorCasting(int level, int end, float rayAngle, int x, float wallHeight); 230 | void R_FloorCastingOld(int end, float rayAngle, int x, float wallHeight); 231 | 232 | void R_FloorCastingHor(); 233 | 234 | //------------------------------------- 235 | // Floorcast and ceilingcast 236 | // Params: 237 | // - end = the end of the wall that states where to start to floorcast 238 | // - rayAngle = the current rayangle 239 | // - x = the x coordinate on the screen for this specific floor cast call 240 | //------------------------------------- 241 | void R_CeilingCasting(int level, float start, float rayAngle, int x, float wallHeight); 242 | void R_CeilingCastingOld(int level,float start, float rayAngle, int x, float wallHeight); 243 | bool R_DoesCeilingCast(wallObject_t* obj); 244 | 245 | bool R_DoesFloorCast(wallObject_t* obj); 246 | void R_CeilingCastingHor(int level); 247 | 248 | //------------------------------------- 249 | // Adds a sprite to the visible sprite array and adds its corresponding drawable 250 | //------------------------------------- 251 | void R_AddToVisibleSprite(int gridX, int gridY, int level, int spriteID); 252 | 253 | //------------------------------------- 254 | // Adds a dynamic sprite to the visible sprite array and adds its corresponding drawable 255 | //------------------------------------- 256 | void R_AddDynamicToVisibleSprite(int level, int gridX, int gridY); 257 | 258 | //------------------------------------- 259 | // Adds a dynamic sprite to the visible sprite array and adds its corresponding drawable 260 | //------------------------------------- 261 | void R_AddDeadDynamicToVisibleSprite(int level, int gridX, int gridY); 262 | 263 | //------------------------------------- 264 | // Draws the visible sprites 265 | //------------------------------------- 266 | void R_DrawSprite(sprite_t* sprite); 267 | 268 | //------------------------------------- 269 | // Draws the visible sprites 270 | //------------------------------------- 271 | void R_DrawDynamicSprite(dynamicSprite_t* sprite); 272 | 273 | //------------------------------------- 274 | // Given a level and the grid coordinates, returns what is in the map 275 | //------------------------------------- 276 | wallObject_t* R_GetWallObjectFromMap(int level, int y, int x); 277 | 278 | //------------------------------------- 279 | // Given a level and the grid coordinates, returns what is in the map 280 | //------------------------------------- 281 | int R_GetValueFromSpritesMap(int level, int y, int x); 282 | 283 | void R_SetValueFromSpritesMap(int level, int y, int x, int value); 284 | 285 | void R_SetValueFromCollisionMap(int level, int y, int x, int value); 286 | 287 | //------------------------------------- 288 | // Draws a column of pixels with texture mapping 289 | //------------------------------------- 290 | void R_DrawColumnTextured(int x, int y, int endY, SDL_Surface* texture, int xOffset, float wallheight); 291 | 292 | //------------------------------------- 293 | // Draws a column of pixels with texture mapping and shading 294 | //------------------------------------- 295 | 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); 296 | 297 | //------------------------------------- 298 | // Queue an Alert Message to be displayed 299 | //------------------------------------- 300 | void R_QueueAlertMessage(alertMessage_t* m, int x, int y, char* msg, float duration, float size); 301 | 302 | //------------------------------------- 303 | // Updates and renders the Alert Messages queue 304 | //------------------------------------- 305 | void R_UpdateAlertMessages(void); 306 | 307 | int R_ThreadRoutine(void* data); 308 | 309 | #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 | } -------------------------------------------------------------------------------- /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 | } textureID_e; 55 | 56 | // All sprites 57 | typedef enum spritesObjectID_e 58 | { 59 | // 0 = Empty 60 | S_Barrel1 = 1, 61 | S_Campfire, 62 | DS_Skeleton, 63 | S_Fireball1, 64 | S_PickupAxe, 65 | S_PickupHealthPotion, 66 | S_PickupManaPotion, 67 | S_IceDart1, 68 | S_TomeFireball1, 69 | S_TomeIceDart1, 70 | S_Table1, 71 | S_SkullStatic, 72 | DS_SkeletonElite, 73 | S_AltarEmpty, 74 | S_AltarHealth, 75 | S_AltarMana, 76 | DS_SkeletonBurnt, 77 | S_PickupGreatsword, 78 | DS_SkeletonLord 79 | } spritesObjectID_t; 80 | 81 | // All skies textures 82 | typedef enum skiesObjectID_e 83 | { 84 | // 0 = Empty 85 | SKY_Default1 = 1, 86 | SKY_Red1 87 | } skiesObjectID_e; 88 | 89 | // Animations of an Object 90 | typedef enum objectanimationsID_e 91 | { 92 | ANIM_IDLE = 0, 93 | ANIM_ATTACK1, 94 | ANIM_DIE, 95 | ANIM_CAST_SPELL, 96 | ANIM_SPECIAL1, 97 | ANIM_SPECIAL2 98 | } objectanimationsID_e; 99 | 100 | // Specific object data for AI 101 | typedef struct objectAnimations_s 102 | { 103 | struct object_s* belongsTo; 104 | 105 | SDL_Surface* animIdle; // The image 106 | unsigned animIdleSheetLength; // The sheet lenght 107 | unsigned animIdleActionFrame; // The frame that has to be reached in order to perform the animation action (like casting a spell) 108 | 109 | SDL_Surface* animDie; 110 | unsigned animDieSheetLength; 111 | unsigned animDieActionFrame; 112 | 113 | SDL_Surface* animAttack; 114 | unsigned animAttackSheetLength; 115 | unsigned animAttackActionFrame; 116 | 117 | SDL_Surface* animCastSpell; 118 | unsigned animCastSpellSheetLength; 119 | unsigned animCastSpellActionFrame; 120 | 121 | // Extra Animations 122 | SDL_Surface* animSpecial1; 123 | unsigned animSpecial1SheetLength; 124 | unsigned animSpecial1ActionFrame; 125 | 126 | SDL_Surface* animSpecial2; 127 | unsigned animSpecial2SheetLength; 128 | unsigned animSpecial2ActionFrame; 129 | } objectanimations_t; 130 | 131 | 132 | // Data type for Walls 133 | typedef struct wallAsset_s 134 | { 135 | int ID; 136 | 137 | byte flags; // Flags to diversify types of objects 138 | 139 | // Callbacks 140 | void (*Callback)(char* data); 141 | } wallAsset_t; 142 | 143 | // Identify the faces of the walls 144 | #define TEXTURE_ARRAY_TOP 0 145 | #define TEXTURE_ARRAY_BOTTOM 1 146 | #define TEXTURE_ARRAY_LEFT 2 147 | #define TEXTURE_ARRAY_RIGHT 3 148 | #define TEXTURE_ARRAY_UP 4 149 | #define TEXTURE_ARRAY_DOWN 5 150 | 151 | // Wall Objects are dynamic wall data loaded from the map file 152 | typedef struct wallObject_s 153 | { 154 | // The related wallAsset_t 155 | int assetID; 156 | 157 | // Texture IDs for each face, used for cube walls, after being init they point to texture 158 | // TOP BOTTOM LEFT RIGHT UP DOWN 159 | // (0, 1, 2, 3, 4, 5 ) 160 | // Top texture (index 0) is the default texture, used for non cubed wall (like doors and thin walls) 161 | int texturesArray[6]; 162 | 163 | // Data of the callback (i.e. the level to load for the wallTrigger asset) 164 | char data[OBJECTARRAY_DEFAULT_SIZE]; 165 | } wallObject_t; 166 | 167 | 168 | /* object_t Flags 169 | 170 | // ============ 171 | // For walls 172 | // ============ 173 | // 0000000 0 174 | // \ 175 | // 1 = Is Ladder 176 | */ 177 | // The common Object data 178 | typedef struct object_s 179 | { 180 | int ID; 181 | SDL_Surface* texture; 182 | struct object_s* alt; 183 | byte flags; // Flags to diversify types of objects 184 | 185 | objectanimations_t* animations; 186 | 187 | // Callbacks 188 | char* data; 189 | void (*Callback)(char* data); 190 | } object_t; 191 | 192 | // Lightweight version of object_t suited for textures 193 | typedef struct textureObject_s 194 | { 195 | int ID; 196 | SDL_Surface* texture; 197 | byte flags; 198 | } textureObject_t; 199 | 200 | 201 | // The Text rendering is not hardcoded to use 16x6 elements font sheets, but the translation map is, 202 | // if you wish to use more character or a different map of the characters, you'll have to edit the translation code, 203 | // but the system's code should work just fine 204 | #define FONT_MAX_ELEMENTS_WIDTH 16 205 | #define FONT_MAX_ELEMENTS_HEIGHT 6 206 | 207 | // Font data 208 | typedef struct fontSheet_s 209 | { 210 | unsigned int width; // Width of the tiles that compose the font sheet 211 | int numHorElements; // How many horizontal items this sheet has 212 | int numVerElements; // How many vertical items this sheet has 213 | SDL_Surface* texture; // The whole fontsheet as an SDL_Surface 214 | int glyphsWidth[FONT_MAX_ELEMENTS_HEIGHT][FONT_MAX_ELEMENTS_WIDTH]; // The actual used width of each glyph, used for text-spacing 215 | } fontsheet_t; 216 | 217 | // Lightweight version of object_t suited for UI Assets 218 | typedef struct uiAssets_s 219 | { 220 | SDL_Surface* texture; 221 | } uiAssets_t; 222 | 223 | // All Fonts IDS 224 | typedef enum fontsID_e 225 | { 226 | FONT_BLKCRY = 0, 227 | } fontsID_t; 228 | 229 | // Menu Assets IDS 230 | typedef enum uiAssetsID_e 231 | { 232 | M_ASSET_SELECT_CURSOR = 0, 233 | M_ASSET_TITLE, 234 | G_ASSET_HEALTHBAR_EMPTY, 235 | G_ASSET_HEALTHBAR_FILL, 236 | G_ASSET_MANABAR_EMPTY, 237 | G_ASSET_MANABAR_FILL, 238 | G_ASSET_ICON_FISTS, 239 | G_ASSET_ICON_AXE, 240 | G_ASSET_ICON_SPELL_FIREBALL1, 241 | G_ASSET_UI_CROSSHAIR, 242 | G_ASSET_ICON_SPELL_ICEDART1, 243 | G_ASSET_UI_CROSSHAIR_HIT, 244 | G_ASSET_BOSS_HEALTHBAR_EMPTY, 245 | G_ASSET_BOSS_HEALTHBAR_FILL, 246 | G_ASSET_ICON_GREATSWORD 247 | } uiAssetsID_e; 248 | 249 | // FP Player IDs 250 | typedef enum playerFPID_e 251 | { 252 | PLAYER_FP_HANDS = 0, 253 | PLAYER_FP_AXE, 254 | PLAYER_FP_GREATSWORD 255 | } playerFPID_e; 256 | 257 | 258 | /* object_t Flags 259 | 260 | // ============ 261 | // For walls 262 | // ============ 263 | // 0000000 0 264 | // \ 265 | // 1 = Is Thin Wall 266 | 267 | // 000000 0 0 268 | // \ 269 | // 1 = Is Vertical (used with thin Wall) 270 | 271 | // 00000 0 00 272 | // \ 273 | // 1 = Is Door 274 | 275 | // 0000 0 000 276 | // \ 277 | // 1 = Is Trigger 278 | 279 | // 000 0 0000 280 | // \ 281 | // 1 = Is Invisible (Skipped by non-occlusive raycast) 282 | 283 | // ============ 284 | // For sprites 285 | // ============ 286 | For sprites 287 | // 0000000 0 288 | // \ 289 | // 1 = solid (used for collision checking) 290 | 291 | // 000000 0 0 292 | // \ 293 | // 1 = Animated sprite (uses horizontal sheet) 294 | 295 | // 00000 0 00 296 | // \ 297 | // 1 = Dynamic sprite: A dynamic sprite is a sprite that can move in the world and update the sprites maps, used for AI 298 | 299 | // 0000 0 000 300 | // \ 301 | // 1 = Auto call callback (if present) upon player's collision 302 | */ 303 | 304 | // Table of Content elements for opening archives (MUST BE IN SYNCH WITH ARCH) 305 | typedef struct tocElement_s 306 | { 307 | uint32_t id; 308 | uint32_t startingOffset; 309 | uint32_t size; 310 | } tocElement_t; 311 | 312 | // IDs of the Images in the IMGArchive 313 | // 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 314 | typedef enum imgIDs_e 315 | { 316 | IMG_ID_EDEFAULT_1 = 0, 317 | IMG_ID_W_1, 318 | IMG_ID_W_1Alt, 319 | IMG_ID_W_2, 320 | IMG_ID_WD_Gate1, 321 | IMG_ID_WD_Gate1Alt, 322 | IMG_ID_F_1, 323 | IMG_ID_C_1, 324 | IMG_ID_S_Barrel1, 325 | IMG_ID_S_Campfire, 326 | IMG_ID_BLKCRY_TEXT_SHEET, 327 | IMG_ID_MENU_SELECT_CURSOR, 328 | IMG_ID_MENU_TITLE, 329 | IMG_ID_SKY_DEFAULT, 330 | IMG_ID_P_HANDS_IDLE, 331 | IMG_ID_WT_CASTLE_DOORS, 332 | IMG_ID_AI_SKELETON, 333 | IMG_ID_AI_SKELETON_DEATH, 334 | IMG_ID_P_HANDS_ATTACK, 335 | IMG_ID_P_HANDS_CASTSPELL, 336 | IMG_ID_SPELL_FIREBALL1, 337 | IMG_ID_SPELL_FIREBALL1_EXPLOSION, 338 | IMG_ID_HEALTHBAR_EMPTY, 339 | IMG_ID_HEALTHBAR_FILL, 340 | IMG_ID_MANABAR_EMPTY, 341 | IMG_ID_MANABAR_FILL, 342 | IMG_ID_AI_SKELETON_ATTACK, 343 | IMG_ID_PICKUP_AXE, 344 | IMG_ID_PICKUP_HEALTH_POTION, 345 | IMG_ID_PICKUP_MANA_POTION, 346 | IMG_ID_P_AXE_IDLE, 347 | IMG_ID_P_AXE_ATTACK1, 348 | IMG_ID_ICON_FISTS, 349 | IMG_ID_ICON_AXE, 350 | IMG_ID_ICON_SPELL_FIREBALL1, 351 | IMG_ID_UI_CROSSHAIR, 352 | IMG_ID_SPELL_ICEDART1, 353 | IMG_ID_SPELL_ICEDART_EXPLOSION, 354 | IMG_ID_ICON_SPELL_ICEDART1, 355 | IMG_ID_UI_CROSSHAIR_HIT, 356 | IMG_ID_TOME_FIREBALL01, 357 | IMG_ID_TOME_ICEDART01, 358 | IMG_ID_WALL1_LADDER, 359 | IMG_ID_S_Table1, 360 | IMG_ID_S_SKULL_STATIC, 361 | IMG_ID_AI_SKELETON_ELITE, 362 | IMG_ID_AI_SKELETON_ELITE_ATTACK, 363 | IMG_ID_AI_SKELETON_ELITE_DEATH, 364 | IMG_ID_BOSS_HEALTHBAR_EMPTY, 365 | IMG_ID_BOSS_HEALTHBAR_FILL, 366 | IMG_ID_S_ALTAR_EMPTY, 367 | IMG_ID_S_ALTAR_HEALTH, 368 | IMG_ID_S_ALTAR_MANA, 369 | IMG_ID_AI_SKELETON_BURNT, 370 | IMG_ID_AI_SKELETON_BURNT_ATTACK, 371 | IMG_ID_AI_SKELETON_BURNT_DEATH, 372 | IMG_ID_F_2, 373 | IMG_ID_F_DIRT, 374 | IMG_ID_SKY_RED, 375 | IMG_ID_PIKCUP_GREATSWORD, 376 | IMG_ID_P_GREATSWORD_IDLE, 377 | IMG_ID_P_GREATSWORD_ATTACK, 378 | IMG_ID_ICON_GREATSWORD, 379 | IMG_ID_SKELETON_LORD, 380 | IMG_ID_SKELETON_LORD_ATTACK_MELEE, 381 | IMG_ID_SKELETON_LORD_ATTACK_FIREBALL, 382 | IMG_ID_SKELETON_LORD_DEATH, 383 | IMG_ID_SKELETON_LORD_SPELL_HELL, 384 | IMG_ID_SKELETON_LORD_SPELL_RESURRECTION, 385 | IMG_ID_AI_SKELETON_RESURRECTION 386 | } imgIDs_e; 387 | 388 | // Archt Data 389 | typedef struct archt_s 390 | { 391 | FILE* file; 392 | uint32_t fileLength; 393 | tocElement_t toc[MAX_TOC_LENGTH]; 394 | uint32_t tocSize; 395 | uint32_t tocElementsLenght; 396 | uint32_t tocOffset; // how many bytes (fileLength included) to skip the ToC and access the data 397 | byte* buffer; 398 | } archt_t; 399 | 400 | // The whole datapack of the game 401 | typedef struct tomentdatapack_s 402 | { 403 | // ------------------------------- 404 | // Archives 405 | // ------------------------------- 406 | 407 | // img.archt 408 | archt_t IMGArch; 409 | 410 | // Texture database 411 | textureObject_t* textures[OBJECTARRAY_DEFAULT_SIZE]; 412 | unsigned texturesLength; 413 | 414 | // Font databse 415 | fontsheet_t* fontsheets[OBJECTARRAY_DEFAULT_SIZE]; // All fonts 416 | unsigned fontsheetsLength; 417 | 418 | // ------------------------------- 419 | // UI 420 | // ------------------------------- 421 | uiAssets_t* uiAssets[OBJECTARRAY_DEFAULT_SIZE]; 422 | unsigned uiAssetsLenght; 423 | 424 | // ------------------------------- 425 | // In Game Assets 426 | // ------------------------------- 427 | 428 | // Default Engine's Objects 429 | object_t* enginesDefaults[OBJECTARRAY_DEFAULT_SIZE]; 430 | unsigned enginesDefaultsLength; 431 | 432 | // Object in the game 433 | object_t* skies[OBJECTARRAY_DEFAULT_SIZE]; 434 | unsigned skiesLength; 435 | 436 | wallAsset_t* walls[OBJECTARRAY_DEFAULT_SIZE]; 437 | unsigned wallsLength; 438 | 439 | object_t* sprites[OBJECTARRAY_DEFAULT_SIZE]; 440 | unsigned spritesLength; 441 | 442 | // Contains the value of the length of the spreadsheet for each sprite delcared 443 | // Access by spritesObjectID_e 444 | int spritesSheetsLenghtTable[OBJECTARRAY_DEFAULT_SIZE]; 445 | 446 | object_t* playersFP[OBJECTARRAY_DEFAULT_SIZE]; 447 | unsigned playersFPLength; 448 | } tomentdatapack_t; 449 | 450 | extern tomentdatapack_t tomentdatapack; 451 | 452 | //------------------------------------- 453 | // Returns true if the texture has correctly loaded, otherwise false and an error 454 | //------------------------------------- 455 | bool D_CheckTextureLoaded(SDL_Surface* ptr, int ID); 456 | 457 | //------------------------------------- 458 | // Initializes defauls for an object_t 459 | //------------------------------------- 460 | void D_InitObject(object_t* obj); 461 | 462 | //------------------------------------- 463 | // Initializes defauls for a wallAsset_t 464 | //------------------------------------- 465 | void D_InitWallAsset(wallAsset_t* obj); 466 | 467 | //------------------------------------- 468 | // Initializes defauls for a textureObject_t 469 | //------------------------------------- 470 | void D_InitTextureAsset(textureObject_t* obj); 471 | 472 | //------------------------------------- 473 | // Initializes the Asset Manager 474 | //------------------------------------- 475 | void D_InitAssetManager(void); 476 | 477 | //------------------------------------- 478 | // Opens the archives to allow objects initializations 479 | //------------------------------------- 480 | void D_OpenArchs(void); 481 | 482 | //------------------------------------- 483 | // Closes the archives to and frees buffers 484 | //------------------------------------- 485 | void D_CloseArchs(void); 486 | 487 | 488 | //------------------------------------- 489 | // Initialize the TomentDataPack elements 490 | //------------------------------------- 491 | 492 | void D_InitEnginesDefaults(void); 493 | void D_InitLoadTextures(void); 494 | void D_InitFontSheets(void); 495 | void D_InitUIAssets(void); 496 | void D_InitLoadWalls(void); 497 | void D_InitLoadSprites(void); 498 | void D_InitLoadSkies(void); 499 | void D_InitLoadPlayersFP(void); 500 | 501 | //------------------------------------- 502 | // Sets the object for the given parameters 503 | //------------------------------------- 504 | void D_SetObject(object_t* obj, int id, SDL_Surface* texture, object_t* alt); 505 | 506 | #endif -------------------------------------------------------------------------------- /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 | 6 | pathnode_t frontier[MAP_HEIGHT*MAP_WIDTH+2]; 7 | unsigned int frontierLength = 0; 8 | bool visited[MAP_HEIGHT][MAP_WIDTH]; 9 | 10 | // Adds a Node to be added to the frontier 11 | 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); 12 | 13 | // Adds the nodes adiacent to a Node to be added to the frontier 14 | static void I_AddAdiacentNodes(int level, int oGridX, int oGridY, pathnode_t* parent, bool debug, dynamicSprite_t* entity); 15 | 16 | path_t G_PerformPathfinding(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos, dynamicSprite_t* entity) //entity performing pathfinding 17 | { 18 | // Path to return 19 | path_t path; 20 | 21 | bool pathExists = false; 22 | 23 | // Reset state 24 | memset(visited, false, MAP_HEIGHT*MAP_WIDTH*sizeof(bool)); 25 | frontierLength = 0; 26 | 27 | // Insert the start node and its adiacents 28 | I_InsertNode(level, &frontier[0], gridPos.x, gridPos.y, 0, 0, 0, NULL, false, entity); 29 | I_AddAdiacentNodes(level, gridPos.x, gridPos.y, &frontier[0], false, entity); 30 | 31 | // Check element keeps track of the elements that have been checked 32 | int checkElement = 1; 33 | 34 | // While there are elements that haven't been checked 35 | while(checkElement < frontierLength) 36 | { 37 | // Get that element 38 | pathnode_t* cur = &frontier[checkElement]; 39 | 40 | // Check If the target was found 41 | if(cur->gridPos.x == gridTargetPos.x && cur->gridPos.y == gridTargetPos.y) 42 | { 43 | // If so, path exists 44 | pathExists = true; 45 | break; 46 | } 47 | 48 | // Otherwise we'll check all the other adiacent nodes 49 | I_AddAdiacentNodes(level, cur->gridPos.x, cur->gridPos.y, cur, false, entity); 50 | checkElement++; 51 | } 52 | 53 | // Once the BFS ended, if the target was found 54 | if(pathExists) 55 | { 56 | // Create the path 57 | path.isValid = true; 58 | pathnode_t* cur; 59 | 60 | // Last element of frontier is the goal, which is the first element of the path (path is reversed) 61 | cur = &frontier[checkElement]; 62 | path.nodes[0] = cur; 63 | path.nodesLength = 1; 64 | 65 | // Track back to the entity who performed the pathfinding 66 | while((cur = cur->parent) != &frontier[0]) 67 | { 68 | path.nodes[path.nodesLength] = cur; 69 | path.nodesLength++; 70 | } 71 | 72 | // Return it 73 | return path; 74 | } 75 | else 76 | { 77 | // Path is invalid 78 | path.isValid = false; 79 | return path; 80 | } 81 | } 82 | 83 | path_t G_PerformPathfindingDebug(int level, vector2Int_t gridPos, vector2Int_t gridTargetPos) 84 | { 85 | // Path to return 86 | path_t path; 87 | 88 | bool pathExists = false; 89 | 90 | // Reset state 91 | memset(visited, false, MAP_HEIGHT*MAP_WIDTH*sizeof(bool)); 92 | frontierLength = 0; 93 | 94 | // Insert the start node and its adiacents 95 | I_InsertNode(level, &frontier[0], gridPos.x, gridPos.y, 0, 0, 0, NULL, true, NULL); 96 | I_AddAdiacentNodes(level, gridPos.x, gridPos.y, &frontier[0], true, NULL); 97 | 98 | // Draw start 99 | SDL_Rect curRect; 100 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 101 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 102 | curRect.x = gridPos.x * TILE_SIZE / MINIMAP_DIVIDER; 103 | curRect.y = gridPos.y * TILE_SIZE / MINIMAP_DIVIDER; 104 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 0, 0, 255), &curRect); 105 | 106 | // Check element keeps track of the elements that have been checked 107 | int checkElement = 1; 108 | 109 | // While there are elements that haven't been checked 110 | while(checkElement < frontierLength) 111 | { 112 | // Get that element 113 | pathnode_t* cur = &frontier[checkElement]; 114 | 115 | // Check If the target was found 116 | if(cur->gridPos.x == gridTargetPos.x && cur->gridPos.y == gridTargetPos.y) 117 | { 118 | // If so, path exists 119 | pathExists = true; 120 | 121 | // Draw the path 122 | // Set X and Y 123 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 124 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 125 | curRect.x = gridTargetPos.x * TILE_SIZE / MINIMAP_DIVIDER; 126 | curRect.y = gridTargetPos.y * TILE_SIZE / MINIMAP_DIVIDER; 127 | // If it is an empty space 128 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 255, 255, 0), &curRect); 129 | SDL_UpdateWindowSurface(application.win); 130 | SDL_Delay(2000); 131 | 132 | // Stop looking 133 | break; 134 | } 135 | 136 | // Otherwise we'll check all the other adiacent nodes 137 | I_AddAdiacentNodes(level, cur->gridPos.x, cur->gridPos.y, cur, true, NULL); 138 | checkElement++; 139 | } 140 | 141 | // Once the BFS ended, if the target was found 142 | if(pathExists) 143 | { 144 | // Create the path 145 | path.isValid = true; 146 | pathnode_t* cur; 147 | 148 | // Last element of frontier is the goal, which is the first element of the path (path is reversed) 149 | cur = &frontier[checkElement]; 150 | path.nodes[0] = cur; 151 | int pathLength = 1; 152 | 153 | // Track back to the entity who performed the pathfinding 154 | while((cur = cur->parent) != &frontier[0]) 155 | { 156 | path.nodes[pathLength] = cur; 157 | pathLength++; 158 | } 159 | 160 | // There you have it 161 | printf("Path length: %d\n", pathLength); 162 | 163 | // Draw path 164 | for(int i = 0; i < pathLength; i++) 165 | { 166 | // Set X and Y 167 | curRect.w = (HALF_TILE_SIZE) / MINIMAP_DIVIDER; 168 | curRect.h = (HALF_TILE_SIZE) / MINIMAP_DIVIDER; 169 | curRect.x = (path.nodes[i]->gridPos.x) * TILE_SIZE / MINIMAP_DIVIDER +1; 170 | curRect.y = (path.nodes[i]->gridPos.y) * TILE_SIZE / MINIMAP_DIVIDER +1; 171 | 172 | 173 | // If it is an empty space 174 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 255, 0, 255), &curRect); 175 | SDL_UpdateWindowSurface(application.win); 176 | SDL_Delay(20); 177 | } 178 | 179 | SDL_Delay(4000); 180 | 181 | // Return it 182 | return path; 183 | } 184 | else 185 | { 186 | // Path is invalid 187 | path.isValid = false; 188 | return path; 189 | } 190 | } 191 | 192 | 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) 193 | { 194 | dynamicSprite_t* entityAtNode = G_GetFromDynamicSpriteMap(level, gridy, gridx); 195 | 196 | if(visited[gridy][gridx] || G_CheckCollisionMap(level, gridy, gridx) > 0 || G_CheckDynamicSpriteMap(level, gridy, gridx) && G_GetFromDynamicSpriteMap(level, gridy, gridx) != entity) 197 | return; 198 | 199 | node->gridPos.x = gridx; 200 | node->gridPos.y = gridy; 201 | node->f = f; 202 | node->g = g; 203 | node->h = h; 204 | node->parent = parent; 205 | 206 | visited[gridy][gridx] = true; 207 | 208 | frontierLength++; 209 | 210 | if(debug) 211 | { 212 | SDL_Rect curRect; 213 | // Set X and Y 214 | curRect.w = TILE_SIZE / MINIMAP_DIVIDER; 215 | curRect.h = TILE_SIZE / MINIMAP_DIVIDER; 216 | curRect.x = gridx * TILE_SIZE / MINIMAP_DIVIDER; 217 | curRect.y = gridy * TILE_SIZE / MINIMAP_DIVIDER; 218 | 219 | // If it is an empty space 220 | R_BlitColorIntoScreen(SDL_MapRGB(win_surface->format, 0, 255, 0), &curRect); 221 | SDL_Delay(5); 222 | SDL_UpdateWindowSurface(application.win); 223 | } 224 | } 225 | 226 | void I_AddAdiacentNodes(int level, int oGridX, int oGridY, pathnode_t* parent, bool debug, dynamicSprite_t* entity) 227 | { 228 | // Top 229 | if(oGridY-1 >= 0 && !visited[oGridY-1][oGridX]) 230 | I_InsertNode(level, &frontier[frontierLength], oGridX, oGridY-1, 0,0,0, parent, debug, entity); 231 | 232 | // Bottom 233 | if(oGridY+1 < MAP_HEIGHT && !visited[oGridY+1][oGridX]) 234 | I_InsertNode(level, &frontier[frontierLength], oGridX, oGridY+1, 0,0,0, parent, debug, entity); 235 | 236 | // Left 237 | if(oGridX-1 >= 0 && !visited[oGridY][oGridX-1]) 238 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY, 0,0,0, parent, debug, entity); 239 | 240 | // Right 241 | if(oGridX+1 < MAP_WIDTH && !visited[oGridY][oGridX+1]) 242 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY, 0,0,0, parent, debug, entity); 243 | 244 | 245 | // Allow Diagonal movement and prevent corner cutting 246 | // Top Left 247 | if(oGridY-1 >= 0 && oGridX-1 >= 0 && !visited[oGridY-1][oGridX-1]) 248 | if(G_CheckCollisionMap(level, oGridY-1, oGridX-1+1) == 0 && // check right 249 | G_CheckCollisionMap(level, oGridY-1+1, oGridX-1) == 0) // check under 250 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY-1, 0,0,0, parent, debug, entity); 251 | 252 | // Top Right 253 | if(oGridY-1 >= 0 && oGridX+1 < MAP_WIDTH && !visited[oGridY-1][oGridX+1]) 254 | if(G_CheckCollisionMap(level, oGridY-1+1, oGridX+1) == 0 && // check under 255 | G_CheckCollisionMap(level, oGridY-1, oGridX+1-1) == 0) // left 256 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY-1, 0,0,0, parent, debug, entity); 257 | 258 | // Bottom Left 259 | if(oGridY+1 < MAP_HEIGHT && oGridX-1 >= 0 && !visited[oGridY+1][oGridX-1]) 260 | if(G_CheckCollisionMap(level, oGridY+1, oGridX-1+1) == 0 && // check right 261 | G_CheckCollisionMap(level, oGridY+1-1, oGridX-1) == 0) // check up 262 | I_InsertNode(level, &frontier[frontierLength], oGridX-1, oGridY+1, 0,0,0, parent, debug, entity); 263 | 264 | // Bottom Right 265 | if(oGridY+1 < MAP_HEIGHT && oGridX+1 < MAP_WIDTH && !visited[oGridY+1][oGridX+1]) 266 | if(G_CheckCollisionMap(level, oGridY+1-1, oGridX+1) == 0 && // check under 267 | G_CheckCollisionMap(level, oGridY+1, oGridX+1-1) == 0) // check left 268 | I_InsertNode(level, &frontier[frontierLength], oGridX+1, oGridY+1, 0,0,0, parent, debug, entity); 269 | } 270 | 271 | bool G_CheckDynamicSpriteMap(int level, int y, int x) 272 | { 273 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 274 | { 275 | switch(level) 276 | { 277 | case 0: 278 | return (currentMap.dynamicSpritesLevel0[y][x]) != NULL ? currentMap.dynamicSpritesLevel0[y][x] : NULL; 279 | 280 | case 1: 281 | return (currentMap.dynamicSpritesLevel1[y][x]) != NULL ? currentMap.dynamicSpritesLevel1[y][x] : NULL; 282 | 283 | case 2: 284 | return (currentMap.dynamicSpritesLevel2[y][x]) != NULL ? currentMap.dynamicSpritesLevel2[y][x] : NULL; 285 | 286 | default: 287 | //printf("WARNING! Level get was out of max/min level size\n"); 288 | return false; 289 | } 290 | } 291 | else 292 | { 293 | return false; 294 | } 295 | } 296 | 297 | dynamicSprite_t* G_GetFromDynamicSpriteMap(int level, int y, int x) 298 | { 299 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 300 | { 301 | switch(level) 302 | { 303 | case 0: 304 | return (currentMap.dynamicSpritesLevel0[y][x]); 305 | 306 | case 1: 307 | return (currentMap.dynamicSpritesLevel1[y][x]); 308 | 309 | case 2: 310 | return (currentMap.dynamicSpritesLevel2[y][x]); 311 | 312 | default: 313 | //printf("WARNING! Level get was out of max/min level size\n"); 314 | return NULL; 315 | } 316 | } 317 | else 318 | { 319 | return NULL; 320 | } 321 | } 322 | 323 | 324 | void G_ClearFromDynamicSpriteMap(int level, int y, int x) 325 | { 326 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 327 | { 328 | switch(level) 329 | { 330 | case 0: 331 | (currentMap.dynamicSpritesLevel0[y][x]) = NULL; 332 | break; 333 | 334 | case 1: 335 | (currentMap.dynamicSpritesLevel1[y][x]) = NULL; 336 | break; 337 | 338 | case 2: 339 | (currentMap.dynamicSpritesLevel2[y][x]) = NULL; 340 | break; 341 | 342 | default: 343 | //printf("WARNING! Level get was out of max/min level size\n"); 344 | break; 345 | } 346 | } 347 | } 348 | 349 | bool G_CheckDeadDynamicSpriteMap(int level, int y, int x) 350 | { 351 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 352 | { 353 | switch(level) 354 | { 355 | case 0: 356 | return (currentMap.deadDynamicSpritesLevel0[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel0[y][x] : NULL; 357 | 358 | case 1: 359 | return (currentMap.deadDynamicSpritesLevel1[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel1[y][x] : NULL; 360 | 361 | case 2: 362 | return (currentMap.deadDynamicSpritesLevel2[y][x]) != NULL ? currentMap.deadDynamicSpritesLevel2[y][x] : NULL; 363 | 364 | default: 365 | //printf("WARNING! Level get was out of max/min level size\n"); 366 | return false; 367 | } 368 | } 369 | else 370 | { 371 | return false; 372 | } 373 | } 374 | 375 | dynamicSprite_t* G_GetFromDeadDynamicSpriteMap(int level, int y, int x) 376 | { 377 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 378 | { 379 | switch(level) 380 | { 381 | case 0: 382 | return (currentMap.deadDynamicSpritesLevel0[y][x]); 383 | 384 | case 1: 385 | return (currentMap.deadDynamicSpritesLevel1[y][x]); 386 | 387 | case 2: 388 | return (currentMap.deadDynamicSpritesLevel2[y][x]); 389 | 390 | default: 391 | //printf("WARNING! Level get was out of max/min level size\n"); 392 | return NULL; 393 | } 394 | } 395 | else 396 | { 397 | return NULL; 398 | } 399 | } 400 | 401 | 402 | void G_ClearFromDeadDynamicSpriteMap(int level, int y, int x) 403 | { 404 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) 405 | { 406 | switch(level) 407 | { 408 | case 0: 409 | (currentMap.deadDynamicSpritesLevel0[y][x]) = NULL; 410 | break; 411 | 412 | case 1: 413 | (currentMap.deadDynamicSpritesLevel1[y][x]) = NULL; 414 | break; 415 | 416 | case 2: 417 | (currentMap.deadDynamicSpritesLevel2[y][x]) = NULL; 418 | break; 419 | 420 | default: 421 | //printf("WARNING! Level get was out of max/min level size\n"); 422 | break; 423 | } 424 | } 425 | } 426 | 427 | void G_AddToDeadDynamicSpriteMap(dynamicSprite_t* cur, int level, int y, int x) 428 | { 429 | if(x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT && cur != NULL) 430 | { 431 | switch(level) 432 | { 433 | case 0: 434 | (currentMap.deadDynamicSpritesLevel0[y][x]) = cur; 435 | break; 436 | 437 | case 1: 438 | (currentMap.deadDynamicSpritesLevel1[y][x]) = cur; 439 | break; 440 | 441 | case 2: 442 | (currentMap.deadDynamicSpritesLevel2[y][x]) = cur; 443 | break; 444 | 445 | default: 446 | //printf("WARNING! Level get was out of max/min level size\n"); 447 | break; 448 | } 449 | } 450 | } -------------------------------------------------------------------------------- /src/Engine/G_Game.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "I_InputHandling.h" 4 | #include "G_Game.h" 5 | #include "P_Physics.h" 6 | #include "M_Map.h" 7 | #include "G_Pathfinding.h" 8 | #include "G_AI.h" 9 | #include "G_MainMenu.h" 10 | 11 | // Game Timer 12 | Timer* gameTimer; 13 | 14 | // Current Game Time 15 | double curTime = 0; 16 | 17 | // Game Time at last tick 18 | double oldTime = 0; 19 | 20 | // Doors 21 | int doorstateLevel0[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 22 | int doorstateLevel1[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 23 | int doorstateLevel2[MAP_HEIGHT][MAP_WIDTH]; // State of the door (open, closed, opening, closing) 24 | 25 | float doorpositionsLevel0[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 26 | float doorpositionsLevel1[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 27 | float doorpositionsLevel2[MAP_HEIGHT][MAP_WIDTH]; // Timer holding the position of the door 28 | 29 | // Projectiles travelling in the world 30 | projectileNode_t* projectilesHead = NULL; 31 | 32 | // Projectiles that hit something and are exploding 33 | projectileNode_t* explodingProjectilesHead = NULL; 34 | 35 | bool showFPS = false; 36 | float readOnlyFPS = 0; 37 | char fpsText[16]; 38 | int frameCountForFPSDisplay = 0; 39 | 40 | //------------------------------------- 41 | // Initialize game related stuff 42 | //------------------------------------- 43 | void G_InitGame(void) 44 | { 45 | // Initialize game 46 | if(gameTimer == NULL) 47 | gameTimer = U_TimerCreateNew(); 48 | 49 | gameTimer->Init(gameTimer); 50 | 51 | // Initialize Doors // 52 | memset(doorstateLevel0, 0, MAP_HEIGHT*MAP_WIDTH*sizeof(int)); 53 | memset(doorstateLevel1, 0, MAP_HEIGHT*MAP_WIDTH*sizeof(int)); 54 | memset(doorstateLevel2, 0, MAP_HEIGHT*MAP_WIDTH*sizeof(int)); 55 | 56 | // All doors start closed 57 | for(int y = 0; y < MAP_HEIGHT; y++) 58 | for(int x = 0; x < MAP_WIDTH; x++) 59 | { 60 | doorpositionsLevel0[y][x] = DOOR_FULLY_CLOSED; 61 | doorpositionsLevel1[y][x] = DOOR_FULLY_CLOSED; 62 | doorpositionsLevel2[y][x] = DOOR_FULLY_CLOSED; 63 | } 64 | 65 | // Empty projectile list 66 | if(projectilesHead != NULL) 67 | { 68 | projectileNode_t* current = projectilesHead; 69 | 70 | while(current != NULL) 71 | { 72 | projectileNode_t* tmp = current; 73 | current = current->next; 74 | free(tmp); 75 | } 76 | 77 | projectilesHead = NULL; 78 | } 79 | 80 | G_ChangeMap("lvl1"); 81 | 82 | gameTimer->Start(gameTimer); 83 | 84 | frameCountForFPSDisplay = 0; 85 | } 86 | 87 | //------------------------------------- 88 | // Game Tick 89 | //------------------------------------- 90 | void G_GameLoop(void) 91 | { 92 | Uint32 start_time, frame_time; 93 | float fps; 94 | 95 | start_time = SDL_GetTicks(); 96 | switch(application.gamestate) 97 | { 98 | case GSTATE_MENU: 99 | G_StateMenuLoop(); 100 | break; 101 | 102 | case GSTATE_GAME: 103 | G_StateGameLoop(); 104 | break; 105 | 106 | default: 107 | break; 108 | } 109 | 110 | frame_time = SDL_GetTicks()-start_time; 111 | fps = (frame_time > 0) ? 1000.0f / frame_time : 0.0f; 112 | 113 | // To display fps on screen 114 | frameCountForFPSDisplay++; 115 | if(frameCountForFPSDisplay > N_FRAMES_SKIP_FOR_DISPLAY) 116 | { 117 | readOnlyFPS = fps; 118 | frameCountForFPSDisplay = 0; 119 | } 120 | } 121 | 122 | 123 | void G_StateGameLoop(void) 124 | { 125 | curTime = gameTimer->GetTicks(gameTimer); 126 | 127 | P_PhysicsTick(); 128 | 129 | // Handle input 130 | I_HandleInputGame(); 131 | 132 | // If the game state was changed by the game input handler prevent this loop from going forward 133 | if(application.gamestate != GSTATE_GAME) 134 | return; 135 | 136 | // Do stuff 137 | G_PlayerTick(); 138 | G_UpdateDoors(); 139 | G_AIUpdate(); 140 | G_UpdateProjectiles(); 141 | P_PhysicsEndTick(); 142 | 143 | // Render 144 | // Creates the frame 145 | R_ComposeFrame(); 146 | 147 | // Displays it on the screen 148 | R_FinishUpdate(); 149 | 150 | oldTime = curTime; 151 | } 152 | 153 | void G_StateMenuLoop(void) 154 | { 155 | // Keep physic ticking even in menus, because game can be paused 156 | P_PhysicsTick(); 157 | 158 | // Handles input 159 | I_HandleInputMenu(); 160 | 161 | // If the game state was changed by the menu input handler prevent this loop from going forward 162 | // Not doing that could cause the program to freeze at the R_ComposeFrame() call, because in this loop we would try to render as if we were in the game state without having things initialized/updated 163 | if(application.gamestate != GSTATE_MENU) 164 | return; 165 | 166 | P_PhysicsEndTick(); 167 | 168 | // Clears current render 169 | SDL_FillRect(win_surface, NULL, r_blankColor); 170 | 171 | // Creates the frame 172 | R_ComposeFrame(); 173 | 174 | // Displays it on the screen 175 | R_FinishUpdate(); 176 | } 177 | 178 | //------------------------------------- 179 | // Update Doors by moving them in base of their timer 180 | //------------------------------------- 181 | void G_UpdateDoors(void) 182 | { 183 | for(int y = 0; y < MAP_HEIGHT; y++) 184 | for(int x = 0; x < MAP_WIDTH; x++) 185 | { 186 | // LEVEL 0 187 | // If the door is closed or open, continue 188 | if(doorstateLevel0[y][x] == DState_Closed || doorstateLevel0[y][x] == DState_Open) 189 | { 190 | // continue 191 | } 192 | else 193 | { 194 | // Open the door 195 | if(doorstateLevel0[y][x] == DState_Opening) 196 | { 197 | if(doorpositionsLevel0[y][x] > DOOR_FULLY_OPENED && 198 | doorpositionsLevel0[y][x] - DOOR_OPEN_SPEED * deltaTime > DOOR_FULLY_OPENED) // check if a step is too big 199 | doorpositionsLevel0[y][x] -= DOOR_OPEN_SPEED * deltaTime; 200 | else 201 | { 202 | // Door opened 203 | doorpositionsLevel0[y][x] = DOOR_FULLY_OPENED; 204 | doorstateLevel0[y][x] = DState_Open; 205 | 206 | // Update collision map 207 | currentMap.collisionMapLevel0[y][x] = 0; 208 | } 209 | } 210 | else if(doorstateLevel0[y][x] == DState_Closing) 211 | { 212 | if(doorpositionsLevel0[y][x] < DOOR_FULLY_CLOSED && 213 | doorpositionsLevel0[y][x] + DOOR_CLOSE_SPEED * deltaTime < DOOR_FULLY_CLOSED) // check if step is too big 214 | doorpositionsLevel0[y][x] += DOOR_CLOSE_SPEED * deltaTime; 215 | else 216 | { 217 | // Door closed 218 | doorpositionsLevel0[y][x] = DOOR_FULLY_CLOSED; 219 | doorstateLevel0[y][x] = DState_Closed; 220 | 221 | // Update collision map 222 | currentMap.collisionMapLevel0[y][x] = 1; 223 | } 224 | } 225 | } 226 | 227 | // LEVEL 1 228 | // If the door is closed or open, continue 229 | if(doorstateLevel1[y][x] == DState_Closed || doorstateLevel1[y][x] == DState_Open) 230 | { 231 | // continue 232 | } 233 | else 234 | { 235 | // Open the door 236 | if(doorstateLevel1[y][x] == DState_Opening) 237 | { 238 | if(doorpositionsLevel1[y][x] > DOOR_FULLY_OPENED && 239 | doorpositionsLevel1[y][x] - DOOR_OPEN_SPEED * deltaTime > DOOR_FULLY_OPENED) // check if a step is too big 240 | doorpositionsLevel1[y][x] -= DOOR_OPEN_SPEED * deltaTime; 241 | else 242 | { 243 | // Door opened 244 | doorpositionsLevel1[y][x] = DOOR_FULLY_OPENED; 245 | doorstateLevel1[y][x] = DState_Open; 246 | 247 | // Update collision map 248 | currentMap.collisionMapLevel1[y][x] = 0; 249 | } 250 | } 251 | else if(doorstateLevel1[y][x] == DState_Closing) 252 | { 253 | if(doorpositionsLevel1[y][x] < DOOR_FULLY_CLOSED && 254 | doorpositionsLevel1[y][x] + DOOR_CLOSE_SPEED * deltaTime < DOOR_FULLY_CLOSED) // check if step is too big 255 | doorpositionsLevel1[y][x] += DOOR_CLOSE_SPEED * deltaTime; 256 | else 257 | { 258 | // Door closed 259 | doorpositionsLevel1[y][x] = DOOR_FULLY_CLOSED; 260 | doorstateLevel1[y][x] = DState_Closed; 261 | 262 | // Update collision map 263 | currentMap.collisionMapLevel1[y][x] = 1; 264 | } 265 | } 266 | } 267 | 268 | // LEVEL 2 269 | // If the door is closed or open, continue 270 | if(doorstateLevel2[y][x] == DState_Closed || doorstateLevel2[y][x] == DState_Open) 271 | { 272 | // continue 273 | } 274 | else 275 | { 276 | // Open the door 277 | if(doorstateLevel2[y][x] == DState_Opening) 278 | { 279 | if(doorpositionsLevel2[y][x] > DOOR_FULLY_OPENED && 280 | doorpositionsLevel2[y][x] - DOOR_OPEN_SPEED * deltaTime > DOOR_FULLY_OPENED) // check if a step is too big 281 | doorpositionsLevel2[y][x] -= DOOR_OPEN_SPEED * deltaTime; 282 | else 283 | { 284 | // Door opened 285 | doorpositionsLevel2[y][x] = DOOR_FULLY_OPENED; 286 | doorstateLevel2[y][x] = DState_Open; 287 | 288 | // Update collision map 289 | currentMap.collisionMapLevel2[y][x] = 0; 290 | } 291 | } 292 | else if(doorstateLevel2[y][x] == DState_Closing) 293 | { 294 | if(doorpositionsLevel2[y][x] < DOOR_FULLY_CLOSED && 295 | doorpositionsLevel2[y][x] + DOOR_CLOSE_SPEED * deltaTime < DOOR_FULLY_CLOSED) // check if step is too big 296 | doorpositionsLevel2[y][x] += DOOR_CLOSE_SPEED * deltaTime; 297 | else 298 | { 299 | // Door closed 300 | doorpositionsLevel2[y][x] = DOOR_FULLY_CLOSED; 301 | doorstateLevel2[y][x] = DState_Closed; 302 | 303 | // Update collision map 304 | currentMap.collisionMapLevel2[y][x] = 1; 305 | } 306 | } 307 | } 308 | } 309 | } 310 | 311 | void G_ChangeMap(char* mapID) 312 | { 313 | // Check if game has ended 314 | if(strcmp("END_GAME", mapID) == 0) 315 | { 316 | player.hasBeenInitialized = false; 317 | G_SetMenu(&EndGameMenu); 318 | A_ChangeState(GSTATE_MENU); 319 | } 320 | else 321 | { 322 | M_LoadMapAsCurrent(mapID); 323 | G_InitPlayer(); 324 | } 325 | } 326 | 327 | void G_UpdateProjectiles(void) 328 | { 329 | projectileNode_t* cur = projectilesHead; 330 | int i = 0; 331 | while(cur != NULL) 332 | { 333 | // Update base info needed for rendering 334 | cur->this.base.centeredPos.x = cur->this.base.pos.x; 335 | cur->this.base.centeredPos.y = cur->this.base.pos.y; 336 | 337 | cur->this.base.pSpacePos.x = cur->this.base.centeredPos.x - player.centeredPos.x; 338 | cur->this.base.pSpacePos.y = cur->this.base.centeredPos.y - player.centeredPos.y; 339 | 340 | cur->this.base.gridPos.x = cur->this.base.centeredPos.x / TILE_SIZE; 341 | cur->this.base.gridPos.y = cur->this.base.centeredPos.y / TILE_SIZE; 342 | 343 | cur->this.base.dist = sqrt(cur->this.base.pSpacePos.x*cur->this.base.pSpacePos.x + cur->this.base.pSpacePos.y*cur->this.base.pSpacePos.y); 344 | 345 | // If the projectile hasnt hit anything, check for hit 346 | if(!cur->this.isBeingDestroyed) 347 | { 348 | // Update pos 349 | cur->this.base.pos.x += cos(cur->this.base.angle) * cur->this.speed * deltaTime; 350 | cur->this.base.pos.y += sin(cur->this.base.angle) * cur->this.speed * deltaTime; 351 | 352 | // Determine Projectile's level 353 | // half a tile is added to the base.z because the collision with the bottom part should always be a bit higer than normal (otherwise the projectile hits a wall with the transparent part of the sprite) 354 | cur->this.base.level = (int)floor((cur->this.base.z+(HALF_TILE_SIZE)) / TILE_SIZE); 355 | cur->this.base.level = SDL_clamp(cur->this.base.level, 0, MAX_N_LEVELS-1); 356 | 357 | if(cur->this.verticalMovementDelta > 0 || cur->this.verticalMovementDelta < 0) 358 | cur->this.base.z += cur->this.verticalMovementDelta * deltaTime; 359 | 360 | // Check if projectile is not out of map 361 | bool insideMap = cur->this.base.gridPos.x >= 0 && cur->this.base.gridPos.y >= 0 && cur->this.base.gridPos.x < MAP_WIDTH && cur->this.base.gridPos.y < MAP_HEIGHT && cur->this.base.z > -TILE_SIZE && cur->this.base.z < TILE_SIZE*(MAX_N_LEVELS); 362 | 363 | // Destroy condition 364 | if(G_CheckCollisionMap(cur->this.base.level, cur->this.base.gridPos.y, cur->this.base.gridPos.x) == 1 || !insideMap) 365 | { 366 | cur->this.isBeingDestroyed = true; 367 | G_AIPlayAnimationOnce(&cur->this, ANIM_DIE); 368 | 369 | i++; 370 | cur = cur->next; 371 | 372 | continue; 373 | } 374 | 375 | // AI hit 376 | dynamicSprite_t* sprite = NULL; 377 | if((sprite = G_GetFromDynamicSpriteMap(cur->this.base.level, cur->this.base.gridPos.y, cur->this.base.gridPos.x)) != NULL && 378 | cur->this.aiOwner != sprite && sprite->canBeHit) 379 | { 380 | float damage = 0.0f; 381 | 382 | // Damage sprite 383 | switch(cur->this.base.spriteID) 384 | { 385 | case S_Fireball1: 386 | damage = 55.65f; 387 | break; 388 | 389 | case S_IceDart1: 390 | damage = 33.0f; 391 | break; 392 | 393 | default: 394 | damage = 0.0f; 395 | break; 396 | } 397 | 398 | if(cur->this.isOfPlayer) 399 | { 400 | player.crosshairHit = true; 401 | player.crosshairTimer->Start(player.crosshairTimer); 402 | } 403 | 404 | G_AITakeDamage(sprite, damage); 405 | 406 | cur->this.isBeingDestroyed = true; 407 | G_AIPlayAnimationOnce(&cur->this, ANIM_DIE); 408 | 409 | i++; 410 | cur = cur->next; 411 | continue; 412 | } 413 | 414 | // Player hit 415 | // Add distance checking for being more precise 416 | float playerDist = sqrt(cur->this.base.pSpacePos.x*cur->this.base.pSpacePos.x + cur->this.base.pSpacePos.y*cur->this.base.pSpacePos.y); 417 | if(!cur->this.isOfPlayer && cur->this.base.level == player.level && cur->this.base.gridPos.x == player.gridPosition.x && cur->this.base.gridPos.y == player.gridPosition.y && playerDist < TILE_SIZE-12) 418 | { 419 | printf("%f\n", playerDist); 420 | float damage = 0.0f; 421 | 422 | // Damage sprite 423 | switch(cur->this.base.spriteID) 424 | { 425 | case S_Fireball1: 426 | damage = 55.65f; 427 | break; 428 | 429 | case S_IceDart1: 430 | damage = 33.0f; 431 | break; 432 | 433 | default: 434 | damage = 0.0f; 435 | break; 436 | } 437 | 438 | G_PlayerTakeDamage(damage); 439 | 440 | cur->this.isBeingDestroyed = true; 441 | G_AIPlayAnimationOnce(&cur->this, ANIM_DIE); 442 | 443 | i++; 444 | cur = cur->next; 445 | 446 | continue; 447 | } 448 | } 449 | // The projectile has hit, wait for the death animation to play 450 | else 451 | { 452 | if(cur->this.animFrame >= tomentdatapack.sprites[cur->this.base.spriteID]->animations->animDieSheetLength-1) 453 | { 454 | // Destroy this 455 | if(projectilesHead == cur) 456 | projectilesHead = cur->next; 457 | 458 | if(cur->next != NULL) 459 | cur->next->previous = cur->previous; 460 | 461 | if(cur->previous != NULL) 462 | cur->previous->next = cur->next; 463 | 464 | projectileNode_t* dead = cur; 465 | cur = cur->next; 466 | free(dead->this.animTimer); 467 | free(dead); 468 | continue; 469 | } 470 | } 471 | 472 | // Select Animation & Play it 473 | switch(cur->this.state) 474 | { 475 | case DS_STATE_IDLE: 476 | cur->this.curAnim = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animIdle; 477 | cur->this.curAnimLength = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animIdleSheetLength; 478 | break; 479 | 480 | case DS_STATE_DEAD: 481 | cur->this.curAnim = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animDie; 482 | cur->this.curAnimLength = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animDieSheetLength; 483 | break; 484 | 485 | case DS_STATE_ATTACKING: 486 | cur->this.curAnim = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animAttack; 487 | cur->this.curAnimLength = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animAttackSheetLength; 488 | break; 489 | 490 | default: 491 | cur->this.curAnim = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animIdle; 492 | cur->this.curAnimLength = tomentdatapack.sprites[cur->this.base.spriteID]->animations->animIdleSheetLength; 493 | break; 494 | } 495 | 496 | if(cur->this.animPlay) 497 | { 498 | if(cur->this.animPlayOnce) 499 | { 500 | if(cur->this.curAnimLength > 0) 501 | cur->this.animFrame = ((int)floor(cur->this.animTimer->GetTicks(cur->this.animTimer) / cur->this.animSpeed) % cur->this.curAnimLength); 502 | 503 | // Prevent loop 504 | if(cur->this.animFrame >= cur->this.curAnimLength-1) 505 | { 506 | cur->this.animPlay = false; 507 | 508 | // Go back to idle if it was attacking 509 | if(cur->this.state == DS_STATE_ATTACKING) 510 | { 511 | G_AIPlayAnimationLoop(&cur->this, ANIM_IDLE); 512 | } 513 | } 514 | } 515 | else 516 | { 517 | // Allow loop 518 | if(cur->this.curAnimLength > 0) 519 | cur->this.animFrame = ((int)floor(cur->this.animTimer->GetTicks(cur->this.animTimer) / cur->this.animSpeed) % cur->this.curAnimLength); 520 | } 521 | } 522 | 523 | i++; 524 | cur = cur->next; 525 | } 526 | } 527 | 528 | 529 | void G_SpawnProjectile(int id, float angle, int level, float posx, float posy, float posz, float verticalAngle, bool isOfPlayer, dynamicSprite_t* aiOwner) 530 | { 531 | // Allocate a node 532 | projectileNode_t* newNode = (projectileNode_t*)malloc(sizeof(projectileNode_t)); 533 | 534 | // Set initial data like pos, dir and speed 535 | newNode->this.type = DS_TYPE_PROJECTILE; 536 | newNode->this.curAnim = NULL; 537 | newNode->this.curAnimLength = 0; 538 | newNode->this.animPlay = true; 539 | newNode->this.state = ANIM_IDLE; 540 | newNode->this.animTimer = U_TimerCreateNew(); 541 | newNode->this.isBeingDestroyed = false; 542 | 543 | newNode->this.base.spriteID = id; 544 | newNode->this.base.sheetLength = tomentdatapack.spritesSheetsLenghtTable[id]; 545 | newNode->this.speed = 500.0f; 546 | 547 | newNode->this.base.active = true; 548 | newNode->this.base.angle = angle; 549 | newNode->this.base.level = level; 550 | 551 | newNode->this.base.pos.x = posx; 552 | newNode->this.base.pos.y = posy; 553 | newNode->this.base.z = posz; 554 | 555 | // Vertical angle is how much the entity that launches the projectile is looking up/down 556 | newNode->this.verticalMovementDelta = verticalAngle; 557 | 558 | newNode->this.base.gridPos.x = posx / TILE_SIZE; 559 | newNode->this.base.gridPos.y = posy / TILE_SIZE; 560 | 561 | newNode->this.base.centeredPos.x = newNode->this.base.pos.x + (HALF_TILE_SIZE); 562 | newNode->this.base.centeredPos.y = newNode->this.base.pos.y + (HALF_TILE_SIZE); 563 | 564 | newNode->this.base.pSpacePos.x = newNode->this.base.centeredPos.x - player.centeredPos.x; 565 | newNode->this.base.pSpacePos.y = newNode->this.base.centeredPos.y - player.centeredPos.y; 566 | 567 | newNode->this.isOfPlayer = isOfPlayer; 568 | newNode->this.aiOwner = aiOwner; 569 | 570 | newNode->next = NULL; 571 | if(projectilesHead == NULL) 572 | { 573 | projectilesHead = newNode; 574 | projectilesHead->next = NULL; 575 | projectilesHead->previous = NULL; 576 | } 577 | else 578 | { 579 | projectileNode_t* current = projectilesHead; 580 | 581 | while(current->next != NULL) 582 | current = current->next; 583 | 584 | // Now we can add 585 | current->next = newNode; 586 | current->next->next = NULL; 587 | newNode->previous = current; 588 | } 589 | 590 | // Play idle anim 591 | G_AIPlayAnimationLoop(&newNode->this, ANIM_IDLE); 592 | } -------------------------------------------------------------------------------- /src/Engine/M_Map.c: -------------------------------------------------------------------------------- 1 | #include "M_Map.h" 2 | #include "G_Game.h" 3 | #include "G_AI.h" 4 | 5 | map_t currentMap; 6 | 7 | static void I_LoadWallMapFromFile(wallObject_t map[MAP_HEIGHT][MAP_WIDTH], FILE* fp); 8 | static void I_LoadMapFromFile(int map[MAP_HEIGHT][MAP_WIDTH], FILE* fp); 9 | static void I_LoadIntFromFile(FILE* fp, int* toLoad); 10 | static void I_LoadBoolFromFile(FILE* fp, bool* toLoad); 11 | static void I_LoadFloatFromFile(FILE* fp, float* toLoad); 12 | static void I_ReadStringFromFile(FILE* fp, char toWrite[MAX_STRLEN]); 13 | 14 | // ------------------------------- 15 | // Loads the map from the file named mapID 16 | // ------------------------------- 17 | void M_LoadMapAsCurrent(char* mapID) 18 | { 19 | FILE* fp; 20 | char filepath[MAX_FILEPATH_L] = "Data/maps/"; 21 | 22 | // Find path 23 | strcat(filepath, mapID); 24 | strcat(filepath, ".tmap"); 25 | 26 | printf("Loading map... %s\n", filepath); 27 | 28 | // Open file 29 | fp = fopen(filepath, "r"); 30 | 31 | if(fp == NULL) 32 | { 33 | printf("Map %s is not present in the Data folder. Aborting. \n"); 34 | return; 35 | } 36 | 37 | // Load Map 38 | char curLine[MAX_STRL_R]; // Current line we're reading 39 | char* str; // Used to strchr 40 | int indx; // Index of the = 41 | int i; // Index for writing in new string 42 | 43 | // -------------------- 44 | // Read ID 45 | // -------------------- 46 | fgets(curLine, MAX_STRL_R, fp); 47 | 48 | // Find index for reading 49 | str = strchr(curLine, '='); 50 | indx = (int)(str - curLine) + 1; 51 | 52 | // Init index for writing 53 | i = 0; 54 | 55 | // Write 56 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 57 | { 58 | currentMap.id[i] = curLine[indx]; 59 | i++; 60 | indx++; 61 | } 62 | currentMap.id[i] = '\0'; 63 | 64 | // -------------------- 65 | // Read MapName 66 | // -------------------- 67 | fgets(curLine, MAX_STRL_R, fp); 68 | 69 | // Find index for reading 70 | str = strchr(curLine, '='); 71 | indx = (int)(str - curLine) + 1; 72 | 73 | // Init index for writing 74 | i = 0; 75 | 76 | // Write 77 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 78 | { 79 | currentMap.name[i] = curLine[indx]; 80 | i++; 81 | indx++; 82 | } 83 | currentMap.name[i] = '\0'; 84 | 85 | 86 | I_LoadIntFromFile(fp, ¤tMap.playerStartingLevel); 87 | 88 | // Load Starting player pos 89 | I_LoadIntFromFile(fp, ¤tMap.playerStartingGridX); 90 | I_LoadIntFromFile(fp, ¤tMap.playerStartingGridY); 91 | I_LoadFloatFromFile(fp, ¤tMap.playerStartingRot); 92 | 93 | // Load Wall Map 94 | I_LoadWallMapFromFile(currentMap.level0, fp); 95 | I_LoadWallMapFromFile(currentMap.level1, fp); 96 | I_LoadWallMapFromFile(currentMap.level2, fp); 97 | 98 | // Load Floor Map 99 | I_LoadMapFromFile(currentMap.floorMap, fp); 100 | 101 | // Load Ceiling Map 102 | I_LoadMapFromFile(currentMap.ceilingMap, fp); 103 | 104 | // Load Sprites Map 105 | I_LoadMapFromFile(currentMap.spritesMapLevel0, fp); 106 | I_LoadMapFromFile(currentMap.spritesMapLevel1, fp); 107 | I_LoadMapFromFile(currentMap.spritesMapLevel2, fp); 108 | 109 | // Read Wall Lighting 110 | I_LoadFloatFromFile(fp, ¤tMap.wallLight); 111 | 112 | // Read Ceiling Lighting 113 | I_LoadFloatFromFile(fp, ¤tMap.floorLight); 114 | 115 | // Read SkyID 116 | I_LoadIntFromFile(fp, ¤tMap.skyID); 117 | 118 | // Read hasAbsCeiling 119 | I_LoadBoolFromFile(fp, ¤tMap.hasAbsCeiling); 120 | 121 | // Read absCeilingLevel 122 | I_LoadIntFromFile(fp, ¤tMap.absCeilingLevel); 123 | 124 | // Read hasAbsCeiling 125 | I_LoadBoolFromFile(fp, ¤tMap.hasFog); 126 | 127 | I_LoadIntFromFile(fp, ¤tMap.fogColorR); 128 | I_LoadIntFromFile(fp, ¤tMap.fogColorG); 129 | I_LoadIntFromFile(fp, ¤tMap.fogColorB); 130 | 131 | // Load the fog color as an SDL_Color 132 | currentMap.fogColor.r = currentMap.fogColorR; 133 | currentMap.fogColor.g = currentMap.fogColorG; 134 | currentMap.fogColor.b = currentMap.fogColorB; 135 | 136 | // Read MinWallFog 137 | I_LoadFloatFromFile(fp, ¤tMap.wallFogMinDist); 138 | 139 | // Read MaxWallFog 140 | I_LoadFloatFromFile(fp, ¤tMap.wallFogMaxDist); 141 | 142 | // Read FloorFogMin 143 | I_LoadFloatFromFile(fp, ¤tMap.floorFogMinDist); 144 | 145 | // Read FloorFogMin 146 | I_LoadFloatFromFile(fp, ¤tMap.floorFogMaxDist); 147 | 148 | fclose(fp); 149 | 150 | // Load the TMap 151 | M_LoadObjectTMap(); 152 | 153 | // Load the Collision Map 154 | M_LoadCollisionMaps(); 155 | 156 | printf("Map loaded successfully! ID: %s\n", currentMap.id); 157 | } 158 | 159 | // ------------------------------- 160 | // Loads the object map 161 | // ------------------------------- 162 | void M_LoadObjectTMap(void) 163 | { 164 | for(int i = 0; i < allDynamicSpritesLength; i++) 165 | if(allDynamicSprites[i]) 166 | free(allDynamicSprites[i]); 167 | 168 | // Clear maps 169 | for(int y = 0; y < MAP_HEIGHT; y++) 170 | for(int x = 0; x < MAP_WIDTH; x++) 171 | { 172 | currentMap.dynamicSpritesLevel0[y][x] = NULL; 173 | currentMap.dynamicSpritesLevel1[y][x] = NULL; 174 | currentMap.dynamicSpritesLevel2[y][x] = NULL; 175 | currentMap.deadDynamicSpritesLevel0[y][x] = NULL; 176 | currentMap.deadDynamicSpritesLevel1[y][x] = NULL; 177 | currentMap.deadDynamicSpritesLevel2[y][x] = NULL; 178 | } 179 | 180 | allDynamicSpritesLength = 0; 181 | 182 | for(int y = 0; y < MAP_HEIGHT; y++) 183 | for(int x = 0; x < MAP_WIDTH; x++) 184 | { 185 | // LEVEL 0 186 | // Initialize 187 | currentMap.objectTMapLevel0[y][x] = ObjT_Empty; 188 | 189 | // Check if it's a wall 190 | int wallID = currentMap.level0[y][x].assetID; 191 | if(wallID > 0) 192 | { 193 | currentMap.objectTMapLevel0[y][x] = ObjT_Wall; 194 | 195 | // Check if it is a door 196 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 2) == 1) 197 | { 198 | currentMap.objectTMapLevel0[y][x] = ObjT_Door; 199 | } 200 | 201 | if (U_GetBit(&tomentdatapack.walls[wallID]->flags, 3) == 1) 202 | { 203 | currentMap.objectTMapLevel0[y][x] = ObjT_Trigger; 204 | } 205 | } 206 | 207 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 208 | int spriteID = currentMap.spritesMapLevel0[y][x]; 209 | if(spriteID > 0) 210 | { 211 | currentMap.objectTMapLevel0[y][x] = ObjT_Sprite; 212 | 213 | // Check if this is a dynamic sprite 214 | if(U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 1) 215 | { 216 | // Construct the dynamic sprite in base of its id 217 | currentMap.dynamicSpritesLevel0[y][x] = (dynamicSprite_t*)malloc(sizeof(dynamicSprite_t)); 218 | dynamicSprite_t* cur = currentMap.dynamicSpritesLevel0[y][x]; 219 | 220 | G_AIInitialize(cur, 0, spriteID, x, y); 221 | 222 | // The rest is calculated at runtime 223 | } 224 | } 225 | 226 | 227 | // LEVEL 1 228 | // Initialize 229 | currentMap.objectTMapLevel1[y][x] = ObjT_Empty; 230 | 231 | // Check if it's a wall 232 | wallID = currentMap.level1[y][x].assetID; 233 | if(wallID > 0) 234 | { 235 | currentMap.objectTMapLevel1[y][x] = ObjT_Wall; 236 | 237 | // Check if it is a door 238 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 2) == 1) 239 | { 240 | currentMap.objectTMapLevel1[y][x] = ObjT_Door; 241 | } 242 | if (U_GetBit(&tomentdatapack.walls[wallID]->flags, 3) == 1) 243 | { 244 | currentMap.objectTMapLevel1[y][x] = ObjT_Trigger; 245 | } 246 | } 247 | 248 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 249 | spriteID = currentMap.spritesMapLevel1[y][x]; 250 | if(spriteID > 0) 251 | { 252 | currentMap.objectTMapLevel1[y][x] = ObjT_Sprite; 253 | 254 | // Check if this is a dynamic sprite 255 | if(U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 1) 256 | { 257 | currentMap.dynamicSpritesLevel1[y][x] = (dynamicSprite_t*)malloc(sizeof(dynamicSprite_t)); 258 | dynamicSprite_t* cur = currentMap.dynamicSpritesLevel1[y][x]; 259 | 260 | G_AIInitialize(cur, 1, spriteID, x, y); 261 | 262 | // The rest is calculated at runtime 263 | } 264 | } 265 | 266 | 267 | // LEVEL 2 268 | // Initialize 269 | currentMap.objectTMapLevel2[y][x] = ObjT_Empty; 270 | 271 | // Check if it's a wall 272 | wallID = currentMap.level2[y][x].assetID; 273 | if(wallID > 0) 274 | { 275 | currentMap.objectTMapLevel2[y][x] = ObjT_Wall; 276 | 277 | // Check if it is a door 278 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 2) == 1) 279 | { 280 | currentMap.objectTMapLevel2[y][x] = ObjT_Door; 281 | } 282 | if (U_GetBit(&tomentdatapack.walls[wallID]->flags, 3) == 1) 283 | { 284 | currentMap.objectTMapLevel2[y][x] = ObjT_Trigger; 285 | } 286 | } 287 | 288 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 289 | spriteID = currentMap.spritesMapLevel2[y][x]; 290 | if(spriteID > 0) 291 | { 292 | currentMap.objectTMapLevel2[y][x] = ObjT_Sprite; 293 | 294 | // Check if this is a dynamic sprite 295 | if(U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 1) 296 | { 297 | currentMap.dynamicSpritesLevel2[y][x] = (dynamicSprite_t*)malloc(sizeof(dynamicSprite_t)); 298 | dynamicSprite_t* cur = currentMap.dynamicSpritesLevel2[y][x]; 299 | 300 | G_AIInitialize(cur, 2, spriteID, x, y); 301 | 302 | // The rest is calculated at runtime 303 | } 304 | } 305 | } 306 | } 307 | 308 | // ------------------------------- 309 | // Loads the collision map 310 | // 311 | // Collision: 1 = For everything 312 | // 2 = For player but not projectiles 313 | // ------------------------------- 314 | void M_LoadCollisionMaps(void) 315 | { 316 | // Load Level 0 317 | for(int y = 0; y < MAP_HEIGHT; y++) 318 | for(int x = 0; x < MAP_WIDTH; x++) 319 | { 320 | // LEVEL 0 321 | // Initialize 322 | currentMap.collisionMapLevel0[y][x] = 0; 323 | 324 | // Check if it's a wall 325 | int wallID = currentMap.level0[y][x].assetID; 326 | if(wallID > 0) 327 | { 328 | // If this is an invisible wall, projectile should be able to go throught it 329 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 4)) 330 | { 331 | currentMap.collisionMapLevel0[y][x] = 2; 332 | } 333 | else 334 | currentMap.collisionMapLevel0[y][x] = 1; 335 | } 336 | 337 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 338 | int spriteID = currentMap.spritesMapLevel0[y][x]; 339 | if(spriteID > 0 && U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 0) == 1 && 340 | U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 0) 341 | currentMap.collisionMapLevel0[y][x] = 1; 342 | 343 | 344 | // LEVEL 1 345 | // Initialize 346 | currentMap.collisionMapLevel1[y][x] = 0; 347 | 348 | // Check if it's a wall 349 | wallID = currentMap.level1[y][x].assetID; 350 | if(wallID > 0) 351 | { 352 | // If this is an invisible wall, projectile should be able to go throught it 353 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 4)) 354 | { 355 | currentMap.collisionMapLevel1[y][x] = 2; 356 | } 357 | else 358 | currentMap.collisionMapLevel1[y][x] = 1; 359 | } 360 | 361 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 362 | spriteID = currentMap.spritesMapLevel1[y][x]; 363 | if(spriteID > 0 && U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 0) == 1 && 364 | U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 0) 365 | currentMap.collisionMapLevel1[y][x] = 1; 366 | 367 | // LEVEL 2 368 | // Initialize 369 | currentMap.collisionMapLevel2[y][x] = 0; 370 | 371 | // Check if it's a wall 372 | wallID = currentMap.level2[y][x].assetID; 373 | if(wallID > 0) 374 | { 375 | // If this is an invisible wall, projectile should be able to go throught it 376 | if(U_GetBit(&tomentdatapack.walls[wallID]->flags, 4)) 377 | { 378 | currentMap.collisionMapLevel2[y][x] = 2; 379 | } 380 | else 381 | currentMap.collisionMapLevel2[y][x] = 1; 382 | } 383 | 384 | // Check if it's a sprite (overrides doors, but spirtes should never be placed on top of walls) 385 | spriteID = currentMap.spritesMapLevel2[y][x]; 386 | if(spriteID > 0 && U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 0) == 1 && 387 | U_GetBit(&tomentdatapack.sprites[spriteID]->flags, 2) == 0) 388 | currentMap.collisionMapLevel2[y][x] = 1; 389 | } 390 | } 391 | 392 | 393 | static void I_LoadWallMapFromFile(wallObject_t map[MAP_HEIGHT][MAP_WIDTH], FILE* fp) 394 | { 395 | // Load Map 396 | char curLine[MAX_STRL_R]; // Current line we're reading 397 | char* str; // Used to strchr 398 | int indx; // Index of the = 399 | int i; // Index for writing in new string 400 | 401 | fgets(curLine, MAX_STRL_R, fp); // Layout = 402 | fgets(curLine, MAX_STRL_R, fp); // [ start of map 403 | fgets(curLine, MAX_STRL_R, fp); // First Row 404 | 405 | // Find the first row 406 | str = strchr(curLine, '{'); 407 | indx = (int)(str - curLine) + 1; // '(' 408 | 409 | bool mapDone = false; 410 | int column = 0; 411 | int row = 0; 412 | 413 | while(!mapDone) 414 | { 415 | // Read columns 416 | while(curLine[indx] != '}') 417 | { 418 | indx++; 419 | unsigned sum = 0; 420 | 421 | // Read Asset ID 422 | do 423 | { 424 | sum *= 10; 425 | sum += curLine[indx] - '0'; 426 | map[column][row].assetID = sum; // Set int value 427 | indx++; 428 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 429 | indx++; 430 | 431 | // Read Top Texture 432 | sum = 0; 433 | do 434 | { 435 | sum *= 10; 436 | sum += curLine[indx] - '0'; 437 | map[column][row].texturesArray[TEXTURE_ARRAY_TOP] = sum; // Set int value 438 | indx++; 439 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 440 | indx++; 441 | 442 | // Read Bottom Texture 443 | sum = 0; 444 | do 445 | { 446 | sum *= 10; 447 | sum += curLine[indx] - '0'; 448 | map[column][row].texturesArray[TEXTURE_ARRAY_BOTTOM] = sum; // Set int value 449 | indx++; 450 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 451 | indx++; 452 | 453 | // Read Left Texture 454 | sum = 0; 455 | do 456 | { 457 | sum *= 10; 458 | sum += curLine[indx] - '0'; 459 | map[column][row].texturesArray[TEXTURE_ARRAY_LEFT] = sum; // Set int value 460 | indx++; 461 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 462 | indx++; 463 | 464 | // Read Right Texture 465 | sum = 0; 466 | do 467 | { 468 | sum *= 10; 469 | sum += curLine[indx] - '0'; 470 | map[column][row].texturesArray[TEXTURE_ARRAY_RIGHT] = sum; // Set int value 471 | indx++; 472 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 473 | indx++; 474 | 475 | // Read Forward Texture 476 | sum = 0; 477 | do 478 | { 479 | sum *= 10; 480 | sum += curLine[indx] - '0'; 481 | map[column][row].texturesArray[TEXTURE_ARRAY_UP] = sum; // Set int value 482 | indx++; 483 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 484 | indx++; 485 | 486 | // Read Back Texture 487 | sum = 0; 488 | do 489 | { 490 | sum *= 10; 491 | sum += curLine[indx] - '0'; 492 | map[column][row].texturesArray[TEXTURE_ARRAY_DOWN] = sum; // Set int value 493 | indx++; 494 | } while(curLine[indx] != ',' && curLine[indx] != ')'); 495 | indx++; 496 | 497 | // Read Data 498 | int dataIndex = 0; 499 | indx++; 500 | while(curLine[indx] != '"') 501 | { 502 | map[column][row].data[dataIndex] = curLine[indx]; 503 | dataIndex++; 504 | indx++; 505 | } 506 | map[column][row].data[dataIndex] = '\0'; 507 | indx++; 508 | indx++; 509 | 510 | // If next is comma, continue and get next number 511 | if(curLine[indx] == ',') 512 | { 513 | indx++; 514 | row++; 515 | } 516 | //printf("%c!\n", curLine[indx]); 517 | } 518 | 519 | // Row end, check if there's a next row or if it is finished 520 | if(curLine[indx + 1] == ',') 521 | { 522 | // There is a next column 523 | column++; 524 | indx = 1; // Move at the start of the next column 525 | row = 0; 526 | fgets(curLine, MAX_STRL_R, fp); // Get next line 527 | continue; 528 | } 529 | else if(curLine[indx + 1] == ']') 530 | { 531 | // Map has finished loading 532 | mapDone = true; 533 | break; 534 | } 535 | } 536 | 537 | printf("%s\n", map[0][0].data); 538 | } 539 | 540 | static void I_LoadMapFromFile(int map[MAP_HEIGHT][MAP_WIDTH], FILE* fp) 541 | { 542 | // Load Map 543 | char curLine[MAX_STRL_R]; // Current line we're reading 544 | char* str; // Used to strchr 545 | int indx; // Index of the = 546 | int i; // Index for writing in new string 547 | 548 | fgets(curLine, MAX_STRLEN, fp); // Layout = 549 | fgets(curLine, MAX_STRLEN, fp); // [ start of map 550 | fgets(curLine, MAX_STRLEN, fp); // First Row 551 | 552 | // Find the first row 553 | str = strchr(curLine, '{'); 554 | indx = (int)(str - curLine) + 1; 555 | 556 | bool mapDone = false; 557 | int column = 0; 558 | int row = 0; 559 | 560 | while(!mapDone) 561 | { 562 | // Read columns 563 | while(curLine[indx] != '}') 564 | { 565 | unsigned sum = 0; 566 | 567 | do 568 | { 569 | sum *= 10; 570 | sum += curLine[indx] - '0'; 571 | map[column][row] = sum; // Set int value 572 | indx++; 573 | } while(curLine[indx] != ',' && curLine[indx] != '}'); 574 | 575 | 576 | // If next is comma, continue and get next number 577 | if(curLine[indx] == ',') 578 | { 579 | indx++; 580 | row++; 581 | } 582 | 583 | //printf("%c!\n", curLine[indx]); 584 | } 585 | 586 | // Row end, check if there's a next row or if it is finished 587 | if(curLine[indx + 1] == ',') 588 | { 589 | // There is a next column 590 | column++; 591 | indx = 1; // Move at the start of the next column 592 | row = 0; 593 | fgets(curLine, MAX_STRLEN, fp); // Get next line 594 | continue; 595 | } 596 | else if(curLine[indx + 1] == ']') 597 | { 598 | // Map has finished loading 599 | mapDone = true; 600 | break; 601 | } 602 | } 603 | } 604 | 605 | static void I_LoadIntFromFile(FILE* fp, int* toLoad) 606 | { 607 | // Load Map 608 | char curLine[MAX_STRL_R]; // Current line we're reading 609 | char* str; // Used to strchr 610 | int indx; // Index of the = 611 | int i; // Index for writing in new string 612 | 613 | // -------------------- 614 | // Read SkyID 615 | // -------------------- 616 | fgets(curLine, MAX_STRLEN, fp); // Layout = 617 | 618 | // Find index for reading 619 | str = strchr(curLine, '='); 620 | indx = (int)(str - curLine) + 1; 621 | 622 | // Init index for writing 623 | i = 0; 624 | 625 | char tempStr[256]; 626 | // Write 627 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 628 | { 629 | tempStr[i] = curLine[indx]; 630 | i++; 631 | indx++; 632 | } 633 | tempStr[i] = '\0'; 634 | 635 | // Convert to float 636 | *toLoad = atoi(tempStr); 637 | } 638 | 639 | static void I_LoadBoolFromFile(FILE* fp, bool* toLoad) 640 | { 641 | // Load Map 642 | char curLine[MAX_STRL_R]; // Current line we're reading 643 | char* str; // Used to strchr 644 | int indx; // Index of the = 645 | int i; // Index for writing in new string 646 | 647 | // -------------------- 648 | // Read SkyID 649 | // -------------------- 650 | fgets(curLine, MAX_STRLEN, fp); // Layout = 651 | 652 | // Find index for reading 653 | str = strchr(curLine, '='); 654 | indx = (int)(str - curLine) + 1; 655 | 656 | // Init index for writing 657 | i = 0; 658 | 659 | char tempStr[256]; 660 | // Write 661 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 662 | { 663 | tempStr[i] = curLine[indx]; 664 | i++; 665 | indx++; 666 | } 667 | tempStr[i] = '\0'; 668 | 669 | // Convert to float 670 | *toLoad = atoi(tempStr); 671 | } 672 | 673 | static void I_LoadFloatFromFile(FILE* fp, float* toLoad) 674 | { 675 | // Load Map 676 | char curLine[MAX_STRL_R]; // Current line we're reading 677 | char* str; // Used to strchr 678 | int indx; // Index of the = 679 | int i; // Index for writing in new string 680 | 681 | // -------------------- 682 | // Read SkyID 683 | // -------------------- 684 | fgets(curLine, MAX_STRLEN, fp); // Layout = 685 | 686 | // Find index for reading 687 | str = strchr(curLine, '='); 688 | indx = (int)(str - curLine) + 1; 689 | 690 | // Init index for writing 691 | i = 0; 692 | 693 | char tempStr[256]; 694 | // Write 695 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 696 | { 697 | tempStr[i] = curLine[indx]; 698 | i++; 699 | indx++; 700 | } 701 | tempStr[i] = '\0'; 702 | 703 | // Convert to float 704 | *toLoad = atof(tempStr); 705 | } 706 | 707 | static void I_ReadStringFromFile(FILE* fp, char toWrite[MAX_STRLEN]) 708 | { 709 | char curLine[MAX_STRL_R]; // Current line we're reading 710 | char* str; // Used to strchr 711 | int indx; // Index of the = 712 | int i; // Index for writing in new string 713 | 714 | fgets(curLine, MAX_STRL_R, fp); 715 | 716 | printf("%s\n", curLine); 717 | 718 | // Find index for reading 719 | str = strchr(curLine, '='); 720 | indx = (int)(str - curLine) + 1; 721 | 722 | // Init index for writing 723 | i = 0; 724 | 725 | // Write 726 | while(curLine[indx] != ';' && curLine[indx] != '\n' && curLine[indx] != EOF) 727 | { 728 | toWrite[i] = curLine[indx]; 729 | i++; 730 | indx++; 731 | } 732 | toWrite[i] = '\0'; 733 | } --------------------------------------------------------------------------------