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